????在django中巫延,有一個記錄了項目中所有model元數(shù)據(jù)的表效五,就是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就可以獲取對應的類戒劫。也就是說,今后婆廊,我們?nèi)绻约憾xmodel如果有外鍵關聯(lián)到這個ContentType上迅细,我們就能找到對應的model名稱
django_content_type記錄了當前的Django項目中所有model所屬的app(即app_label屬性)以及model的名字(即model屬性)。 當然淘邻,django_content_type并不只是記錄屬性這么簡單茵典,contenttypes是對model的一次封裝,因此可以通過contenttypes動態(tài)的訪問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類型,這個跟傳統(tǒng)的get方法的區(qū)別就是它跟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的類型蜻直,通過這種類型可以實現(xiàn)在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的字段命名都是作為字符串參數(shù)傳進content_object的赎瑰,即:
????????content_object=fields.GenericForeignKey('content_type','object_id')
Django-ContentType-signals
轉(zhuǎn)載:https://www.cnblogs.com/vipchenwei/p/7891890.html
????????django的signal結(jié)合contenttypes可以實現(xiàn)好友最新動態(tài),新鮮事破镰,消息通知等功能餐曼⊙勾ⅲ總體來說這個功能就是在用戶發(fā)生某個動作的時候?qū)⑵溆涗浵聛砘蛘吒郊幽承┎僮?/b>,比如通知好友源譬。要實現(xiàn)這種功能可以在動作發(fā)生的代碼里實現(xiàn)也可以通過數(shù)據(jù)庫觸發(fā)器等實現(xiàn)集惋,但在django中,一個很簡單的方法的就是使用signals踩娘。
????????當django保存一個object的時候會發(fā)出一系列的signals刮刑,可以通過對這些signals注冊listener,從而在相應的signals發(fā)出時執(zhí)行一定的代碼养渴。
????????使用signals來監(jiān)聽用戶的動作有很多好處雷绢,1、不管這個動作是發(fā)生在什么頁面理卑,甚至在很多頁面都可以發(fā)生這個動作习寸,都只需要寫一次代碼來監(jiān)聽保存object這個動作就可以了。2傻工、可以完全不修改原來的代碼就可以添加監(jiān)聽signals的功能霞溪。3、你幾乎可以在signals監(jiān)聽代碼里寫任何代碼中捆,包括做一些判斷是不是第一次發(fā)生此動作還是一個修改行為等等鸯匹。
????????想要記錄下每個操作,同時還能追蹤到這個操作的具體動作泄伪。
????????*首先用信號機制殴蓬,監(jiān)聽信號,實現(xiàn)對信號的響應函數(shù)蟋滴,在響應函數(shù)中記錄發(fā)生的動作(記錄在一張記錄表染厅,相當于下文的Event)。
????????*其次就是為了能追蹤到操作的具體動作津函,必須從這張表中得到相應操作的model肖粮,這就得用到上面說的ContentType。
對于新鮮事這個功能來說就是使用GenericRelation來產(chǎn)生一個特殊的外鍵尔苦,它不像models.ForeignKey那樣涩馆,必須指定一個Model來作為它指向的對象。GenericRelation可以指向任何Model對象允坚,有點像C語言中 void* 指針魂那。
這樣關于保存用戶所產(chǎn)生的這個動作,比如用戶寫了一片日志稠项,我們就可以使用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'發(fā)表時間', 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 發(fā)表了日志《%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'事件發(fā)生時間', 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:監(jiān)測的類:Post類
? ????????????????????? :param instance: 監(jiān)測的類: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)可以監(jiān)聽多個類
只要model中有object的保存操作迷殿,都將執(zhí)行post_post_save函數(shù),故可以在這個接受函數(shù)中實現(xiàn)通知好友等功能咖杂。
前面說到django在保存一個object的時候會發(fā)出一系列signals庆寺,在這里我們所監(jiān)聽的是signals.post_save這個signal,這個signal是在django保存完一個對象后發(fā)出的诉字,django中已定義好得一些signal, 在django/db/models/signal.py中可以查看懦尝,同時也可以自定義信號。
利用connect這個函數(shù)來注冊監(jiān)聽器壤圃, connect原型為:
def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
第一個參數(shù)是要執(zhí)行的函數(shù)陵霉,第二個參數(shù)是指定發(fā)送信號的Class,這里指定為Post這個Model伍绳,對其他Model所發(fā)出的signal并不會執(zhí)行注冊的函數(shù)踊挠。
instance這個參數(shù),即剛剛保存完的Model對象實例效床。創(chuàng)建事件的時候看到可以將post這個instance直接賦給generic.GenericForeignKey類型的字段谨朝,從而event實例就可以通過它來獲取事件的真正信息了字币。
最后有一點需要的注意的是共缕,Post的Model定義里現(xiàn)在多了一個字段:
content_object= GenericRelation(‘Event’)
? ? ? 通過這個字段可以得到與某篇post相關聯(lián)的所有事件洗出,最重要的一點是如果沒有這個字段,那么當刪除一篇post的時候图谷,與該post關聯(lián)的事件是不會自動刪除的。反之有這個字段就會進行自動的級聯(lián)刪除