Django——ContentType(与多个表建立外键关系)及ContentType-sig
<div id="cnblogs_post_body" class="blogpost-body"> 一、ContentType在django中,有一个记录了项目中所有model元数据的表,就是ContentType,表中一条记录对应着一个存在的model,所以可以通过一个ContentType表的id和一个具体表中的id找到任何记录,及先通过ContenType表的id可以得到某个model,再通过model的id得到具体的对象。 ![]() = models.CharField(max_length=100= models.CharField(_(),max_length=100=
</span><span style="color: #0000ff;">class</span><span style="color: #000000;"> Meta:
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._meta.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;">kwargs):
</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;">kwargs)
</span><span style="color: #0000ff;">def</span> get_all_objects_for_this_type(self,**<span style="color: #000000;">kwargs):
</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;">kwargs)
</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视图中,来这样玩玩: = models.ContentType.objects.get(id=10
HttpResponse()
可以看到,我们。也就是说,今后,我们如果自己定义model如果有外键关联到這个ContentType上,我们就能找到对应的model名称。 二、Django-ContentType-signalsdjango的signal结合contenttypes可以实现好友最新动态,新鲜事,消息通知等功能。总体来说这个功能就是在用户发生某个动作的时候将其记录下来或者附加某些操作,比如通知好友。要实现这种功能可以在动作发生的代码里实现也可以通过数据库触发器等实现,但在django中,一个很简单的方法的就是使用signals。 当django保存一个object的时候会发出一系列的signals,可以通过对这些signals注册listener,从而在相应的signals发出时执行一定的代码。 想要记录下每个操作,同时还能追踪到这个操作的具体动作。 对于新鲜事这个功能来说就是使用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: django.db django.contrib.auth.models django.contrib.contenttypes django.db.models <span style="color: #0000ff;">class<span style="color: #000000;"> Post(models.Model):
author =<span style="color: #000000;"> models.ForeignKey(User) title = models.CharField(max_length=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;">',auto_now_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)
<span style="color: #0000ff;">class<span style="color: #000000;"> Event(models.Model):
<span style="color: #0000ff;">def post_post_save(sender,instance,signal,*args,**<span style="color: #000000;">kwargs): signals.post_save.connect(post_post_save,sender=Post) 只要model中有object的保存操作,都将执行post_post_save函数,故可以在这个接受函数中实现通知好友等功能。 前面说到django在保存一个object的时候会发出一系列signals,在这里我们所监听的是signals.post_save这个signal,这个signal是在django保存完一个对象后发出的,django中已定义好得一些signal,在django/db/models/signal.py中可以查看,同时也可以自定义信号。 利用connect这个函数来注册监听器, connect原型为: 通过这个字段可以得到与某篇post相关联的所有事件,最重要的一点是如果没有这个字段,那么当删除一篇post的时候,与该post关联的事件是不会自动删除的。反之有这个字段就会进行自动的级联删除 三、ContentType其他案例总结案例一、调查问卷表设计例如:设计如下类型的调查问卷表:问卷类型包括(打分,建议,选项),先来看看一个简单的问答,
对于上面一个类型的问答,我们可以知道,一个问卷系统主要包括:问卷,问卷中每个题目,每个题目的答案,以及生成问卷记录。常规设计表如下: ![]() django.db django.contrib.contenttypes.fields django.contrib.contenttypes.models = models.CharField(verbose_name=,max_length=128,unique== models.ForeignKey(verbose_name=,to== models.DateTimeField(verbose_name=,auto_now_add== models.ForeignKey(verbose_name=,to=<span style="color: #0000ff;">class<span style="color: #000000;"> SurveryItem(models.Model):
<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;">",max_length=255<span style="color: #000000;">) date = models.DateField(auto_now_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):
但是,如果我有另外一个需求,也需要与SurveryRecord建立外键关系,那么此时应该怎么做呢?是再给上面的表增加一个外键,然后重新修改数据库么?显然是不能,一旦数据库被创建了,我们几乎很少再去修改数据,如果再给其添加额外字段,无疑会带来不必要的麻烦。为此,我们可以利用Django自带的ContentType类,来做这件事情。 下面来看看经过修改以后的model: ![]() django.db django.contrib.contenttypes.fields = models.IntegerField(verbose_name== GenericRelation(<span style="color: #0000ff;">class<span style="color: #000000;"> Score(models.Model):
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;">) points = models.IntegerField(verbose_name=<span style="color: #800000;">'<span style="color: #800000;">分值<span style="color: #800000;">'<span style="color: #000000;">) surveryrecord = GenericRelation(<span style="color: #800000;">"<span style="color: #800000;">SurveryRecord<span style="color: #800000;">"<span style="color: #000000;">) <span style="color: #0000ff;">class<span style="color: #000000;"> Suggestion(models.Model): <span style="color: #0000ff;">class<span style="color: #000000;"> SurveryRecord(models.Model):
<span style="color: #000000;"> 案例二、优惠券系统设计应用场景:某一在线教育网,需要为每位积极客户发一些观看视频的优惠券,但是,对于不同类型的视频,优惠券是不同。比如:有一个普通课程,需要发一些满200减30的优惠券,而又有精品课程,需要发满100键70的优惠券。根据以上需求,我们很快就知道,需要三张表,学位课程表,课程表以及优惠券表,那么,这三张表又是如何关联的呢? 正常情况我们会想到下面這种方式: <span style="color: #008000;"># <span style="color: #008000;">B普通课程表<span style="color: #008000;"> <span style="color: #008000;">ID 名称<span style="color: #008000;"> <span style="color: #008000;">1 普通课1<span style="color: #008000;"> <span style="color: #008000;">2 普通课2<span style="color: #008000;">#<span style="color: #008000;">优惠券表<span style="color: #008000;"> <span style="color: #008000;">ID 优惠券名称 A(FK) B(FK)<span style="color: #008000;"><span style="color: #008000;">1 通用优惠券 null null # 两个都为空,说明全场都可以使用<span style="color: #008000;"><span style="color: #008000;">2 满100-10 1 null # 给学位课程创建优惠券<span style="color: #008000;"><span style="color: #008000;">3 满200-30 null 1 # 给普通课程创建优惠券但是这样一来,如果再来一种课程,上面的优惠券表还需要额外新增一列,为了解决這个问题,我们可以使用ContentType类来实现上述需求。 ![]() django.db django.contrib.contenttypes.fields django.contrib.contenttypes.models <span style="color: #008000;">#<span style="color: #008000;"> Create your models here.
<span style="color: #0000ff;">class <span style="color: #000000;"> DegreeCourse(models.Model):<span style="color: #800000;">"""<span style="color: #800000;">学位课程 ID 名称 1 学位课1 2 学位课2
<span style="color: #0000ff;">class<span style="color: #000000;"> Course(models.Model):<span style="color: #800000;">"""<span style="color: #800000;">课程 ID 名称 1 普通课1 2 普通课2 <span style="color: #800000;">"""<span style="color: #000000;"> name = models.CharField(max_length=128,unique=<span style="color: #000000;">True) <span style="color: #0000ff;">class<span style="color: #000000;"> Coupon(models.Model):
根据ContentType字段查询关联字段操作![]() django.shortcuts django.contrib.contenttypes.models
content </span>= ContentType.objects.get(app_label=<span style="color: #800000;">'</span><span style="color: #800000;">app01</span><span style="color: #800000;">'</span>,model=<span style="color: #800000;">'</span><span style="color: #800000;">coupon</span><span style="color: #800000;">'</span><span style="color: #000000;">)
model_class </span>=<span style="color: #000000;"> content.model_class()
</span><span style="color: #0000ff;">print</span>(content) <span style="color: #008000;">#</span><span style="color: #008000;"> coupon</span>
<span style="color: #0000ff;">print</span>(model_class) <span style="color: #008000;">#</span><span style="color: #008000;"> <class 'app01.models.Coupon'></span>
<span style="color: #0000ff;">print</span>(model_class.objects.all()) <span style="color: #008000;">#</span><span style="color: #008000;"><QuerySet [<Coupon: 通用>,<Coupon: 满100-10>,<Coupon: 满200-30>,<Coupon: 满200-30>]></span>
<span style="color: #008000;">#</span><span style="color: #008000;"> 给学位课1或普通课创建优惠券</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> d1 = models.DegreeCourse.objects.get(id=1)</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> models.Coupon.objects.create(name='优惠券',brief='2000-30',content_object=d1)</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> d1 = models.Course.objects.get(id=1)</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> models.Coupon.objects.create(name='优惠券',brief='100-90',content_object=d1)</span>
<span style="color: #008000;">#</span><span style="color: #008000;">或 models.Coupon.objects.create(name='优惠券',content_object_id=1)</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> 当前优惠券,绑定的课程?</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> obj = models.Coupon.objects.get(id=2)</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> print(obj.content_object) #普通课1</span>
<span style="color: #800000;">'''</span><span style="color: #800000;">通过content_object直接找到与其关联的models对象</span><span style="color: #800000;">'''</span>
<span style="color: #008000;">#</span><span style="color: #008000;">正向查找:models对象.content_object得到的是models对象</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> 当前课程,都有哪些优惠券?</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> obj = models.DegreeCourse.objects.get(id=1)</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> print(obj.x1.all()) # [<Coupon: 满200-30>]></span>
<span style="color: #008000;">#</span><span style="color: #008000;"> v = models.DegreeCourse.objects.values('name','x1__brief')</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> print(v) # <QuerySet [{'name': '学位课1','x1__brief': ''},{'name': '学位课2','x1__brief': None}]></span>
<span style="color: #008000;">#</span><span style="color: #008000;"> 反向查找:models对象.反向关联字段.all() 得到的是QuerySet对象</span>
<span style="color: #0000ff;">return</span> HttpResponse(<span style="color: #800000;">'</span><span style="color: #800000;">...</span><span style="color: #800000;">'</span>)</pre>
总之,如果一个表与其他表有多个外键关系,我们可以通过ContentType来解决这种关联。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |