Django ContentTypes框架详解及使用场景介绍

Django contenttypes是一个非常有用的框架,主要用来创建模型间的通用关系(generic relation)。不过由于其非常抽象,理解起来并不容易。当你创建一个django项目的时候,可以看到在默认的INSTALL_APPS已经包含了django.contrib.contenttypes。今天我们来重点讲下它的使用场景及如何使用django contenttypes。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

本文参考了StackOverflow上著名问题及DjangoContentTypes详解

How exactly do Django content types work?

https://blog.csdn.net/laughing2333/article/details/53014267


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模型如下图所示:

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

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。

猜你喜欢

转载自blog.csdn.net/weixin_42134789/article/details/80690182
今日推荐