Wie genau funktionieren Django-Inhaltstypen?

148

Es fällt mir wirklich schwer, das Konzept der Inhaltstypen von Django zu verstehen. Es fühlt sich sehr hackig an und letztendlich dagegen, wie Python dazu neigt, Dinge zu tun. Davon abgesehen muss ich innerhalb der Grenzen des Frameworks arbeiten, wenn ich Django verwenden möchte.

Ich frage mich, ob jemand ein praktisches Beispiel dafür geben kann, wie ein Inhaltstyp funktioniert und wie Sie ihn implementieren würden. Fast alle Tutorials (meistens in Blogs), die ich durchgesehen habe, machen keine großartige Arbeit, die das Konzept wirklich abdeckt. Sie scheinen dort weiterzumachen, wo die Django-Dokumentation aufgehört hat (was nirgendwo zu sein scheint).

Chris Shelton
quelle
5
Ich glaube (jemand korrigiert mich, wenn ich falsch liege), dass Inhaltstypen so etwas wie ein Polymorphismus sind. Sie werden zu einem Werkzeug in Ihren Händen, sobald Ihr Projekt Modelle hat, die viele verschiedene Formen haben können. Das Tag-Beispiel in der Dokumentation ist ziemlich einfach. Sie möchten Elemente mit Tags versehen können, möchten jedoch nicht genau festlegen, um welche Art von Elementen es sich handelt. Schließlich kann ein Tag Beiträge, Seiten, Benutzer usw. unterstützen. Produkte. Mithilfe von Inhaltstypen können Sie Beziehungen zu verschiedenen Implementierungen erstellen, ohne das zugehörige Modell genau kennen zu müssen.
Petkostas
1
Okay, ich bin gestolpert, weil sie eine Klasse namens "TaggedItem" erstellt haben, die mir nicht klar war. Ich war mir damals nicht sicher, ob das TaggedItem eine Platzhalter- "Bridge" -Klasse war. Meine natürliche Neigung wäre so etwas wie "Tag" mit einer Eigenschaft namens "Begriff" gewesen.
Chris Shelton

Antworten:

307

Sie möchten also das Content Types-Framework für Ihre Arbeit verwenden?

Stellen Sie sich zunächst folgende Frage: "Muss eines dieser Modelle auf die gleiche Weise mit anderen Modellen in Beziehung gesetzt werden und / oder werde ich diese Beziehungen später auf unvorhergesehene Weise wiederverwenden?" Der Grund, warum wir diese Frage stellen, liegt darin, dass das Framework für Inhaltstypen dies am besten kann: Es erstellt generische Beziehungen zwischen Modellen. Blah blah, lass uns in einen Code eintauchen und sehen, was ich meine.

# ourapp.models
from django.conf import settings
from django.db import models

# Assign the User model in case it has been "swapped"
User = settings.AUTH_USER_MODEL

# Create your models here
class Post(models.Model):
  author = models.ForeignKey(User)
  title = models.CharField(max_length=75)
  slug = models.SlugField(unique=True)
  body = models.TextField(blank=True)

class Picture(models.Model):
  author = models.ForeignKey(User)
  image = models.ImageField()
  caption = models.TextField(blank=True)

class Comment(models.Model):
  author = models.ForeignKey(User)
  body = models.TextField(blank=True)
  post = models.ForeignKey(Post)
  picture = models.ForeignKey(Picture)

Okay, wir haben also eine Möglichkeit, diese Beziehung theoretisch herzustellen. Als Python-Programmierer sagt Ihnen Ihr überlegener Verstand jedoch, dass dies scheiße ist und Sie es besser machen können. Gib mir fünf!

Betreten Sie das Content Types Framework!

Nun werden wir uns unsere Modelle genauer ansehen und sie überarbeiten, um sie "wiederverwendbarer" und intuitiver zu machen. Lassen Sie uns zunächst die beiden Fremdschlüssel unseres CommentModells entfernen und durch a ersetzen GenericForeignKey.

# ourapp.models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType

...

class Comment(models.Model):
  author = models.ForeignKey(User)
  body = models.TextField(blank=True)
  content_type = models.ForeignKey(ContentType)
  object_id = models.PositiveIntegerField()
  content_object = GenericForeignKey()

Also was ist passiert? Nun, wir haben den notwendigen Code hinzugefügt, um eine generische Beziehung zu anderen Modellen zu ermöglichen. Beachten Sie, dass es mehr als nur ein GenericForeignKey, sondern auch ein ForeignKeyzu ContentTypeund ein PositiveIntegerFieldfür das gibt object_id. Diese Felder dienen dazu, Django mitzuteilen, mit welchem ​​Objekttyp dies zusammenhängt und wie die ID für dieses Objekt lautet. In Wirklichkeit ist dies sinnvoll, da Django beide benötigt, um diese verwandten Objekte nachzuschlagen.

Nun, das ist nicht sehr Python-artig ... es ist irgendwie hässlich!

Sie suchen wahrscheinlich nach luftdichtem, makellosem, intuitivem Code, der Guido van Rossum stolz machen würde . Ich krieg dich. Schauen GenericRelationwir uns das Feld an, damit wir uns hübsch verbeugen können.

# ourapp.models
from django.contrib.contenttypes.fields import GenericRelation

...

class Post(models.Model):
  author = models.ForeignKey(User)
  title = models.CharField(max_length=75)
  slug = models.SlugField(unique=True)
  body = models.TextField(blank=True)
  comments = GenericRelation('Comment')

class Picture(models.Model):
  author = models.ForeignKey(User)
  image = models.ImageField()
  caption = models.TextField(blank=True)
  comments = GenericRelation('Comment')

Bam! So können Sie mit den Kommentaren für diese beiden Modelle arbeiten. Lassen Sie uns dies in unserer Shell tun (geben Sie es python manage.py shellaus Ihrem Django-Projektverzeichnis ein).

>>> from django.contrib.auth import get_user_model
>>> from ourapp.models import Picture, Post

# We use get_user_model() since we are referencing directly
User = get_user_model()

# Grab our own User object
>>> me = User.objects.get(username='myusername')

# Grab the first of our own pictures so we can comment on it
>>> pic = Picture.objects.get(author=me)

# Let's start making a comment for our own picture
>>> pic.comments.create(author=me, body="Man, I'm cool!")

# Let's go ahead and retrieve the comments for this picture now
>>> pic.comments.all()
[<Comment: "Man, I'm cool!">]

# Same for Post comments
>>> post = Post.objects.get(author=me)
>>> post.comments.create(author=me, body="So easy to comment now!")
>>> post.comments.all()
[<Comment: "So easy to comment now!"]

So einfach ist das.

Was sind die anderen praktischen Implikationen dieser "generischen" Beziehungen?

Generische Fremdschlüssel ermöglichen weniger aufdringliche Beziehungen zwischen verschiedenen Anwendungen. Angenommen, wir haben das Kommentarmodell in eine eigene App mit dem Namen gezogen chatterly. Jetzt möchten wir eine weitere Anwendung mit dem Namen erstellen, noise_nimbusin der Benutzer ihre Musik speichern, um sie mit anderen zu teilen.

Was ist, wenn wir diesen Songs Kommentare hinzufügen möchten? Nun, wir können einfach eine generische Beziehung zeichnen:

# noise_nimbus.models
from django.conf import settings
from django.contrib.contenttypes.fields import GenericRelation
from django.db import models

from chatterly.models import Comment

# For a third time, we take the time to ensure custom Auth isn't overlooked
User = settings.AUTH_USER_MODEL

# Create your models here
class Song(models.Model):
  '''
  A song which can be commented on.
  '''
  file = models.FileField()
  author = models.ForeignKey(User)
  title = models.CharField(max_length=75)
  slug = models.SlugField(unique=True)
  description = models.TextField(blank=True)
  comments = GenericRelation(Comment)

Ich hoffe, ihr fand das hilfreich, da ich gerne auf etwas gestoßen wäre, das mir die realistischere Anwendung von GenericForeignKeyund GenericRelationFeldern gezeigt hat.

Ist das zu schön um wahr zu sein?

Wie bei allem im Leben gibt es Vor- und Nachteile. Jedes Mal, wenn Sie mehr Code und mehr Abstraktion hinzufügen, werden die zugrunde liegenden Prozesse schwerer und etwas langsamer. Das Hinzufügen generischer Beziehungen kann ein wenig zu einer Leistungsdämpfung führen, obwohl versucht wird, die Ergebnisse intelligent zwischenzuspeichern. Alles in allem kommt es darauf an, ob die Sauberkeit und Einfachheit die geringen Leistungskosten überwiegt. Für mich ist die Antwort millionenfach ja.

Das Content Types-Framework enthält mehr, als ich hier angezeigt habe. Es gibt ein ganzes Maß an Granularität und eine ausführlichere Verwendung, aber für den Durchschnittsmenschen werden Sie es meiner Meinung nach 9 von 10 Mal so verwenden.

Generische Relationizer (?) Vorsicht!

Eine ziemlich große Einschränkung ist, dass bei Verwendung von a GenericRelation, wenn das Modell mit dem GenericRelationangewendeten ( Picture) gelöscht wird, auch alle verwandten ( Comment) Objekte gelöscht werden. Oder zumindest zum Zeitpunkt dieses Schreibens.

Chris Shelton
quelle
11
Also, wenn ich GenericRelationin verwende Postund Picturedann muss ich nicht verwenden object_id, content_typeund content_objectin Comment?
avi
5
Es wäre schön, eine so klare Beschreibung des Contenttype-Frameworks irgendwo in der offiziellen Django-Dokumentation zu haben. Was mich betrifft, wurde mir erst nach dem Lesen dieses Ports klar, was dieses Framework bewirkt. Danke dir.
Prokher
2
etwas spät ... aber ich habe gehört, dass Ihre Anwendung mit dem Framework für Inhaltstypen möglicherweise nicht richtig skaliert. Kann mir bitte jemand sagen, ob dies wahr ist oder ein Scherz?
Karan Kumar
1
Wie bei allem in der Programmierung, Karan, lautet die Antwort immer "es kommt darauf an". Ich würde sagen, verwenden Sie Inhaltstypen. Es ist eine Art "Kompromiss", einige der starren Grundlagen eines tabellenorientierten SQL-Systems zu umgehen. Optimieren Sie Ihre App nicht vorzeitig! Django ist am besten in der Lage, Ihnen aus dem Weg zu gehen, damit Sie die Anwendung der nächsten Generation schreiben können, die Sie sich immer gewünscht haben: Nutzen Sie die Funktionen zu Ihrem Vorteil!
Chris Shelton
2
Karan, da ist etwas Wahres dran. Ich arbeite an einer Anwendung, die Benachrichtigungen für Benutzer verfolgt. Jede Benachrichtigung hat eine GenericForeignKey-Beziehung zu einem anderen von uns gespeicherten Inhaltstyp. Jedes Mal, wenn ein Benutzer Benachrichtigungen anzeigt, gibt der ORM N Abfragen aus, um alle zugehörigen Inhalte abzurufen. Kaum ideal.
Travis Mehlinger
-2

Ok, die direkte Antwort auf Ihre Frage: (aus dem Django-Quellcode) lautet: Analysieren von Medientypen gemäß RFC 2616, Abschnitt 3.7.

Welches ist die Tränen-Art zu sagen, dass es den httpd-Header vom Typ "Inhalt" liest / erlaubt, ihn zu ändern / weiterzugeben.

Sie fordern jedoch ein Beispiel für mehr Übung. Ich habe 2 Vorschläge für Sie:

1: Überprüfen Sie diesen Code

def index(request):
   media_type='text/html'
   if request.META.has_key('CONTENT_TYPE'):
      media_type = request.META['CONTENT_TYPE'].split(';')[0]

   if media_type.lower() == 'application/json':
      return HttpResponse("""{ "ResponseCode": "Success"}""", content_type="application/json; charset=UTF-8")

   return HttpResponse("<h1>regular old joe</h1>");

2: Denken Sie daran, dass Django Python ist und als solches die Macht der Python-Community ausübt. Es gibt 2 tolle RESTFul-Plugins für Django. Wenn Sie also sehen möchten, wie tief das ganze Kaninchen ist, können Sie es sich ansehen.

Ich schlage vor, das Django-Rest-Framework-Tutorial durchzugehen, in dem speziell auf verschiedene Inhalte / Typen eingegangen wird. Hinweis: Es ist üblich, den Inhaltstyp-Header zu verwenden , um restful APIs zu "versionieren" .

Jeff Sheffield
quelle
1
Ist das das, worauf er sich bezieht? oder zum Contenttypes Framework?: docs.djangoproject.com/de/dev/ref/contrib/contenttypes
petkostas
1
Ja, ich habe mich auf das Framework für Inhaltstypen bezogen. Ich habe vielleicht nicht gut genug gearbeitet, um mich zu vermitteln. Ich schätze die Antwort trotzdem. Für was es wert ist, wenn das meine Frage wäre, hättest du es aus dem Park geworfen =)
Chris Shelton