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.