Django Contenttype

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

class ContentType(models.Model):

        app_label = models.CharField(max_length=100)

        model = models.CharField(_('python model class name'), max_length=100)

        objects = ContentTypeManager()

        class Meta:

                verbose_name = _('content type')

                verbose_name_plural = _('content types')

                db_table = 'django_content_type'

                unique_together = (('app_label', 'model'),)

        def __str__(self):

               return self.name


def demo(request):

        obj = models.ContentType.objects.get(id=10)

        print(obj.model_class()) #

        returnHttpResponse('............')

过model_class就可以获取对应的类。也就是说,今后,我们如果自己定义model如果有外键关联到這个ContentType上,我们就能找到对应的model名称


django_content_type记录了当前的Django项目中所有model所属的app(即app_label属性)以及model的名字(即model属性)。 当然,django_content_type并不只是记录属性这么简单,contenttypes是对model的一次封装,因此可以通过contenttypes动态的访问model类型,而不需要每次import具体的model类型。


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 importUser

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-ContentType-signals 

转载:https://www.cnblogs.com/vipchenwei/p/7891890.html

        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关联的事件是不会自动删除的。反之有这个字段就会进行自动的级联删除

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容