Django contenttypes介绍及基本使用
承蒙大家厚爱,我的《Go语言之路》的纸质版图书已经上架京东,有需要的朋友请点击 此链接 购买。
本文介绍了Django框架中的contenttypes
及其基本使用。
Django contenttypes framework
简介
Django包含一个contenttypes应用程序(app),可以跟踪Django项目中安装的所有模型(Model),提供用于处理模型的高级通用接口。
概述
Contenttypes应用的核心是ContentType模型,位于django.contrib.contenttypes.models.ContentType
。 ContentType的实例表示并保存项目中安装的模型的信息,每当有新的模型时会自动创建新的ContentType实例。
只要使用django-admin startproject
命令创建的Django项目(PyCharm创建Django项目同理),默认都会在settings.py
的INSTALLED_APPS
列表中安装好django.contrib.contenttypes
。
我们执行了数据迁移命令之后,会自动在数据库中创建一个名为django_content_type
的表。
表结构如下图所示:
其中,app_label
字段存储了APP的名称,model字段存储了APP下的具体的模型类的名称。
contenttypes的应用场景
比如我们现在要实现一个开放的论坛,用户可以在论坛里发布帖子或图片,用户当然还可以对帖子发表评论,或者对图片发表评论。这个时候我们可能会想到按如下方式去设计表结构:
from django.db import models
# 这里使用auth模块的用户表(使用其他用户表同理)
from django.contrib.auth.models import User
class Post(models.Model):
"""帖子表"""
author = models.ForeignKey(User)
title = models.CharField(max_length=72)
class Picture(models.Model):
"""图片表"""
author = models.ForeignKey(User)
image = models.ImageField()
class Comment(models.Model):
"""评论表"""
author = models.ForeignKey(User)
content = models.TextField()
post = models.ForeignKey(Post, null=True, blank=True, on_delete=models.CASCADE)
picture = models.ForeignKey(Picture, null=True, blank=True, on_delete=models.CASCADE)
再来个数据示例图:
上面的表结构能够实现我们的需求,但是如果以后我们还需要对问答、用户等模块的评论功能时,我们就需要对我们的Comment
表进行修改,这个时候就会在Comment
表上添加很多的外键字段(这些外键字段通常只有一个有值,其他都是空的)。
这种方式很显然不合适,现在有一个更好的方式是我们可以按照如下方式设计:
我们再优化一下:
这个时候我们就用上了前面讲到的contenttypes
,借助contenttypes
我们就能够在创建Comment
的时候再决定和Post
关联还是和Picture
关联。
在models.py
中使用django.contrib.contenttypes
中提供的特殊字段GenericForeignKey
来实现:
class Comment(models.Model):
"""评论表"""
author = models.ForeignKey(User)
content = models.TextField()
content_type = models.ForeignKey(ContentType) # 外键关联django_content_type表
object_id = models.PositiveIntegerField() # 关联数据的主键
content_object = GenericForeignKey('content_type', 'object_id')
看一下表结构:
contenttypes使用
import os
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "about_contenttype.settings")
import django
django.setup()
from app01.models import Post, Picture, Comment
from django.contrib.auth.models import User
# 准备测试数据
user_1 = User.objects.create_user(username='aaa', password='123')
user_2 = User.objects.create_user(username='bbb', password='123')
user_3 = User.objects.create_user(username='ccc', password='123')
post_1 = Post.objects.create(author=user_1, title='Python入门教程')
post_2 = Post.objects.create(author=user_2, title='Python进阶教程')
post_3 = Post.objects.create(author=user_1, title='Python入土教程')
picture_1 = Picture.objects.create(author=user_1, image='小姐姐01.jpg')
picture_2 = Picture.objects.create(author=user_1, image='小姐姐02.jpg')
picture_3 = Picture.objects.create(author=user_3, image='小哥哥01.jpg')
# 给帖子创建评论数据
comment_1 = Comment.objects.create(author=user_1, content='好文!', content_object=post_1)
# 给图片创建评论数据
comment_2 = Comment.objects.create(author=user_2, content='好美!', content_object=picture_1)
接下来如果我们想要查看某篇帖子或者某个照片的所有评论,这个时候就可以用上另外一个工具–GenericRelation
了。
from django.contrib.contenttypes.fields import GenericRelation
修改models.py
中的Post
和Picture
,添加用于反向查询的comments
字段:
class Post(models.Model):
"""帖子表"""
author = models.ForeignKey(User)
title = models.CharField(max_length=72)
comments = GenericRelation('Comment') # 支持反向查找评论数据(不会在数据库中创建字段)
class Picture(models.Model):
"""图片表"""
author = models.ForeignKey(User)
image = models.ImageField()
comments = GenericRelation('Comment') # 支持反向查找评论数据(不会在数据库中创建字段)
查询示例:
post_1 = Post.objects.filter(id=1).first()
comment_list = post_1.comments.all()