Django内容类型究竟是如何工作的?

Chr*_*ton 132 python django

我真的很难掌握Django内容类型的概念.感觉非常hackish,并且最终反对Python倾向于做事情.话虽如此,如果我要使用Django,那么我必须在框架的范围内工作.

所以我来到这里想知道是否有人可以提供一个实际的现实世界示例,说明内容类型如何工作以及如何实现它.我所评论的几乎所有教程(主要是在博客上)并没有真正涵盖这个概念.他们似乎已经接受了Django文档遗留下来的地方(似乎无处可去).

Chr*_*ton 276

那么你想在你的工作中使用内容类型框架吗?

首先问问自己这个问题:"这些模型中的任何一个是否需要以与其他模型相同的方式相关联和/或我将在未来后期以不可预知的方式重用这些关系?" 我们之所以提出这个问题是因为这就是内容类型框架最擅长的:它创建了模型之间的通用关系.Blah blah,让我们深入研究一些代码,看看我的意思.

# 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)
Run Code Online (Sandbox Code Playgroud)

好的,所以我们确实有办法在理论上建立这种关系.但是,作为一名Python程序员,你的超级智慧告诉你这很糟糕,你可以做得更好.举手击掌!

输入内容类型框架!

那么,现在我们将仔细研究一下我们的模型,并将它们重新设计为更具"可重用性"和直观性.让我们首先摆脱Comment模型上的两个外键,然后用一个替换它们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()
Run Code Online (Sandbox Code Playgroud)

所以发生了什么事?好吧,我们进入并添加了必要的代码以允许与其他模型的通用关系.请注意如何有不仅仅是一个更GenericForeignKey,更是一种 ForeignKeyContentTypePositiveIntegerFieldobject_id.这些字段用于告诉Django与此对象相关的对象类型以及该对象的id是什么.实际上,这是有道理的,因为Django将需要两者来查找这些相关对象.

好吧,那不像Python那样...它有点难看!

您可能正在寻找能够让Guido van Rossum自豪的气密,一尘不染,直观的代码.我找到了你 让我们来看看这个GenericRelation领域,这样我们就能对此大加赞赏.

# 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')
Run Code Online (Sandbox Code Playgroud)

巴姆!就像那样,你可以使用这两个模型的评论.实际上,让我们继续在我们的shell(python manage.py shell从Django项目目录中输入)中执行此操作.

>>> from django.contrib.auth import get_user_model
>>> from ourapp.models import Picture

# 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!">]
Run Code Online (Sandbox Code Playgroud)

就这么简单.

这些"通用"关系的其他实际意义是什么?

通用外键允许各种应用程序之间的侵入性较小的关系.例如,假设我们将评论模型拉出到它自己的应用程序中chatterly.现在我们要创建另一个名为noise_nimbus人们存储音乐的应用程序,以便与他人分享.

如果我们想为这些歌曲添加评论怎么办?好吧,我们可以绘制一个通用的关系:

# 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)
Run Code Online (Sandbox Code Playgroud)

我希望你们发现这很有帮助,因为我喜欢看到一些东西,这些东西向我展示了更真实的应用GenericForeignKeyGenericRelation领域.

这真是太好了吗?

与生活中的任何事物一样,有利有弊.无论何时添加更多代码和更多抽象,底层进程都会变得更重,速度更慢.添加泛型关系可以增加一点性能阻尼,尽管它会尝试并智能缓存其结果.总而言之,它归结为清洁度和简洁性是否超过了小的性能提升.对我来说,答案是百万次是的.

内容类型框架比我在此处显示的更多.有一个完整的粒度级别和更详细的使用,但对于普通人来说,这就是我认为你将在10次中使用它的方式.

通用关系器(?)要小心!

一个相当大的警告是当你使用a时GenericRelation,如果删除了具有GenericRelationapplied(Picture)的模型,那么所有related(Comment)对象也将被删除.或至少截至撰写本文时.

  • 因此,如果我在`Post`和`Picture`中使用`GenericRelation`,那么我不需要在`Comment`中使用`object_id`,`content_type`和`content_object`? (10认同)
  • 在Django官方文档中的某个地方对contenttype框架进行如此简洁的描述会很好.至于我,我只是在读完这个端口后才意识到这个框架的作用.谢谢. (3认同)
  • 我迟到了...但我听说使用内容类型框架,您的应用程序可能无法正确扩展.有人可以告诉我这是真的还是恶作剧? (2认同)
  • 卡兰,有一些道理.我正在开发一个跟踪用户通知的应用程序.每个通知都与我们存储的其他类型的内容有GenericForeignKey关系.每次用户查看通知时,ORM都会发出N个查询以获取所有相关内容.很难理想. (2认同)