Django的ContentType应用

ContentType实例提供的接口

1.ContentType.model_class() :
获取当前ContentType类型所代表的模型类

2.ContentType.get_object_for_this_type() :
使用当前ContentType类型所代表的模型类做一次get查询

ContentType管理器(manager)提供的接口

1.ContentType.objects.get_for_id() :
通过id寻找ContentType类型,这个跟传统的get方法的区别就是它跟get_for_model共享一个缓存,因此更为推荐。

2.ContentType.objects.get_for_model() :
通过model或者model的实例来寻找ContentType类型

Django ContentTypes的使用场景

ContentType的通用类型:

from django.db import models
from django.contrib.auth.models import User

class Post(models.Model): 
        author = models.ForeignKey(User)
        title = models.CharField(max_length=75)
        slug = models.SlugField(unique=True)
        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)

(Comment是分别和Picture或者Post中其中一个对应即可,一个Comment并不既需要Post又需要Picture才能建立)

通过Contenttype框架对以上代码进行改进,ContentType提供了一种GenericForeignKey的类型,通过这种类型可以实现在Comment对其余所有model的外键关系:

class Comment(models.Model):
        author = models.ForeignKey(User)    
        body = models.TextField(blank=True)    
        content_type = models.ForeignKey(ContentType)    
        object_id = models.PositiveIntegerField()    
        content_object = fields.GenericForeignKey()

在这里,通过使用一个content_type属性代替了实际的model(如Post,Picture),而object_id则代表了实际model中的一个实例的主键,其中,content_type和object_id的字段命名都是作为字符串参数传进content_object的,即:
content_object=fields.GenericForeignKey('content_type','object_id')

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实例。

from django.db import models
from django.contrib.auth.models import User
from django.contrib.contenttypes import fields
from django.db.models import signals

class Post(models.Model):
       author = models.ForeignKey(User)
       title = models.CharField(max_length=255)
       content = models.TextField()
       created = models.DateTimeField(u'发表时间', auto_now_add=True)
       updated = models.DateTimeField(u'最后修改时间', auto_now=True)
       events = fields.GenericRelation('Event')

       def __str__(self):
            return self.title

       def description(self):
            return u'%s 发表了日志《%s》' % (self.author, self.title)

class Event(models.Model):
        user = models.ForeignKey(User)
        content_type = models.ForeignKey(ContentType)
        object_id = models.PositiveIntegerField()
        content_object= fields.GenericForeignKey('content_type', 'object_id')
        created = models.DateTimeField(u'事件发生时间', auto_now_add=True)

        def __str__(self):
            return "%s的事件: %s" % (self.user, self.description())

        def description(self):
            return self.content_object.description()

        def post_post_save(sender, instance, signal, *args, **kwargs):

            """
            :param sender:监测的类:Post类
            :param instance: 监测的类:Post类
            :param signal: 信号类
            """
            post = instance
            event = Event(user=post.author, content_object=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关联的事件是不会自动删除的。反之有这个字段就会进行自动的级联删除

转载:https://www.jianshu.com/p/5636167ff230

猜你喜欢

转载自www.cnblogs.com/qiaoqianshitou/p/9655362.html