django内置 Contenttypes 框架

一、什么是Django ContentTypes?

  1、Django 包含一个ContentTypes 应用,它可以追踪安装在你的Django 项目里的所有应用,并提供一个高层次的、通用的接口用于与你的模型进行交互。

  2、Django ContentTypes是由Django框架提供的一个核心功能,它对当前项目中所有基于Django驱动的model提供了更高层次的抽象接口。

  3、Contenttypes 的核心应用是ContentType模型,存在于 django.contrib.contenttypes.models.ContentTypeContentType 的实例表示并存储你的项目当中安装的应用的信息,并且每当新的模型安装时会自动创建新的 ContentType 实例。

  4、ContentType  实例具有返回它们表示的模型类的方法,以及从这些模型查询对象的方法。ContentType 还有一个自定义的管理器用于添加方法来与ContentType工作,以及用于获得ContentType实例的特定模型。

  5、你的模型和ContentType 之间的关系还可以用于一个模型实例和任意一个已经安装的模型的实例建立“generic关联”。

二、Django ContentTypes做了什么?

  1、当使用django-admin初始化一个django项目的时候,可以看到在默认的INSTALL_APPS已经包含了django.contrib.contenttypes:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
注释:1、注意django.contrib.contenttypes是在django.contrib.auth之后,这是因为auth中的permission系统是根据contenttypes来实现的。
   2、Contenttypes 框架包含在django-admin startproject 创建的默认的INSTALLED_APPS列表中  
   
3、Admin 应用使用它来记录通过Admin 界面添加或更改每个对象的历史。
   4、Django 的authentication 框架用它来授用户权限给特殊的模型。

   2、关于django.contrib.contenttypes.models文件:

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
注释:可以看出ContentType就是一个简单的django model,而且它在数据库中的表的名字为django_content_type。

   3、有经验的Django开发者对于这个表的名字一般都不会陌生,在第一次对Django的model进行migrate之后,就可以发现在数据库中出现了一张默认生成的名为django_content_type的表。

如果没有建立任何的model,默认django_content_type是这样的:

sqlite> select * from django_content_type;
1|admin|logentry
2|auth|group
3|auth|user
4|auth|permission
5|contenttypes|contenttype
6|sessions|session

注释:1、django_content_type记录了当前的Django项目中所有model所属的app(即app_label属性)以及model的名字(即model属性)。
   2、每一个ContentType 实例有两个字段,共同来唯一描述一个已经安装的模型。
     
app_label模型所在的应用的名称。 这取自模型的app_label 属性,并只包括应用的Python 导入路径的最后的部分。例如,"django.contrib.contenttypes"的app_label 是"contenttypes"。
     model:模型的类的名称。

   4、django_content_type并不只是记录属性这么简单,contenttypes其实是对model的一次封装,因此可以通过contenttypes动态的访问model类型,而不需要每次import具体的model类型。

1、ContentType实例提供的接口
        ContentType.model_class()   # 获取当前ContentType类型所代表的模型类
        ContentType.get_object_for_this_type()  #使用当前ContentType类型所代表的模型类做一次get查询

2、ContentType管理器(manager)提供的接口 ContentType.objects.get_for_id() #通过id寻找ContentType类型,这个跟传统的get方法的区别就是它跟get_for_model共享一个缓存,因此更为推荐。 ContentType.objects.get_for_model() #接收一个模型类或模型的实例,并返回表示该模型的ContentType实例
    ContentType.objects.get_for_models()

 

三、Django ContentTypes的使用方法

  1、每一个 ContentType 都有一些方法允许你用ContentType实例来到达它所代表的model, 或者从model取出对象:

    ContentType.get_object_for_this_type(**kwargs)[source]   #接收ContentType 表示的模型所接收的查询参数,对该模型做一次get() 查询,然后返回相应的对象。
    ContentType.model_class()[source]   #返回此ContentType 实例所表示的模型类。

    示例:

例如,我们可以查找User 模型的ContentType︰

>>> from django.contrib.contenttypes.models import ContentType
>>> user_type = ContentType.objects.get(app_label="auth", model="user")
>>> user_type
<ContentType: user>

然后使用它来查询一个特定的User,或者访问 User 模型类︰

>>> user_type.model_class()
<class 'django.contrib.auth.models.User'>
>>> user_type.get_object_for_this_type(username='Guido')
<User: Guido>
View Code

  2、ContentTypeManager(自定义的管理器ContentTypeManager

    clear_cache()  #清除ContentType 用于跟踪模型的内部缓存,它已为其创建ContentType 实例。你可能不需要自己调用此方法;Django 将在它需要的时候自动调用。

    get_for_id(id) #通过ID查找ContentType由于此方法使用与get_for_model() 相同的共享缓存,建议使用这个方法而不是通常的 ContentType.objects.get(pk=id)

    get_for_model(model[, for_concrete_model=True])#接收一个模型类或模型的实例,并返回表示该模型的ContentType 实例。for_concrete_model=False 允许获取代理模型的ContentType

    get_for_models(*models[, for_concrete_models=True])#接收可变数目的模型类,并返回一个字典,将模型类映射到表示它们的ContentType 实例。for_concrete_model=False 允许获取代理模型的ContentType

get_by_natural_key(app_label, model)#返回由给定的应用标签和模型名称唯一标识的ContentType 实例。这种方法的主要目的是为允许ContentType 对象在反序列化期间通过自然键来引用。

、ContentType的通用类型  
  假设以下的应用场景:

class Coupon(models.Model):
"""优惠券生成规则
ID 优惠券名称 A FK B.FK c.FK
1 通用 null null
2 满100-10 8 1
3 满200-30 8 2
4 满200-30 9 1

ID 优惠券名称 content_type_id(表) object_id(表中数据ID)
1 通用 null null
2 满100-10 8 1
3 满200-30 8 2
4 满200-30 9 1
总结:
"""
name = models.CharField(max_length=64, verbose_name="活动名称")
brief = models.TextField(blank=True, null=True, verbose_name="优惠券介绍")

# 那个表?
content_type = models.ForeignKey(ContentType, blank=True, null=True)
# 对象ID
object_id = models.PositiveIntegerField("绑定课程", blank=True, null=True, help_text="可以把优惠券跟课程绑定")

content_object = GenericForeignKey('content_type', 'object_id')
  #content_object = fields.GenericForeignKey() #应该可以这么写

注释:ContentType提供了一种GenericForeignKey的类型,通过这种类型可以实现在Comment对其余所有model的外键关系。
   一张表和其他多个表管理,并创建多个FK时,并且多个Fk只能选择其中一个时,可以用ContentType。
   通过使用一个content_type属性代替了实际的model,而object_id则代表了实际model中的一个实例的主键,其中,content_type和object_id的字段命名都是作为字符串参数传进content_object的。

 

原文地址:https://www.cnblogs.com/xuanan/p/7892722.html