Django contenttypes是一个非常有用的框架,主要用来创建模型间的通用关系(generic relation)。不过由于其非常抽象,
理解起来并不容易。当你创建一个django项目的时候,可以看到在默认的INSTALL_APPS已经包含了django.contrib.contenttypes。
今天我们来重点讲下它的使用场景及如何使用django contenttypes。
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config',
]
Django ContentTypes框架使用场景
假设我们创建了如下模型,里面包含文章Post,Picture和评论Comment模型。
Comment可以是对Post的评论,也可以是对Picture的评论。
如果你还想对其它对象(比如回答,用户) 进行评论, 这样你将需要在comment对象里添加非常多的ForeignKey。
你的直觉会告诉你,这样做很傻,会造成代码重复和字段浪费。一个更好的方式是,只有当你需要对某个对象或模型进行评论时,
才创建comment与那个模型的关系。这时你就需要使用django contenttypes了。
from django.db import models from django.contrib.auth.models import User # Create your models here. class Post(models.Model): author = models.ForeignKey(User) title = models.CharField(max_length=75) 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, null=True) picture = models.ForeignKey(Picture, null=True)
Django ContentType提供了一种GenericForeignKey的类型,通过这种类型可以指定content_object。修改过的comment模型如下图所示:
import ContentType import GenericForeignKey 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(content_type,object_id)
comment里加了3个字段:
-
content_type: 内容类型,代表了模型的名字(比如Post, Picture)
-
object_id: 传入对象的id
-
content_object: 传入的实例化对象,其包含两个属性content_type和object_id。
当你需要对某篇文章或某个图片进行评论时(建立评论关系),
你只需要将实例化的对象user, post或picture传入comment。
这样实现了按需建立评论关系。首先你先需要实例化对象。
user = User.objects.create_user(username='user1', password='2333') post = Post.objects.create(author=user, title='title1', body='') picture = Picture.objects.create(author=user, image="default.png", caption='picture1')
然后在views或者shell里,你可以按如下代码建立评论关系。
from foreign.models import Post, Picture, Common >>> from django.contrib.auth.models import User
>>> user = User.objects.get(username='user1') >>> post = Post.objects.get(title='title1')
>>> c = Comment.objects.create(author=user, body='', content_object=post) >>> picture = Picture.objects.get(caption='picuture1') >>> c1 = Comment.objects.create(author=user, body='', content_object=picture)
然而上述创建评论的方式我们并不推荐。
我们更希望直接从模型中获取或创建comment,
我们只需在模型中创建一个与Comment的GenericRelation即可。
注意该字段不会存储于数据库中。
from django.contrib.contenttypes.fields import GenericRelation class Post(models.Model): author = models.ForeignKey(User) title = models.CharField(max_length=75) 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')
>>> me = User.objects.get(username='myusername') >>> pic = Picture.objects.get(author=me) >>> pic.comments.create(author=me, body="Man, I'm cool!") >>> pic.comments.all()[<Comment: "Man, I'm cool!">]
值得注意的是,如果在Post中定义了GenericRelation,删除了一个post实例,
在Comment中所有与post相关实例也会被删除。GenericForeignKey不支持设置on_delete参数。
因此,如果对级联删除不满意的话就不要设置GenericRelation。