Jak dokładnie działają typy zawartości Django?

148

Naprawdę trudno mi zrozumieć koncepcję typów treści Django. Wydaje się to bardzo hakerskie i ostatecznie przeciwne temu, jak Python ma tendencję do robienia rzeczy. Biorąc to pod uwagę, jeśli mam używać Django, muszę pracować w ramach tego frameworka.

Przychodzę więc tutaj i zastanawiam się, czy ktoś może podać praktyczny przykład ze świata rzeczywistego, jak działa typ treści i jak można go zaimplementować. Prawie wszystkie samouczki (głównie na blogach), które recenzowałem, nie radzą sobie tak naprawdę z koncepcją. Wydaje się, że wychodzą z miejsca, w którym dokumentacja Django została przerwana (co wydaje się nigdzie).

Chris Shelton
źródło
5
Wierzę (ktoś mnie poprawi, jeśli się mylę), że typy treści są czymś w rodzaju polimorfizmu, staną się narzędziem w twoich rękach, gdy twój projekt zacznie mieć modele, które mogą mieć wiele różnych form. Przykład tagu w dokumentacji jest dość prosty, chcesz mieć możliwość tagowania elementów, ale nie chcesz być konkretny, w końcu tag może obsługiwać, posty, strony, użytkownicy, produkty. Za pomocą typów Content możesz tworzyć relacje do różnych różnych implementacji bez konieczności znajomości modelu, który jest do nich przypisany.
petkostas
1
Okay, więc potknąłem się, że stworzyli klasę o nazwie „TaggedItem”, która nie była dla mnie jasna. Nie byłem wtedy pewien, czy TaggedItem jest zastępczą klasą „pomostową”. Moją naturalną skłonnością byłoby coś w rodzaju „Tag” z właściwością o nazwie „term”.
Chris Shelton,

Odpowiedzi:

307

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 Commentmodelu 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 ForeignKeydo ContentTypei a PositiveIntegerFielddla 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 GenericRelationpole, ż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 shellz 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_nimbusktó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 GenericForeignKeyi 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 GenericRelationzastosowano metodę ( Picture), zostanie usunięty, wszystkie powiązane ( Comment) obiekty również zostaną usunięte. A przynajmniej w chwili pisania tego tekstu.

Chris Shelton
źródło
11
Więc jeśli używam GenericRelationw, Posta Picturepotem nie muszę object_id, content_typei content_objectw Comment?
avi
5
Byłoby miło mieć taki czysty opis frameworka contenttype gdzieś w oficjalnej dokumentacji Django. Jeśli chodzi o mnie, to dopiero po przeczytaniu tego portu zdałem sobie sprawę, co robi ten framework. Dziękuję Ci.
prokher
2
trochę późno ... ale słyszałem, że używając frameworka typu zawartości, twoja aplikacja może nie skalować się prawidłowo. czy ktoś może mi powiedzieć, czy to prawda, czy mistyfikacja?
Karan Kumar
1
Jak w przypadku wszystkiego w programowaniu, Karan, odpowiedź zawsze brzmi „to zależy”. Powiedziałbym, że używaj typów treści. Jest to swego rodzaju „kompromis” polegający na obejściu niektórych sztywnych podstaw systemu SQL zorientowanego na tabele. Nie optymalizuj swojej aplikacji przedwcześnie! Django najlepiej wychodzi z drogi, dzięki czemu możesz napisać aplikację nowej generacji, o której zawsze marzyłeś: wykorzystaj jej funkcje na swoją korzyść!
Chris Shelton,
2
Karan, jest w tym trochę prawdy. Pracuję nad aplikacją, która śledzi powiadomienia dla użytkowników. Każde powiadomienie ma związek GenericForeignKey z innym rodzajem przechowywanych przez nas treści. Za każdym razem, gdy użytkownik wyświetla powiadomienia, ORM wysyła N zapytań, aby uzyskać całą powiązaną zawartość. Mało idealna.
Travis Mehlinger
-2

Dobra, bezpośrednia odpowiedź na twoje pytanie: (z kodu źródłowego django) brzmi: Analiza typów multimediów zgodnie z RFC 2616, sekcja 3.7.

To jest łzawym sposobem na powiedzenie, że czyta / umożliwia-ci-modyfikację / przechodzi wzdłuż nagłówka httpd „Content-type” .

Jednak prosisz o bardziej praktyczny przykład użycia. Mam dla Ciebie 2 sugestie:

1: sprawdź ten kod

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: pamiętaj, że django to Python i jako takie dzierży potęgę społeczności Pythona. Istnieją 2 niesamowite wtyczki RESTFul do django. Jeśli więc chcesz zobaczyć, jak głęboki jest cały królik, możesz to sprawdzić.

Proponuję zapoznać się z samouczkiem django-rest-framework, który w szczególności zajmie się „działaniem na różnych treściach / typach”. Uwaga: powszechną praktyką jest używanie nagłówka typu zawartości do „wersji” spokojnych interfejsów API .

Jeff Sheffield
źródło
1
Czy to właśnie ma na myśli? lub do frameworka contenttypes ?
petkostas
1
Tak, miałem na myśli ramy typów treści. Być może nie wykonałem wystarczająco dobrej roboty, aby się przekazać. Mimo wszystko doceniam odpowiedź. Cóż to jest warte, gdyby to było moje pytanie, wyrzuciłbyś go z parku =)
Chris Shelton,