Então você deseja usar a estrutura de tipos de conteúdo em seu trabalho?
Comece perguntando a si mesmo esta pergunta: "Algum desses modelos precisa estar relacionado da mesma maneira com outros modelos e / ou vou reutilizar esses relacionamentos de maneiras imprevisíveis mais adiante?" A razão pela qual fazemos essa pergunta é porque é o que a estrutura de Tipos de conteúdo faz melhor: cria relações genéricas entre modelos. Blá, blá, vamos mergulhar em algum código e ver o que quero dizer.
# 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)
Ok, então temos uma maneira de criar teoricamente esse relacionamento. No entanto, como programador Python, seu intelecto superior está lhe dizendo que isso é péssimo e você pode fazer melhor. Toca aqui!
Entre na estrutura de tipos de conteúdo!
Bem, agora vamos dar uma olhada em nossos modelos e retrabalhá-los para serem mais "reutilizáveis" e intuitivos. Vamos começar removendo as duas chaves estrangeiras do nosso Comment
modelo e substituindo-as por 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()
Então o que aconteceu? Bem, nós adicionamos o código necessário para permitir uma relação genérica com outros modelos. Observe como há mais do que apenas um GenericForeignKey
, mas também um ForeignKey
para ContentType
e um PositiveIntegerField
para oobject_id
. Esses campos são para dizer ao Django a que tipo de objeto isso está relacionado e qual é o ID desse objeto. Na realidade, isso faz sentido porque o Django precisará de ambos para pesquisar esses objetos relacionados.
Bem, isso não é muito parecido com o Python ... é meio feio!
Você provavelmente está procurando um código impermeável, impecável e intuitivo que deixaria Guido van Rossum orgulhoso. Eu entendo você. Vejamos o GenericRelation
campo para que possamos dar um belo arco nisso.
# 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! Assim, você pode trabalhar com os Comentários desses dois modelos. De fato, vamos fazer isso em nosso shell (digite python manage.py shell
no diretório do seu projeto 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!"]
É simples assim.
Quais são as outras implicações práticas dessas relações "genéricas"?
Chaves estrangeiras genéricas permitem relações menos intrusivas entre vários aplicativos. Por exemplo, digamos que extraímos o modelo de comentário para seu próprio aplicativo chamado chatterly
. Agora, queremos criar outro aplicativo chamado noise_nimbus
onde as pessoas armazenam suas músicas para compartilhar com outras pessoas.
E se quisermos adicionar comentários a essas músicas? Bem, podemos apenas desenhar uma relação 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 vocês tenham achado isso útil, pois eu adoraria encontrar algo que me mostrasse a aplicação GenericForeignKey
e GenericRelation
campos mais realistas .
Isso é bom demais para ser verdade?
Como em qualquer coisa na vida, existem prós e contras. Sempre que você adiciona mais código e mais abstração, os processos subjacentes ficam mais pesados e mais lentos. A adição de relações genéricas pode adicionar um pouco de um amortecedor de desempenho, apesar de tentar e armazenar em cache seus resultados com inteligência. Em suma, tudo se resume a se a limpeza e a simplicidade superam os pequenos custos de desempenho. Para mim, a resposta é um milhão de vezes sim.
Há mais na estrutura de Tipos de conteúdo do que eu exibi aqui. Existe todo um nível de granularidade e uso mais detalhado, mas para o indivíduo médio, é assim que você o utilizará 9 em 10 vezes, na minha opinião.
Relacionadores genéricos (?) Cuidado!
Uma ressalva bastante grande é que, quando você usa a GenericRelation
, se o modelo que tiver GenericRelation
aplicado ( Picture
) for excluído, todos os Comment
objetos relacionados ( ) também serão excluídos. Ou pelo menos no momento em que este artigo foi escrito.