Comment fonctionnent exactement les types de contenu Django?

148

J'ai vraiment du mal à saisir le concept des types de contenu de Django. Cela semble très hackish et, en fin de compte, contre la façon dont Python a tendance à faire les choses. Cela étant dit, si je veux utiliser Django, je dois travailler dans les limites du framework.

Je viens donc ici en me demandant si quelqu'un peut donner un exemple concret du fonctionnement d'un type de contenu et de sa mise en œuvre. Presque tous les tutoriels (principalement sur les blogs) que j'ai examinés ne font pas un excellent travail couvrant vraiment le concept. Ils semblent reprendre là où la documentation Django s'est arrêtée (ce qui ne semble nulle part).

Chris Shelton
la source
5
Je crois (quelqu'un me corrige si je me trompe) que les types de contenu sont quelque chose comme un polymorphisme, cela deviendra un outil entre vos mains une fois que votre projet commencera à avoir des modèles qui peuvent avoir de nombreuses formes différentes. L'exemple de balise dans la documentation est assez simple, vous voulez pouvoir étiqueter des éléments, mais vous ne voulez pas être spécifique au type d'éléments qu'ils sont, après tout une balise peut prendre en charge, des articles, des pages, des utilisateurs, des produits. Avec l'utilisation des types de contenu, vous pouvez créer des relations avec diverses implémentations différentes sans avoir à savoir quel est exactement le modèle associé.
petkostas
1
D'accord, alors là où j'ai trébuché, c'est qu'ils ont créé une classe nommée "TaggedItem", ce qui n'était pas clair pour moi. Je n'étais pas sûr alors si le TaggedItem était une classe «pont» d'espace réservé. Mon inclination naturelle aurait été quelque chose comme "Tag" avec une propriété nommée "term".
Chris Shelton

Réponses:

307

Vous souhaitez donc utiliser le framework Types de contenu sur votre travail?

Commencez par vous poser cette question: "Est-ce que l'un de ces modèles doit être lié de la même manière à d'autres modèles et / ou vais-je réutiliser ces relations de manière imprévue plus tard?" La raison pour laquelle nous posons cette question est que c'est ce que le cadre des types de contenu fait le mieux: il crée des relations génériques entre les modèles. Bla bla, plongeons dans un peu de code et voyons ce que je veux dire.

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

D'accord, nous avons donc un moyen de créer théoriquement cette relation. Cependant, en tant que programmeur Python, votre intellect supérieur vous dit que c'est nul et que vous pouvez faire mieux. Tape m'en cinq!

Entrez dans le cadre des types de contenu!

Eh bien, maintenant nous allons examiner de près nos modèles et les retravailler pour qu'ils soient plus «réutilisables» et intuitifs. Commençons par nous débarrasser des deux clés étrangères de notre Commentmodèle et remplaçons-les par un 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()

Alors, qu'est-ce-qu'il s'est passé? Eh bien, nous sommes entrés et avons ajouté le code nécessaire pour permettre une relation générique avec d'autres modèles. Remarquez qu'il y a plus qu'un simple GenericForeignKey, mais aussi un ForeignKeyto ContentTypeet un PositiveIntegerFieldpour le object_id. Ces champs servent à indiquer à Django le type d'objet auquel il est lié et l'id de cet objet. En réalité, cela a du sens car Django aura besoin des deux pour rechercher ces objets associés.

Eh bien, ce n'est pas très Python ... c'est un peu moche!

Vous êtes probablement à la recherche d'un code intuitif, impeccable et étanche qui ferait la fierté de Guido van Rossum . Je te comprends. Regardons le GenericRelationterrain pour que nous puissions mettre un joli arc là-dessus.

# 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! Juste comme ça, vous pouvez travailler avec les commentaires pour ces deux modèles. En fait, allons-y et faisons cela dans notre shell (tapez à python manage.py shellpartir du répertoire de votre projet 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!"]

C'est si simple.

Quelles sont les autres implications pratiques de ces relations «génériques»?

Les clés étrangères génériques permettent des relations moins intrusives entre diverses applications. Par exemple, disons que nous avons extrait le modèle Comment dans sa propre application nommée chatterly. Maintenant, nous voulons créer une autre application nommée noise_nimbusoù les gens stockent leur musique pour la partager avec d'autres.

Et si nous voulons ajouter des commentaires à ces chansons? Eh bien, nous pouvons simplement dessiner une relation générique:

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

J'espère que vous avez trouvé cela utile, car j'aurais aimé trouver quelque chose qui m'a montré l'application plus réaliste des champs GenericForeignKeyet GenericRelation.

Est-ce trop beau pour être vrai?

Comme pour tout dans la vie, il y a des avantages et des inconvénients. Chaque fois que vous ajoutez plus de code et plus d'abstraction, les processus sous-jacents deviennent plus lourds et un peu plus lents. L'ajout de relations génériques peut ajouter un peu d'atténuation des performances malgré le fait qu'il essaiera de mettre en cache ses résultats. Dans l'ensemble, il s'agit de savoir si la propreté et la simplicité l'emportent sur les faibles coûts de performance. Pour moi, la réponse est un million de fois oui.

Il y a plus dans le cadre des types de contenu que ce que j'ai affiché ici. Il y a tout un niveau de granularité et d'utilisation plus verbeuse, mais pour l'individu moyen, c'est ainsi que vous l'utiliserez 9 fois sur 10 à mon avis.

Relationnels génériques (?) Attention!

Une mise en garde assez importante est que lorsque vous utilisez a GenericRelation, si le modèle qui a GenericRelationappliqué ( Picture) est supprimé, tous les Commentobjets related ( ) seront également supprimés. Ou du moins au moment de la rédaction de cet article.

Chris Shelton
la source
11
Donc, si j'utilise GenericRelationin Postet que Pictureje n'ai pas besoin d'utiliser object_id, content_typeet content_objectin Comment?
avi
5
Ce serait bien d'avoir une description aussi claire du framework contenttype quelque part dans la documentation officielle de Django. Quant à moi, je n'ai réalisé ce que fait ce framework qu'après avoir lu ce port. Je vous remercie.
prokher le
2
peu de retard ... mais j'ai entendu dire qu'en utilisant un framework de type de contenu, votre application pourrait ne pas évoluer correctement. quelqu'un peut-il me dire si cela est vrai ou si c'est un canular?
Karan Kumar
1
Comme pour tout ce qui concerne la programmation, Karan, la réponse est toujours «ça dépend». Je dirais utiliser des types de contenu. C'est une sorte de «compromis» pour contourner certains des fondamentaux rigides d'un système SQL orienté table. N'optimisez pas prématurément votre application! Django est le meilleur pour sortir de votre chemin afin que vous puissiez écrire l'application de nouvelle génération que vous avez toujours voulue: utilisez ses fonctionnalités à votre avantage!
Chris Shelton
2
Karan, il y a du vrai. Je travaille sur une application qui suit les notifications des utilisateurs. Chaque notification a une relation GenericForeignKey avec un autre type de contenu que nous stockons. Chaque fois qu'un utilisateur affiche des notifications, l'ORM émet N requêtes pour obtenir tout le contenu associé. À peine idéal.
Travis Mehlinger
-2

Ok, la réponse directe à votre question: (à partir du code source de django) est: Analyse des types de médias selon RFC 2616, section 3.7.

Quelle est la façon déchirante de dire qu'il lit / permet-vous-de-modifier / passe le long de l'en - tête httpd 'Content-type' .

Cependant, vous demandez un exemple d'utilisation plus pratique. J'ai 2 suggestions pour vous:

1: examinez ce 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: rappelez-vous que django est python, et en tant que tel, il exerce la puissance de la communauté python. Il existe 2 super plugins RESTFul pour django. Donc, si vous voulez voir à quelle profondeur le lapin entier va, vous pouvez vérifier.

Je suggère de parcourir le didacticiel django-rest-framework qui abordera spécifiquement `` agir sur différents contenus / types ''. Remarque: il est courant d'utiliser l'en-tête de type de contenu pour «version» des API de repos .

Jeff Sheffield
la source
1
Est-ce à cela qu'il fait référence? ou au framework contenttypes
petkostas
1
Ouais, je faisais référence au cadre des types de contenu. Je n'ai peut-être pas fait un assez bon travail pour me transmettre. J'apprécie la réponse malgré tout. Pour ce que ça vaut, si c'était ma question, vous l'auriez fait sortir du parc =)
Chris Shelton