Come funzionano esattamente i tipi di contenuto Django?


148

Sto davvero facendo fatica a capire il concetto dei tipi di contenuto di Django. Sembra molto hacker e, in definitiva, contro il modo in cui Python tende a fare le cose. Detto questo, se ho intenzione di usare Django, devo lavorare entro i limiti del framework.

Quindi vengo qui chiedendomi se qualcuno può dare un esempio pratico nel mondo reale di come funziona un tipo di contenuto e come lo implementereste. Quasi tutti i tutorial (per lo più sui blog) che ho recensito non fanno un ottimo lavoro sul concetto. Sembrano riprendere da dove era stata interrotta la documentazione di Django (quello che sembra da nessuna parte).


5
Credo (qualcuno mi corregga se sbaglio) che i tipi di contenuto sono qualcosa di simile a un polimorfismo, diventerà uno strumento nelle tue mani quando il tuo progetto inizierà ad avere modelli che possono avere molte forme diverse. L'esempio di tag nella documentazione è piuttosto semplice, vuoi essere in grado di taggare gli articoli, ma non vuoi essere specifico del tipo di articoli che sono, dopo tutto un tag può supportare, post, pagine, utenti, prodotti. Con l'uso dei tipi di contenuto è possibile creare relazioni con varie implementazioni diverse senza dover conoscere esattamente il modello relativo.
Petkostas,

1
Ok, quindi dove sono stato inciampato è che hanno fatto una classe chiamata "TaggedItem" che non mi era chiara. Non ero sicuro quindi se TaggedItem fosse una classe "bridge" segnaposto. La mia naturale inclinazione sarebbe stata qualcosa come "Tag" con una proprietà chiamata "termine".
Chris Shelton,

Risposte:


307

Quindi vuoi usare il framework Tipi di contenuto per il tuo lavoro?

Inizia ponendoti questa domanda: "Qualcuno di questi modelli deve essere correlato allo stesso modo con altri modelli e / o riutilizzerò queste relazioni in modi non previsti più avanti?" Il motivo per cui facciamo questa domanda è perché questo è ciò che fa meglio il framework Tipi di contenuto: crea relazioni generiche tra i modelli. Blah blah, immergiamoci in un po 'di codice e vediamo cosa intendo.

# 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)

Va bene, quindi abbiamo un modo per creare teoricamente questa relazione. Tuttavia, come programmatore Python, il tuo intelletto superiore ti sta dicendo che fa schifo e puoi fare di meglio. Il cinque!

Entra nel framework Tipi di contenuto!

Bene, ora daremo uno sguardo da vicino ai nostri modelli e li rielaboreremo per renderli più "riutilizzabili" e intuitivi. Iniziamo eliminando le due chiavi esterne sul nostro Commentmodello e sostituendole con una 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()

Allora, cos'è successo? Bene, siamo entrati e abbiamo aggiunto il codice necessario per consentire una relazione generica con altri modelli. Notare come non ci sia solo un GenericForeignKey, ma anche un ForeignKeya ContentTypee un PositiveIntegerFieldper il object_id. Questi campi servono per dire a Django a quale tipo di oggetto è correlato e qual è l'id per quell'oggetto. In realtà, questo ha senso perché Django avrà bisogno di entrambi per cercare questi oggetti correlati.

Beh, non è molto simile a Python ... è un po 'brutto!

Probabilmente stai cercando un codice a tenuta stagna, perfettamente pulito e intuitivo che renderebbe orgoglioso Guido van Rossum . Ti capisco. Diamo un'occhiata al GenericRelationcampo in modo da poter fare un bel inchino su questo.

# 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! Proprio così puoi lavorare con i commenti per questi due modelli. In effetti, andiamo avanti e facciamolo nella nostra shell (digitare python manage.py shelldalla directory del progetto 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!"]

È così semplice.

Quali sono le altre implicazioni pratiche di queste relazioni "generiche"?

Le chiavi esterne generiche consentono relazioni meno invasive tra le varie applicazioni. Ad esempio, supponiamo di aver estratto il modello Comment nella sua app denominata chatterly. Ora vogliamo creare un'altra applicazione chiamata noise_nimbusdove le persone memorizzano la loro musica per condividerla con gli altri.

E se volessimo aggiungere commenti a quelle canzoni? Bene, possiamo solo disegnare una relazione generica:

# 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)

Spero che voi ragazzi vi sia stato utile perché mi sarebbe piaciuto trovare qualcosa che mi mostrasse l'applicazione GenericForeignKeye i GenericRelationcampi più realistici .

È troppo bello per essere vero?

Come per qualsiasi cosa nella vita, ci sono pro e contro. Ogni volta che aggiungi più codice e più astrazione, i processi sottostanti diventano più pesanti e un po 'più lenti. L'aggiunta di relazioni generiche può aggiungere un po 'di smorzamento delle prestazioni nonostante il fatto che proverà a mettere in cache i risultati. Tutto sommato, dipende dal fatto che la pulizia e la semplicità superino i piccoli costi delle prestazioni. Per me, la risposta è un milione di volte sì.

C'è molto di più nel framework Tipi di contenuto di quello che ho visualizzato qui. C'è un intero livello di granularità e un uso più dettagliato, ma per l'individuo medio, questo è come lo userete 9 su 10 volte secondo me.

Rapporti generici (?) Attenzione!

Un avvertimento piuttosto grande è che quando si utilizza a GenericRelation, se il modello che ha il GenericRelationmetodo apply ( Picture) viene eliminato, Commentverranno eliminati anche tutti gli oggetti correlati ( ). O almeno al momento della stesura di questo scritto.


11
Quindi se uso GenericRelationin Poste Picturequindi non ho bisogno di usare object_id, content_typee content_objectin Comment?
avi

5
Sarebbe bello avere una descrizione così chiara del framework contenttype da qualche parte nella documentazione ufficiale di Django. Quanto a me, ho capito cosa fa questo framework solo dopo aver letto questa porta. Grazie.
circa

2
un po 'tardi ... ma ho sentito che usando il framework dei tipi di contenuto, la tua applicazione potrebbe non adattarsi correttamente. qualcuno può dirmi se questo è vero o una bufala?
Karan Kumar il

1
Come per tutto ciò che riguarda la programmazione, Karan, la risposta è sempre "dipende". Direi usare tipi di contenuto. È un "compromesso" di sorta bypassare alcuni dei fondamentali rigidi di un sistema SQL orientato alla tabella. Non ottimizzare prematuramente la tua app! Django è il migliore per toglierti di mezzo in modo da poter scrivere l'applicazione di prossima generazione che hai sempre desiderato: usa le sue funzionalità a tuo vantaggio!
Chris Shelton,

2
Karan, c'è del vero. Sto lavorando a un'applicazione che tiene traccia delle notifiche per gli utenti. Ogni notifica ha una relazione GenericForeignKey con qualche altro tipo di contenuto che memorizziamo. Ogni volta che un utente visualizza le notifiche, l'ORM invia N query per ottenere tutto il contenuto correlato. Difficilmente ideale.
Travis Mehlinger,

-2

Bene, la risposta diretta alla tua domanda: (dal codice sorgente di django) è: Tipi di media analizzati secondo RFC 2616, sezione 3.7.

Qual è il modo in cui le lacrime dicono che legge / ti permette di modificare / passa lungo l' intestazione httpd 'Content-type' .

Tuttavia, stai chiedendo un esempio di utilizzo più pratico. Ho 2 suggerimenti per te:

1: esamina questo codice

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: ricorda che django è python e come tale esercita il potere della comunità python. Ci sono 2 fantastici plugin RESTFul su django. Quindi, se vuoi vedere quanto in profondità arriva il coniglio, puoi dare un'occhiata.

Suggerisco di passare attraverso il tutorial django-rest-framework che affronterà nello specifico "agire su contenuti / tipi diversi". Nota: è pratica comune utilizzare l'intestazione del tipo di contenuto per le API restanti "versione" .


1
È quello a cui si riferisce? o al framework contenttypes ?: docs.djangoproject.com/en/dev/ref/contrib/contenttypes
petkostas,

1
Sì, mi riferivo al framework dei tipi di contenuto. Potrei non aver fatto un lavoro abbastanza buono trasportandomi. Apprezzo la risposta a prescindere. Per quanto valesse la pena, se questa fosse la mia domanda, l'avresti buttato fuori dal parco =)
Chris Shelton,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.