<div id="cnblogs_post_body" class="blogpost-body">
一、ContentType
在django中,有一个记录了项目中所有model元数据的表,就是ContentType,表中一条记录对应着一个存在的model,所以可以通过一个ContentType表的id和一个具体表中的id找到任何记录,及先通过ContenType表的id可以得到某个model,再通过model的id得到具体的对象。

</span><span style="color: #0000ff;">class</span><span style="color: #000000;"> <a href="/tag/Meta/" target="_blank" class="keywords">Meta</a>:
verbose_name </span>= _(<span style="color: #800000;">'</span><span style="color: #800000;">content type</span><span style="color: #800000;">'</span><span style="color: #000000;">)
verbose_name_plural </span>= _(<span style="color: #800000;">'</span><span style="color: #800000;">content types</span><span style="color: #800000;">'</span><span style="color: #000000;">)
db_table </span>= <span style="color: #800000;">'</span><span style="color: #800000;">django_content_type</span><span style="color: #800000;">'</span><span style="color: #000000;">
unique_together </span>= ((<span style="color: #800000;">'</span><span style="color: #800000;">app_label</span><span style="color: #800000;">'</span>,<span style="color: #800000;">'</span><span style="color: #800000;">model</span><span style="color: #800000;">'</span><span style="color: #000000;">),)
</span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__str__</span><span style="color: #000000;">(self):
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> self.name
@property
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> name(self):
model </span>=<span style="color: #000000;"> self.model_class()
</span><span style="color: #0000ff;">if</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> model:
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> self.model
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> force_text(model._<a href="/tag/Meta/" target="_blank" class="keywords">Meta</a>.verbose_name)
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> model_class(self):
</span><span style="color: #800000;">"</span><span style="color: #800000;">Returns the Python model class for this type of content.</span><span style="color: #800000;">"</span>
<span style="color: #0000ff;">try</span><span style="color: #000000;">:
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> apps.get_model(self.app_label,self.model)
</span><span style="color: #0000ff;">except</span><span style="color: #000000;"> LookupError:
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> None
</span><span style="color: #0000ff;">def</span> get_object_for_this_type(self,**<span style="color: #000000;"><a href="/tag/kwargs/" target="_blank" class="keywords">kwargs</a>):
</span><span style="color: #800000;">"""</span><span style="color: #800000;">
Returns an object of this type for the keyword arguments given.
Basically,this is a proxy around this object_type's get_object() model
method. The ObjectNotExist exception,if thrown,will not be caught,so code that calls this method should catch it.
</span><span style="color: #800000;">"""</span>
<span style="color: #0000ff;">return</span> self.model_class()._base_manager.using(self._state.db).get(**<span style="color: #000000;"><a href="/tag/kwargs/" target="_blank" class="keywords">kwargs</a>)
</span><span style="color: #0000ff;">def</span> get_all_objects_for_this_type(self,**<span style="color: #000000;"><a href="/tag/kwargs/" target="_blank" class="keywords">kwargs</a>):
</span><span style="color: #800000;">"""</span><span style="color: #800000;">
Returns all objects of this type for the keyword arguments given.
</span><span style="color: #800000;">"""</span>
<span style="color: #0000ff;">return</span> self.model_class()._base_manager.using(self._state.db).filter(**<span style="color: #000000;"><a href="/tag/kwargs/" target="_blank" class="keywords">kwargs</a>)
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> natural_key(self):
</span><span style="color: #0000ff;">return</span> (self.app_label,self.model)</pre>
這个类主要作用是记录每个app中的model。例如,我们在自己的app中创建了如下几个model:post,event。迁移之后,我们来查看一下ContentType這个数据表中生成的数据:
如上图,生成了app与model的对应关系。那么,這个主要有什么用呢?
我们在View视图中,来这样玩玩:
可以看到,我们获取对应的类。也就是说,今后,我们如果自己定义model如果有外键关联到這个ContentType上,我们就能找到对应的model名称。
二、Django-ContentType-signals
django的signal结合contenttypes可以实现好友最新动态,新鲜事,消息通知等功能。总体来说这个功能就是在用户发生某个动作的时候将其记录下来或者附加某些操作,比如通知好友。要实现这种功能可以在动作发生的代码里实现也可以通过数据库触发器等实现,但在django中,一个很简单的方法的就是使用signals。
当django保存一个object的时候会发出一系列的signals,可以通过对这些signals注册listener,从而在相应的signals发出时执行一定的代码。
使用signals来监听用户的动作有很多好处,1、不管这个动作是发生在什么页面,甚至在很多页面都可以发生这个动作,都只需要写一次代码来监听保存object这个动作就可以了。2、可以完全不修改原来的代码就可以添加监听signals的功能。3、你几乎可以在signals监听代码里写任何代码,包括做一些判断是不是第一次发生此动作还是一个修改行为等等。
想要记录下每个操作,同时还能追踪到这个操作的具体动作。
*首先用信号机制,监听信号,实现对信号的响应函数,在响应函数中记录发生的动作(记录在一张记录表,相当于下文的Event)。
*其次就是为了能追踪到操作的具体动作,必须从这张表中得到相应操作的model,这就得用到上面说的ContentType。
对于新鲜事这个功能来说就是使用GenericRelation来产生一个特殊的外键,它不像models.ForeignKey那样,必须指定一个Model来作为它指向的对象。GenericRelation可以指向任何Model对象,有点像C语言中 void* 指针。
这样关于保存用户所产生的这个动作,比如用户写了一片日志,我们就可以使用Generic relations来指向某个Model实例比如Post,而那个Post实例才真正保存着关于用户动作的完整信息,即Post实例本身就是保存动作信息最好的地方。这样我们就可以通过存取Post实例里面的字段来描述用户的那个动作了,需要什么信息就往那里面去取。而且使用Generic relations的另外一个好处就是在删除了Post实例后,相应的新鲜事实例也会自动删除。
怎么从这张操作记录表中得到相应操作的model呢,这就得用到fields.GenericForeignKey,它是一个特殊的外键,可以指向任何Model的实例,在这里就可以通过这个字段来指向类似Post这样保存着用户动作信息的Model实例。
先来看看model:
author =<span style="color: #000000;"> models.ForeignKey(User)
title = models.CharField(maxlength=255<span style="color: #000000;">)
content =<span style="color: #000000;"> models.TextField()
created = models.DateTimeField(u<span style="color: #800000;">'<span style="color: #800000;">发表时间<span style="color: #800000;">',autoNow_add=<span style="color: #000000;">True)
updated = models.DateTimeField(u<span style="color: #800000;">'<span style="color: #800000;">最后修改时间<span style="color: #800000;">',auto_Now=<span style="color: #000000;">True)
events </span>= fields.GenericRelation(<span style="color: #800000;">'</span><span style="color: #800000;">Event</span><span style="color: #800000;">'</span><span style="color: #000000;">)
</span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__str__</span><span style="color: #000000;">(self):
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> self.title
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> description(self):
</span><span style="color: #0000ff;">return</span> u<span style="color: #800000;">'</span><span style="color: #800000;"><a href="/tag/s/" target="_blank" class="keywords">%s</a> 发表了日志《<a href="/tag/s/" target="_blank" class="keywords">%s</a>》</span><span style="color: #800000;">'</span> %<span style="color: #000000;"> (self.author,self.title)
<span style="color: #0000ff;">class<span style="color: #000000;"> Event(models.Model):
user =<span style="color: #000000;"> models.ForeignKey(User)
content_type =<span style="color: #000000;"> models.ForeignKey(ContentType)
object_id =<span style="color: #000000;"> models.PositiveIntegerField()
content_object</span>= fields.GenericForeignKey(<span style="color: #800000;">'</span><span style="color: #800000;">content_type</span><span style="color: #800000;">'</span>,<span style="color: #800000;">'</span><span style="color: #800000;">object_id</span><span style="color: #800000;">'</span><span style="color: #000000;">)
created </span>= models.DateTimeField(u<span style="color: #800000;">'</span><span style="color: #800000;">事件发生时间</span><span style="color: #800000;">'</span>,auto_<a href="/tag/Now/" target="_blank" class="keywords">Now</a>_add=<span style="color: #000000;">True)
</span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__str__</span><span style="color: #000000;">(self):
</span><span style="color: #0000ff;">return</span> <span style="color: #800000;">"</span><span style="color: #800000;"><a href="/tag/s/" target="_blank" class="keywords">%s</a>的事件: <a href="/tag/s/" target="_blank" class="keywords">%s</a></span><span style="color: #800000;">"</span> %<span style="color: #000000;"> (self.user,self.description())
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> description(self):
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> self.content_object.description()
<span style="color: #0000ff;">def post_post_save(sender,instance,signal,*args,**<span style="color: #000000;">kwargs):
<span style="color: #800000;">"""<span style="color: #800000;">
:param sender:监测的类:Post类
:param instance: 监测的类:Post类
:param signal: 信号类
:param args:
:param kwargs:
:return:
<span style="color: #800000;">"""<span style="color: #000000;">
post =<span style="color: #000000;"> instance
event = Event(user=post.author,content_object=<span style="color: #000000;">post)
event.save()
signals.post_save.connect(post_post_save,sender=Post)
#signals.post_save.connect(post_post_sace,sender=Book)可以监听多个类
只要model中有object的保存操作,都将执行post_post_save函数,故可以在这个接受函数中实现通知好友等功能。
前面说到django在保存一个object的时候会发出一系列signals,在这里我们所监听的是signals.post_save这个signal,这个signal是在django保存完一个对象后发出的,django中已定义好得一些signal,在django/db/models/signal.py中可以查看,同时也可以自定义信号。 利用connect这个函数来注册监听器, connect原型为:
def connect(self,receiver,sender=None,weak=True,dispatch_uid=None):
第一个参数是要执行的函数,第二个参数是指定发送信号的Class,这里指定为Post这个Model,对其他Model所发出的signal并不会执行注册的函数。
instance这个参数,即刚刚保存完的Model对象实例。创建事件的时候看到可以将post这个instance直接赋给generic.GenericForeignKey类型的字段,从而event实例就可以通过它来获取事件的真正信息了。
最后有一点需要的注意的是,Post的Model定义里现在多了一个字段:
content_object= GenericRelation(‘Event’)
通过这个字段可以得到与某篇post相关联的所有事件,最重要的一点是如果没有这个字段,那么当删除一篇post的时候,与该post关联的事件是不会自动删除的。反之有这个字段就会进行自动的级联删除
三、ContentType其他案例总结
案例一、调查问卷表设计
例如:设计如下类型的调查问卷表:问卷类型包括(打分,建议,选项),先来看看一个简单的问答,
- 您最喜欢吃什么水果?
- A.苹果 B.香蕉 C.梨子 D.橘子
对于上面一个类型的问答,我们可以知道,一个问卷系统主要包括:问卷,问卷中每个题目,每个题目的答案,以及生成问卷记录。常规设计表如下:

<span style="color: #800000;">"""<span style="color: #800000;">
问卷题目
ID survery name date answer_type
1 1(代表上面创建的第一次班级调查) 您最喜欢吃什么水果? xxx-xxx-xx 1
1 1(代表上面创建的第一次班级调查) 您最喜欢什么玩具? xxx-xxx-xx 2
1 1(代表上面创建的第一次班级调查) 您最喜欢什么英雄人物? xxx-xxx-xx 3
<span style="color: #800000;">"""<span style="color: #000000;">
survery = models.ForeignKey(verbose_name=<span style="color: #800000;">'<span style="color: #800000;">问卷<span style="color: #800000;">',to=<span style="color: #800000;">'<span style="color: #800000;">Survery<span style="color: #800000;">'<span style="color: #000000;">)
name = models.CharField(verbose_name=<span style="color: #800000;">"<span style="color: #800000;">调查问题<span style="color: #800000;">",maxlength=255<span style="color: #000000;">)
date = models.DateField(autoNow_add=<span style="color: #000000;">True)
answer_type_choices =<span style="color: #000000;"> (
(1,<span style="color: #800000;">"<span style="color: #800000;">打分(1~10分)<span style="color: #800000;">"<span style="color: #000000;">),(2,<span style="color: #800000;">"<span style="color: #800000;">单选<span style="color: #800000;">"<span style="color: #000000;">),(3,<span style="color: #800000;">"<span style="color: #800000;">建议<span style="color: #800000;">"<span style="color: #000000;">),)
answer_type = models.IntegerField(verbose_name=<span style="color: #800000;">"<span style="color: #800000;">问题类型<span style="color: #800000;">",choices=answer_type_choices,default=1<span style="color: #000000;">) <span style="color: #0000ff;">class<span style="color: #000000;"> SurveryChoices(models.Model):
<span style="color: #800000;">"""<span style="color: #800000;">
问卷选项答案(针对选项类型)
ID item content points
1 2 A 10分
1 2 B 9分
1 2 C 8分
1 2 D 7分
<span style="color: #800000;">"""<span style="color: #000000;">
item = models.ForeignKey(verbose_name=<span style="color: #800000;">'<span style="color: #800000;">问题<span style="color: #800000;">',to=<span style="color: #800000;">'<span style="color: #800000;">SurveryItem<span style="color: #800000;">'<span style="color: #000000;">)
content = models.CharField(verbose_name=<span style="color: #800000;">'<span style="color: #800000;">内容<span style="color: #800000;">',max_length=256<span style="color: #000000;">)
points = models.IntegerField(verbose_name=<span style="color: #800000;">'<span style="color: #800000;">分值<span style="color: #800000;">'<span style="color: #000000;">)
<span style="color: #0000ff;">class<span style="color: #000000;"> SurveryRecord(models.Model):
<span style="color: #800000;">"""<span style="color: #800000;">
问卷记录
ID survery student_name survery_item score single suggestion date
1 1 1 1 10分 null null xxxxx
1 1 1 2 null A null xxxxx
1 1 1 3 null null XXXXX xxxxx
<span style="color: #800000;">"""<span style="color: #000000;">
survery = models.ForeignKey(Survery,verbose_name=<span style="color: #800000;">"<span style="color: #800000;">问卷<span style="color: #800000;">"<span style="color: #000000;">)
student_name = models.ForeignKey(verbose_name=<span style="color: #800000;">"<span style="color: #800000;">学员姓名<span style="color: #800000;">",to=<span style="color: #800000;">"<span style="color: #800000;">Student<span style="color: #800000;">"<span style="color: #000000;">)
survery_item = models.ForeignKey(verbose_name=<span style="color: #800000;">"<span style="color: #800000;">调查项<span style="color: #800000;">",to=<span style="color: #800000;">'<span style="color: #800000;">SurveryItem<span style="color: #800000;">'<span style="color: #000000;">)
<a href="/tag/score/" target="_blank" class="keywords">score</a> </span>= models.IntegerField(verbose_name=<span style="color: #800000;">"</span><span style="color: #800000;">评分</span><span style="color: #800000;">"</span>,blank=True,null=<span style="color: #000000;">True)
single </span>= models.ForeignKey(verbose_name=<span style="color: #800000;">'</span><span style="color: #800000;">单选</span><span style="color: #800000;">'</span>,to=<span style="color: #800000;">'</span><span style="color: #800000;">SurveryChoices</span><span style="color: #800000;">'</span>,null=<span style="color: #000000;">True)
suggestion </span>= models.TextField(verbose_name=<span style="color: #800000;">"</span><span style="color: #800000;">建议</span><span style="color: #800000;">"</span>,max_length=1024,null=<span style="color: #000000;">True)
date </span>= models.DateTimeField(verbose_name=<span style="color: #800000;">"</span><span style="color: #800000;">答题日期</span><span style="color: #800000;">"</span>,auto_<a href="/tag/Now/" target="_blank" class="keywords">Now</a>_add=True)</pre>