假如有一个书城系统,需要给作者和书籍加上评论功能。如果给每个表单独建一个评论表,那么我们以后要扩展其它模块评论功能的时候,还需要随之新建一张评论表,会显得很冗余。对于这种情况,Django 给我们提供了解决方案,那就是 contenttypes 模块。

模型

from django.db import models
from django.contrib.contenttypes.models import ContentType class Book(models.Model):
'''
书籍表
'''
title = models.CharField(max_length=32) # 书籍标题 class Author(models.Model):
name = models.CharField(max_length=32) # 作者名称 class Comment(models.Model):
'''
评论表
'''
 content_type = models.ForeignKey(ContentType, on_delete=None) # 被评论表(对哪张表进行评论)
object_id = models.PositiveIntegerField() # 被评论表中数据的id
content = models.CharField(max_length=200) # 评论内容

上述的表结构很简单,但是注意 Comment 表中的 content_type 字段,它关联的是 django.contrib.contenttypes.models.ContentType 这张表,而这张表就是 Django 为我们提供的,初始化 DB 时就会自动生成。看一下它的数据:

可以看到,它其实就是一张描述我们所建的模型和 Django 内置模型的信息表。而我们通过 Comment 表中的 content_type 字段与之关联,就可以标识出某条评论属于哪个模型(在这里描述的就是某条评论,是对作者,还是对书籍)。再通过 object_id 字段,关联到模型的主键,就可以标识出某条评论属于哪个模型的哪条数据(在这里描述的就是某条评论,是对某个作者,还是对某本书籍)。

新增数据

初始化测试数据:

import os

if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_test.settings')
import django django.setup() from api import models
from django.contrib.contenttypes.models import ContentType models.Author.objects.create(name='鲁迅')
models.Author.objects.create(name='郭德纲')
models.Book.objects.create(title='傻子是这样炼成的')
models.Book.objects.create(title='永夜')
models.Book.objects.create(title='武则天传奇') models.Comment.objects.create(object_id=1, content_type=ContentType.objects.get(id=7), content='鲁大哥,666666') # 鲁迅
models.Comment.objects.create(object_id=1, content_type=ContentType.objects.get(id=7), content='鲁大哥 我占了二楼') # 鲁迅
models.Comment.objects.create(object_id=2, content_type=ContentType.objects.get(id=7), content='老郭没得话说。。') # 郭德纲 models.Comment.objects.create(object_id=1, content_type=ContentType.objects.get(id=8), content='这本书好神奇') # 傻子是这样炼成的
models.Comment.objects.create(object_id=2, content_type=ContentType.objects.get(id=8), content='永夜是什么夜') # 永夜
models.Comment.objects.create(object_id=3, content_type=ContentType.objects.get(id=8), content='武媚娘 武则天') # 武则天传奇

在新增评论表数据的时候我们会发现会有很繁琐的操作,例如我要给郭德纲评论,我需要先在 django_content_type 表中找到作者表,让 content_type 与之关联,再在作者表中找到郭德纲的主键,让其和 object_id 关联,最后才是评论内容。为了简化这种操作,contenttypes 给我们提供了 django.contrib.contenttypes.fields.GenericForeignKey 字段。修改 Comment 模型如下:

from django.contrib.contenttypes.fields import GenericForeignKey
from django.db import models from django.contrib.contenttypes.models import ContentType class Comment(models.Model):
'''
评论表
'''
content_type = models.ForeignKey(ContentType, on_delete=None) # 被评论表(对哪张表 进行评论)
object_id = models.PositiveIntegerField() # 被评论表中数据的id
content = models.CharField(max_length=200) # 评论内容
comment_to = GenericForeignKey('content_type', 'object_id') # 被评论对象

可以看到 Comment 模型中新增了一个 comment_to 字段,此时我们再来进行新增操作:

models.Comment.objects.create(comment_to=models.Author.objects.get(id=2), content='老郭 我又来了~!')
models.Comment.objects.create(comment_to=models.Book.objects.get(id=2), content='永远的夜晚?')

效果:

可以看到,相对于之前的新增操作,这里直接把要评论的对象赋值给 comment_to 字段,然后 contenttypes 会自动根据该对象的映射关系给 object_id 和 content_type 赋上值,不仅简化了操作,还让操作更加直观明了。

注意:这里添加的 comment_to 列仅仅用于操作,并不会在数据库生成对应列。

查询数据

正向查询

假如要查询出每条评论对应的评论对象,很显然我们需要先根据评论的 content_type 找到评论的对象类型(作者或书籍),然后从该对象类型对应的表中找到 pk 值为 object_id 值对应的那条数据。 django.contrib.contenttypes.fields.GenericForeignKey 字段除了给我们提供新增数据时的便利,查询数据的时候也是,如下:

for comment in models.Comment.objects.all():
print('{}=>{}'.format(comment.content,comment.comment_to))

效果:

可以看到每个评论的 comment_to 属性直接对应了评论的对象,是不是很给力~

反向查询

假如要查询出郭德纲(作者)和永夜(书籍)所有的评论,对于这种一对多的查询操作, contenttypes 给我们提供了另外一个字段—— django.contrib.contenttypes.fields.GenericRelation 。修改模型如下:

from django.contrib.contenttypes.fields import GenericRelation, GenericForeignKey
from django.db import models from django.contrib.contenttypes.models import ContentType class Book(models.Model):
title = models.CharField(max_length=32) # 书籍标题
comments = GenericRelation("Comment") # 所有评论 class Author(models.Model):
name = models.CharField(max_length=32) # 作者名称
comments = GenericRelation("Comment") # 所有评论 class Comment(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=None) # 被评论表(对哪张表 进行评论)
object_id = models.PositiveIntegerField() # 被评论表中数据的id
comment_to = GenericForeignKey('content_type', 'object_id') # 被评论对象
content = models.CharField(max_length=200) # 评论内容

如上,给模型 Book 和 Author 个各添加了一个 comments 字段,都对应 Comment 模型,此时我们完成上述要求的查询就很简单了,如下:

    print('作者【郭德纲】的评论:')
[print(' '+comment.content) for comment in models.Author.objects.get(name='郭德纲').comments.all()]
print('书籍【永夜】的评论:')
[print(' '+comment.content) for comment in models.Book.objects.get(title='永夜').comments.all()]

效果:

注意:这里添加的 comments 列与上面加的 comment_to 列一样,都不会在数据库中生成对应列。

python框架之Django(15)-contenttype模块的更多相关文章

  1. python框架之Django(14)-rest_framework模块

    APIView django原生View post请求 from django.shortcuts import render, HttpResponse from django import vie ...

  2. 第六篇:web之python框架之django

    python框架之django   python框架之django 本节内容 web框架 mvc和mtv模式 django流程和命令 django URL django views django te ...

  3. python框架之django

    python框架之django 本节内容 web框架 mvc和mtv模式 django流程和命令 django URL django views django temple django models ...

  4. Python框架之Django学习

    当前标签: Django   Python框架之Django学习笔记(十四) 尛鱼 2014-10-12 13:55 阅读:173 评论:0     Python框架之Django学习笔记(十三) 尛 ...

  5. Python框架之Django的相册组件

    Python框架之Django的相册组件 恩,没错,又是Django,虽然学习笔记已经结贴,但是学习笔记里都是基础的,Django的东西不管怎么说还是很多的,要学习的东西自然不会仅仅用十几篇博文就能学 ...

  6. Python框架之Django学习笔记(十一)

    话说上次说到数据库的基本访问,而数据库我们主要进行的操作就是CRUD,也即是做计算处理时的增加(Create).读取(Retrieve)(重新得到数据).更新(Update)和删除(Delete),俗 ...

  7. python框架之Django(12)-认证系统之auth模块

    我们在开发一个网站的时候,无可避免的需要设计实现网站的用户系统.此时我们需要实现包括用户注册.用户登录.用户认证.注销.修改密码等功能,这还真是个麻烦的事情呢. Django作为一个完美主义者的终极框 ...

  8. Python高级进阶(一)Python框架之Django入门

    传说中的Django Django由来 Django是一个开放源代码的Web应用框架,由Python写成.采用了MVC的框架模式,即模型M,视图V和控制器C.它最初是被开发来用于管理劳伦斯出版集团旗下 ...

  9. python框架之Django(1)-第一个Django项目

    准备 自己写一个简单的webServer import socket # 生成socket实例对象 sk = socket.socket() # 绑定IP和端口 sk.bind(("127. ...

随机推荐

  1. Android Studio添加原生库并自动构建

    [时间:2017-09] [状态:Open] [关键词:Android,Android Studio,gradle,native,c,c++,cmake,原生开发,ndk-build] 0 引言 最近 ...

  2. 【iCore4 双核心板_uC/OS-II】例程九:消息队列

    一.实验说明: 前面介绍通过信息传递可以进行任务间的交流,信息也可以直接发送给一个任务,在uC/OS-II中每一个任务在它们内部都有一个消息队列,也即任务消息队列,用户可以直接给一个任务发送消息,不需 ...

  3. sql server中的用户临时表和全局临时表的区别

    临时表分为: 本地临时表,仅限于当前访问者访问,创建方法去如下:create table #TableName(表结构)储存于数据库tempdb内(硬盘),当前用户断开连接(把当前的),自动删除如果使 ...

  4. GRE封装解封装过程

    GRE(Generic Routing Encapsulation,通用路由封装)协议是对某些网络层协议(IPX, AppleTalk, IP,etc.)的数据报文进行封装,使这些被封装的数据报文能够 ...

  5. elephant-bird学习笔记

    elephant-bird是Twitter的开源项目,项目的地址为 https://github.com/twitter/elephant-bird 该项目是Twitter为LZO,thrift,pr ...

  6. Mixed Content: xxx This request has been blocked; the content must be served over HTTPS.

    在升级https的过程中,出现如下问题: Mixed Content: The page at 'https://www.xxx.com/denglu.html' was loaded over HT ...

  7. 【Excel】输出CSV文本

    '******************************************************************************* ' CSV形式テキストファイル書き出す ...

  8. Android深入源代码分析理解Aidl总体调用流程(雷惊风)

    2017年開始上班的第一天.老不想工作了,假期感觉还没開始就已经结束了,唉,时间就是这样,新的一年開始了,尽管非常不想干正事,没办法,必须干起来.由于后边的路还非常长,距离六十岁还非常远. 刚上班也没 ...

  9. 安装Oracle Database 11g 找不到文件“WFMLRSVCApp.ear” .

    在64位Windows 7 系统下安装Oracle Database 11g 的过程中,出现提示:“未找到文件D:\app\Administrator\product\11.2.0\dbhome_1\ ...

  10. git使用git-credential-winstore保存https访问密码

    使用 https 方式 clone 一个 git 仓库,每次pull 或者 push 的时候都需要输入用户名和密码. 访问远程Git仓库可以用 SSH 方式和 https 方式,https 每次访问时 ...