Chcesz więc używać frameworka Content Types w swojej pracy?
Zacznij od zadania sobie pytania: „Czy któryś z tych modeli musi być powiązany w ten sam sposób z innymi modelami i / lub czy później będę ponownie wykorzystywać te relacje w nieprzewidziany sposób?” Powodem, dla którego zadajemy to pytanie, jest to, że to właśnie najlepiej radzi sobie framework Content Types: tworzy ogólne relacje między modelami. Bla bla, zagłębmy się w jakiś kod i zobaczmy, co mam na myśli.
# 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)
Okej, więc mamy teoretyczny sposób na stworzenie tej relacji. Jednak jako programista Pythona, twój wyższy intelekt mówi ci, że to jest do bani i możesz zrobić lepiej. Piątka!
Wejdź do frameworka typów treści!
Cóż, teraz przyjrzymy się bliżej naszym modelom i przerobimy je, aby były bardziej „wielokrotnego użytku” i bardziej intuicyjne. Zacznijmy od pozbycia się dwóch kluczy obcych w naszym Comment
modelu i zastąp je 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()
Więc co się stało? Cóż, weszliśmy do środka i dodaliśmy niezbędny kod, aby umożliwić ogólną relację z innymi modelami. Zwróć uwagę, że istnieje więcej niż tylko a GenericForeignKey
, ale także a ForeignKey
do ContentType
i a PositiveIntegerField
dla object_id
. Te pola służą do informowania Django, z jakim typem obiektu jest to związane i jaki jest identyfikator tego obiektu. W rzeczywistości ma to sens, ponieważ Django będzie potrzebować obu, aby wyszukać te powiązane obiekty.
Cóż, to nie jest zbyt podobne do Pythona ... jest trochę brzydkie!
Prawdopodobnie szukasz szczelnego, nieskazitelnego, intuicyjnego kodu, z którego Guido van Rossum byłby dumny. Rozumiem cię. Spójrzmy na GenericRelation
pole, żebyśmy mogli założyć na to ładny łuk.
# 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! W ten sposób możesz pracować z komentarzami dla tych dwóch modeli. W rzeczywistości, zróbmy to w naszej powłoce (wpisz python manage.py shell
z katalogu projektu Django).
>>> 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!"]
To takie proste.
Jakie są inne praktyczne implikacje tych „ogólnych” relacji?
Ogólne klucze obce pozwalają na mniej inwazyjne relacje między różnymi aplikacjami. Na przykład, powiedzmy, że wyciągnęliśmy model Comment do jego własnej aplikacji o nazwie chatterly
. Teraz chcemy stworzyć kolejną aplikację o nazwie, w noise_nimbus
której ludzie przechowują swoją muzykę i dzielą się nią z innymi.
A co, jeśli chcemy dodać komentarze do tych piosenek? Cóż, możemy po prostu narysować ogólną relację:
# 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)
Mam nadzieję, że uznacie to za pomocne, ponieważ chciałbym natknąć się na coś, co pokazałoby mi bardziej realistyczne zastosowanie pól GenericForeignKey
i GenericRelation
.
Czy to zbyt piękne, aby mogło być prawdziwe?
Jak wszystko w życiu, są plusy i minusy. Za każdym razem, gdy dodajesz więcej kodu i więcej abstrakcji, podstawowe procesy stają się cięższe i nieco wolniejsze. Dodanie relacji ogólnych może nieco osłabić wydajność, mimo że będzie próbował inteligentnie buforować wyniki. Podsumowując, wszystko sprowadza się do tego, czy czystość i prostota przeważają nad niewielkimi kosztami wydajności. Dla mnie odpowiedź jest milion razy tak.
Struktura typów treści to więcej, niż pokazałem tutaj. Istnieje cały poziom szczegółowości i bardziej szczegółowego użycia, ale dla przeciętnej osoby w ten sposób będziesz go używać 9 na 10 razy, moim zdaniem.
Ogólne relacje (?) Uwaga!
Dość dużym zastrzeżeniem jest to, że jeśli używasz a GenericRelation
, jeśli model, w którym GenericRelation
zastosowano metodę ( Picture
), zostanie usunięty, wszystkie powiązane ( Comment
) obiekty również zostaną usunięte. A przynajmniej w chwili pisania tego tekstu.