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

Posted

技术标签:

【中文标题】Django 内容类型究竟是如何工作的?【英文标题】:How exactly do Django content types work? 【发布时间】:2014-01-20 15:18:16 【问题描述】:

我真的很难掌握 Django 内容类型的概念。感觉非常骇人听闻,最终与 Python 的做事方式背道而驰。话虽如此,如果我要使用 Django,那么我必须在框架的范围内工作。

所以我来到这里想知道是否有人可以给出一个实际的真实世界示例来说明内容类型如何工作以及您将如何实现它。我看过的几乎所有教程(主要是在博客上)都没有很好地真正涵盖这个概念。他们似乎从 Django 文档中断的地方接了过来(似乎无处可去)。

【问题讨论】:

我相信(如果我错了,请有人纠正我)内容类型类似于多态性,一旦您的项目开始拥有可以具有多种不同形式的模型,它将成为您手中的工具。文档中的标签示例非常简单,您希望能够标记项目,但您不想具体说明它们是什么类型的项目,毕竟标签可以支持,帖子,页面,用户,产品。通过使用内容类型,您可以创建与各种不同实现的关系,而无需知道相关模型到底是什么。 好的,所以我被绊倒的地方是他们创建了一个名为“TaggedItem”的类,我不清楚。那时我不确定 TaggedItem 是否是占位符“桥”类。我的自然倾向是类似于带有名为“term”的属性的“Tag”。 【参考方案1】:

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

首先问自己这个问题:“这些模型中的任何一个都需要以相同的方式与其他模型相关联吗?或者我以后会以不可预见的方式重用这些关系吗?”我们问这个问题的原因是因为这是 Content Types 框架最擅长的:它在模型之间创建通用关系。等等,让我们深入研究一些代码,看看我的意思。

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

好的,所以我们确实有办法在理论上建立这种关系。然而,作为一名 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()

那么,发生了什么?好吧,我们进入并添加了必要的代码以允许与其他模型建立通用关系。请注意,不仅仅是GenericForeignKey,还有ForeignKeyContentTypePositiveIntegerField 对应object_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')

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

>>> 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!"]

就这么简单。

这些“通用”关系的其他实际含义是什么?

通用外键可以减少各种应用程序之间的干扰关系。例如,假设我们将 Comment 模型提取到它自己的名为 chatterly 的应用程序中。现在我们要创建另一个名为 noise_nimbus 的应用程序,人们可以在其中存储他们的音乐以与他人分享。

如果我们想在这些歌曲中添加 cmets 怎么办?好吧,我们可以画一个通用关系:

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

我希望你们发现这对你们有帮助,因为我很想遇到一些可以让我看到 GenericForeignKeyGenericRelation 字段的更现实应用的东西。

这真的好得令人难以置信吗?

就像生活中的任何事情一样,有利也有弊。每当您添加更多代码和更多抽象时,底层流程就会变得更重且更慢。添加通用关系可以增加一点性能阻尼器,尽管它会尝试智能缓存其结果。总而言之,它归结为清洁和简单是否超过了小的性能成本。对我来说,答案是一百万次是的。

内容类型框架的内容比我在这里展示的要多。有一个完整的粒度级别和更详细的用法,但对于普通人来说,我认为这就是你将使用它的 10 次中的 9 次。

泛型关系器(?)小心!

一个比较大的警告是当你使用GenericRelation时,如果应用了GenericRelation的模型(Picture)被删除,所有相关的(Comment)对象也会被删除。或者至少在撰写本文时。

【讨论】:

所以如果我在PostPicture 中使用GenericRelation 那么我不需要在Comment 中使用object_idcontent_typecontent_object 如果能在官方 Django 文档的某处对内容类型框架进行如此清晰的描述,那就太好了。至于我,我只有在阅读了这个端口之后才意识到这个框架的作用。谢谢。 有点晚了...但我听说使用内容类型框架,您的应用程序可能无法正确扩展。谁能告诉我这是真的还是骗局? 就像编程中的一切一样,Karan,答案总是“视情况而定”。我会说使用内容类型。绕过面向表的 SQL 系统的一些僵化基础是一种“妥协”。不要过早地优化您的应用程序! Django 最擅长摆脱你的束缚,这样你就可以编写你一直想要的下一代应用程序:利用它的特性来发挥你的优势! 卡兰,这是有道理的。我正在开发一个跟踪用户通知的应用程序。每个通知与我们存储的某些其他类型的内容都有一个 GenericForeignKey 关系。每次用户查看通知时,ORM 都会发出 N 次查询以获取所有相关内容。不太理想。【参考方案2】:

好吧,您的问题的直接答案:(来自 django 源代码)是: Media Types parsing according to RFC 2616, section 3.7.

这是一种流泪的说法,它读取/允许你修改/传递 'Content-type' httpd 标头。

但是,您要求提供更多练习用法示例。我有 2 条建议给你:

1:检查这段代码

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:记住 django 是 python,因此它发挥着 python 社区的力量。 django 有 2 个很棒的 RESTFul 插件。因此,如果您想了解整个兔子的深度,可以查看。

django rest framework tastypie

我建议阅读 django-rest-framework 教程,该教程将专门解决“针对不同的内容/类型采取行动”。 注意:通常的做法是将 content-type 标头用于'version' restful API's。

【讨论】:

他指的是这个吗?还是内容类型框架?:docs.djangoproject.com/en/dev/ref/contrib/contenttypes 是的,我指的是内容类型框架。我可能在传达自己方面做得不够好。无论如何,我都感谢您的回应。对于它的价值,如果这是我的问题,你会把它撞出公园=)

以上是关于Django 内容类型究竟是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章

架构师成长:当谈到架构,究竟是谈什么

br.read() 如何在读取字符串结束时给出 -1 或 br.read() 究竟是如何工作的?

Django 基础模板文件

CDN加速究竟是怎么加速的 其工作原理是怎样的

Postman API 无效的内容类型标头

!function()() 究竟是如何工作的? [复制]