Entonces, ¿desea utilizar el marco de Tipos de contenido en su trabajo?
Comience por hacerse esta pregunta: "¿Es necesario que alguno de estos modelos esté relacionado de la misma manera con otros modelos y / o reutilizaré estas relaciones de manera imprevista más adelante?" La razón por la que hacemos esta pregunta es porque esto es lo que el marco de Tipos de contenido hace mejor: crea relaciones genéricas entre modelos. Bla, bla, vamos a sumergirnos en un código y ver a qué me refiero.
# 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)
Bien, entonces tenemos una manera de crear teóricamente esta relación. Sin embargo, como programador de Python, su intelecto superior le dice que esto apesta y que puede hacerlo mejor. ¡Cinco altos!
¡Ingrese al marco de tipos de contenido!
Bueno, ahora vamos a echar un vistazo de cerca a nuestros modelos y modificarlos para que sean más "reutilizables" e intuitivos. Comencemos por deshacernos de las dos claves foráneas en nuestro Comment
modelo y reemplazarlas con a 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()
¿Entonces qué pasó? Bueno, entramos y agregamos el código necesario para permitir una relación genérica con otros modelos. Observe cómo hay más que solo un GenericForeignKey
, pero también un ForeignKey
to ContentType
y un PositiveIntegerField
para el object_id
. Estos campos son para decirle a Django con qué tipo de objeto está relacionado y cuál es la identificación para ese objeto. En realidad, esto tiene sentido porque Django necesitará ambos para buscar estos objetos relacionados.
Bueno, eso no es muy parecido a Python ... ¡es un poco feo!
Probablemente esté buscando un código hermético, impecable e intuitivo que enorgullezca a Guido van Rossum . Te entiendo Echemos un vistazo al GenericRelation
campo para que podamos hacer una hermosa reverencia sobre esto.
# 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! Solo así puede trabajar con los Comentarios para estos dos modelos. De hecho, sigamos adelante y hagámoslo en nuestro shell (escriba python manage.py shell
desde el directorio de su proyecto 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!"]
Es así de simple.
¿Cuáles son las otras implicaciones prácticas de estas relaciones "genéricas"?
Las claves foráneas genéricas permiten relaciones menos intrusivas entre varias aplicaciones. Por ejemplo, supongamos que sacamos el modelo de comentarios en su propia aplicación llamada chatterly
. Ahora queremos crear otra aplicación llamada noise_nimbus
donde las personas almacenan su música para compartirla con otros.
¿Qué pasa si queremos agregar comentarios a esas canciones? Bueno, podemos dibujar una relación genérica:
# 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)
Espero que les haya resultado útil, ya que me hubiera encantado haber encontrado algo que me mostró la aplicación GenericForeignKey
y los GenericRelation
campos más realistas .
¿Es esto demasiado bueno para ser verdad?
Como con cualquier cosa en la vida, hay pros y contras. Cada vez que agrega más código y más abstracción, los procesos subyacentes se vuelven más pesados y un poco más lentos. Agregar relaciones genéricas puede agregar un poco de amortiguador de rendimiento a pesar del hecho de que intentará almacenar en caché inteligente sus resultados. Con todo, todo se reduce a si la limpieza y la simplicidad superan los pequeños costos de rendimiento. Para mí, la respuesta es un millón de veces sí.
Hay más en el marco de Tipos de contenido de lo que he mostrado aquí. Hay un nivel completo de granularidad y un uso más detallado, pero para el individuo promedio, así es como lo usará 9 de cada 10 veces en mi opinión.
Relacionadores genéricos (?) ¡Cuidado!
Una advertencia bastante grande es que cuando utiliza un GenericRelation
, si se elimina el modelo que tiene GenericRelation
aplicado ( Picture
), todos los Comment
objetos relacionados ( ) también se eliminarán. O al menos al momento de escribir esto.