Como exatamente os tipos de conteúdo do Django funcionam?

148

Estou realmente tendo dificuldades para entender o conceito dos tipos de conteúdo do Django. Parece muito hackish e, finalmente, contra a maneira como o Python tende a fazer as coisas. Dito isto, se vou usar o Django, tenho que trabalhar dentro dos limites do framework.

Então, eu estou aqui pensando se alguém pode dar um exemplo prático do mundo real de como um tipo de conteúdo funciona e como você o implementaria. Quase todos os tutoriais (principalmente em blogs) que revi não fazem um ótimo trabalho realmente cobrindo o conceito. Eles parecem entender de onde parou a documentação do Django (o que parece ser lugar nenhum).

Chris Shelton
fonte
5
Acredito (alguém me corrija se estiver errado) que os tipos de conteúdo são como um polimorfismo, que se tornará uma ferramenta em suas mãos assim que seu projeto começar a ter modelos que podem ter muitas formas diferentes. O exemplo de tag na documentação é bastante simples: você deseja etiquetar itens, mas não quer ser específico a que tipo de itens eles são, afinal, uma tag pode suportar, postagens, páginas, usuários, produtos. Com o uso dos tipos de conteúdo, você pode criar relações com várias implementações diferentes sem precisar saber exatamente qual é o modelo de relacionamento.
precisa saber é
1
Ok, então, onde eu fui enganado é que eles criaram uma classe chamada "TaggedItem" que não estava clara para mim. Eu não tinha certeza se o TaggedItem era uma classe de "ponte" de espaço reservado. Minha inclinação natural teria sido algo como "Tag" com uma propriedade chamada "term".
Chris Shelton

Respostas:

307

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 Commentmodelo 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 ForeignKeypara ContentTypee um PositiveIntegerFieldpara 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 GenericRelationcampo 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 shellno 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_nimbusonde 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 GenericForeignKeye GenericRelationcampos 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 GenericRelationaplicado ( Picture) for excluído, todos os Commentobjetos relacionados ( ) também serão excluídos. Ou pelo menos no momento em que este artigo foi escrito.

Chris Shelton
fonte
11
Então, se eu usar GenericRelationem Poste Picture, em seguida, eu não preciso de uso object_id, content_typee content_objectem Comment?
avi
5
Seria bom ter uma descrição tão limpa do framework contenttype em algum lugar da documentação oficial do Django. Quanto a mim, percebi o que essa estrutura faz apenas depois de ler esta porta. Obrigado.
Prokher 02/02
2
um pouco tarde ... mas ouvi dizer que, usando a estrutura de tipo de conteúdo, seu aplicativo pode não ser dimensionado corretamente. alguém pode me dizer se isso é verdade ou uma farsa?
Karan Kumar
1
Como em tudo na programação, Karan, a resposta é sempre "depende". Eu diria que usar tipos de conteúdo. É um tipo de "compromisso" para ignorar alguns dos fundamentos rígidos de um sistema SQL orientado a tabelas. Não otimize prematuramente seu aplicativo! O Django é o melhor para sair do seu caminho, para que você possa escrever o aplicativo da próxima geração que você sempre desejou: use os recursos a seu favor!
Chris Shelton
2
Karan, há alguma verdade nisso. Estou trabalhando em um aplicativo que rastreia notificações para usuários. Cada notificação tem um relacionamento GenericForeignKey com algum outro tipo de conteúdo que armazenamos. Sempre que um usuário visualiza notificações, o ORM emite N consultas para obter todo o conteúdo relacionado. Dificilmente ideal.
Travis Mehlinger
-2

Ok, bem, a resposta direta à sua pergunta: (do código-fonte do django) é: Tipos de mídia analisando de acordo com a RFC 2616, seção 3.7.

Essa é a maneira mais comum de dizer que lê / permite que você modifique / passa ao longo do cabeçalho httpd 'Content-type' .

No entanto, você está pedindo um exemplo de uso mais prático. Tenho 2 sugestões para você:

1: examine este código

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: lembre-se de que o django é python e, como tal, exerce o poder da comunidade python. Existem 2 plugins RESTFul incríveis para o django. Então, se você quiser ver a profundidade do coelho inteiro, pode conferir.

Sugiro seguir o tutorial do django-rest-framework, que abordará especificamente 'atuar em diferentes tipos / conteúdo'. Nota: É prática comum usar o cabeçalho do tipo de conteúdo para 'versão' APIs repousantes .

Jeff Sheffield
fonte
1
É a isso que ele está se referindo? ou para a estrutura contenttypes ?: docs.djangoproject.com/en/dev/ref/contrib/contenttypes
petkostas
1
Sim, eu estava me referindo à estrutura de tipos de conteúdo. Talvez eu não tenha feito um bom trabalho me transportando. Agradeço a resposta independentemente. Para que o seu valor, se fosse a minha pergunta, você teria batido para fora do parque =)
Chris Shelton