返回顶部

【Django】--Contenttypes框架,了解内容类型和范型关系

Django中文文档1.8 https://legacy.gitbook.com/book/wizardforcel/django-chinese-docs-18/details

Contenttypes

  • Django的contenttypes应用,可以追踪安装在Django项目里的所有应用,并提供一个高层次及通用的接口用于与你的模型进行交互。
  • Contenttypes的核心应用是ContentType模型,位置:django.contrib.contenttypes.models.ContentType
  • 通过添加 'django.contrib.contenttypes' 到你的 INSTALLED_APPS 设置中来启用它。

Contenttypes相关方法

  1.class ContentType[source]

    每一个 ContentType 实例有两个字段,共同来唯一描述一个已经安装的模型,分别是:

      app_label

        模型所在的应用的名称。 这取自模型的 app_label 属性,并只包括应用的Python 导入路径的最后的部分。

          例如,"django.contrib.contenttypes"的 app_label 是"contenttypes".

       model

        模型的类的名称

    额外属性:

     name[source]

        Contenttype 的人类可读的的名称。它取之于模型的 verbose_name 属性.

 

      Changed in Django 1.8:

        安装 contenttypes 应用后,添加 sites应用 到你的 INSTALLED_APPS 设置并运行 manage.py migrate 来安装它,模型 django.contrib.sites.models.Site 将安装到你的数据库中。

        同时将创建 ContentType 的一个具有以下值的新实例︰
          app_label 将设置为 'sites' (Python 路径"django.contrib.sites"的最后部分)。
          model 将设置为 'site' 。

  2.`get_object_for_this_type (**kwargs)[source]

    接收 ContentType 表示的模型所接收的查询参数,对该模型做 一次get() 查询 ,然后返回相应的对象

 

  3.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>

 

    get_object_for_this_type() 和 model_class() 一起使用可以实现两个极其重要的功能

      1. 使用这些方法,你可以编写高级别的泛型代码,执行查询任何已安装的模型—— 而不是导入和使用单一特定模型的类,可以通过 app_label 和 model到 ContentType 在运行时查找,然后使用这个模型类或从它获取对象。
      2. 你可以关联另一个模型到 ContentType 作为一种绑定它到特定模型类的方式,然后使用这些方法来获取对那些模型的访问。

  4.ContentTypeManager 

    class ContentTypeManager [source]

      ContentType 还具有自定义的管理器 ContentTypeManager ,它增加了下列方

 

1.get_for_id (id)
  代替:ContentType.objects.get(pk=id)
  原因:此方法使用与 get_for_model() 有相同的共享缓存
2.get_for_model(model[, for_concrete_model=True])
  接收一个模型类或模型的实例,并返回表示该模型的 ContentType 实例。 for_concrete_model=False 允许获取代理模型的 ContentType 。
3.get_for_models (*models[, for_concrete_models=True])
  接收可变数目的模型类,并返回一个字典,将模型类映射到表示它们的 ContentType 实例。 for_concrete_model=False 允许获取代理模型的 ContentType 。
4.clear_cache ()[source]
  
清除 ContentType 用于跟踪模型的内部缓存,它已为其创建 ContentType 实例。你可能不需要自己调用此方法;Django 将在它需要的时候自动调用。
5.get_by_natural_key (app_label, model)[source]
  
返回由给定的应用标签和模型名称唯一标识的 ContentType 实例。这种方法的主要目的是为允许 ContentType 对象在反序列化期间通过自然键来引用。

get_for_model() 方法特别有用,当你知道你需要与 ContentType 交互但不想要去获取模型元数据以执行手动查找的麻烦。
>>> from django.contrib.auth.models import User
>>> user_type = ContentType.objects.get_for_model(User)
>>> user_type
<ContentType: user>

 



 

通用关系

  在你的model添加一个外键到 ContentType 这将允许你更快捷的绑定自身到其他的model class,就像上述的 Permission model 一样。

  但是它非常有可能进一步的利用 ContentType 来实现真正的 generic (有时称之为多态) relationships 在models之间。

  一个简单的例子是标记系统,它可能看起来像这样:

    

from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
class TaggedItem(models.Model):
  tag = models.SlugField()
  content_type = models.ForeignKey(ContentType)
  object_id = models.PositiveIntegerField()
  content_object = GenericForeignKey('content_type', 'object_id')
  def __str__(self): # __unicode__ on Python 2
    return self.tag


一个普通的 ForeignKey 只能指向其他任意一个model,这是因为TaggedItem model用一个 ForeignKey只能关联一个model,也只能有一个model存储tags.
contenttypes application 提供了一个特殊的字段(GenericForeignKey)避免了这个问题,并且允许你和任何一个model建立关联关系

 

class GenericForeignKey[source]

  三步建立GenericForeignKey:

    1.给你的model设置一个ForeignKey字段到ContentType.一般命名为:“content_type”.

    2.给你的model设置一个字段,用来存储你想要关联的model主键值。对于大多数model,这是一个 PositiveIntegerField字段。并且通常命名为“object_id”.

    3.给你的model一个 GenericForeignKey字段,把1,2点提到的那两个字段的名词传给他。如果这两个字段名字分别为“content_type”和“object_id”,你就可以省略他们。因为GenericForeignKey默认会去自动查找这两个字段。

 

for_concrete_model

  默认是 True .

  如果为 False ,那么字段将会涉及到proxy models(代理模型)。
  这映射了 for_concrete_model 的参数到 get_for_model() .

allow_unsaved_instance_assignme

  New in Django 1.8.
    
与 ForeignKey.allow_unsaved_instance_assignment 类似。
  自1.7版起已弃用

    此类过去在 django.contrib.contenttypes.generic 中定
    义。将从Django 1.9中删除从此旧位置导入的支持。

主键类型的兼容性

  “object_id” 字段并不总是相同的,这是由于储存在相关模型中的主键类型的关系,但
   是他们的主键值必须被转变为相同类型。

   通过 “object_id” 字段 的get_db_prep_value() 方法实现。

  例如, 如果你想要简历generic 关系到一个 IntegerField 或者 CharField 为主
  键的模型, 你可以使用 CharField 给 “object_id”字段,因为数字是可以被
  get_db_prep_value() 转化为字母的

 

序列化 ContentType 的对象引用

  如果你想要序列化一个建立了 generic关系的model数据(for example, whengenerating fixtures ) ,你应该用一个自然键来唯一的标识相关的ContentType 对象。有关详细信息,请参阅natural keys和 dumpdata --natural-foreign 。

  这允许你像平时用 ForeignKey 类似的API来工作。每一个 TaggedItem 都将有一个 content_object 字段返回对象的关联,你也可以指定这个字段或者用它来创建一个 TaggedItem :

  

>>> from django.contrib.auth.models import User
>>> guido = User.objects.get(username='Guido')
>>> t = TaggedItem(content_object=guido, tag='bdfl')
>>> t.save()
>>> t.content_object
<User: Guido>

 

由于 GenericForeignKey 完成的方式问题,,你没有办法用这个字段直接执行数据库API,filters的操作。

  比如 ( filter() and exclude() , for example) 。因为一个 GenericForeignKey 不是一个普通的字段对象t, 这些例子是不会工作的:

 

  

# This will fail
>>> TaggedItem.objects.filter(content_object=guido)
# This will also fail
>>> TaggedItem.objects.get(content_object=guido)

 

同样的, GenericForeignKey s 是不会出现在 ModelForm S

 

 

反向通用关系

  class GenericRelation [source]

    自1.7版起已弃用:此类过去在 django.contrib.contenttypes.generic 中定义。

    将从Django 1.9中删除从此旧位置导入的支持

    
  related_query_name

   New in Django 1.7

     默认情况下,相关对象返回到该对象的关系不存在。设置 related_query_name来创建一个对象从关联对象返回到对象自身。这允许查询和筛选相关的对象。

     如果你知道你最经常使用哪种型号的,你还可以添加一个“反向”的通用关系,以使其能附加一个附加的API。

  

class Bookmark(models.Model):
  url = models.URLField()
  tags = GenericRelation(TaggedItem)

  Bookmark 的每个实例都会有一个 tags 属性,可以用来获取相关的TaggedItems

  

>>> b = Bookmark(url='https://www.djangoproject.com/')
>>> b.save()
>>> t1 = TaggedItem(content_object=b, tag='django')
>>> t1.save()
>>> t2 = TaggedItem(content_object=b, tag='python')
>>> t2.save()
>>> b.tags.all()[<TaggedItem: django>, <TaggedItem: python>]

  New in Django 1.7

    定义一个 GenericRelation 伴有 related_query_name 可以允许从相关联的对象中查询。

    

tags = GenericRelation(TaggedItem, related_query_name='bookmarks’)

  这允许你从 TaggedItem 执行过滤筛选, 排序, 和其他的查询操作 Bookmark

    

>>> # Get all tags belonging to books containing `django` in the url
>>> TaggedItem.objects.filter(bookmarks__url__contains='django')
[<TaggedItem: django>, <TaggedItem: python>]

  GenericRelation和GenericForeignKey一样可以接受以content-type和object-id字段命名的参数。

  如果一个model的GenericForeignKey字段使用的不是默认的命名,当你创建一个GenericRelation的时候一定要显示的传递这个字段命名给它。例如:

  TaggedItem model关联到上述所用的字段用content_type_fk 和 object_primary_key两个名称来创建一个generic foreignkey,然后就需要这样定义:

  

tags = GenericRelation(TaggedItem,
              content_type_field='content_type_fk',
              object_id_field='object_primary_key')

  

  当然,如果你没有添加一个反向的关系,你可以手动做相同类型的查找:

  

>>> b = Bookmark.objects.get(url='https://www.djangoproject.com/')
>>> bookmark_type = ContentType.objects.get_for_model(b)
>>> TaggedItem.objects.filter(content_type__pk=bookmark_type.id,
...                     object_id=b.id)
[<TaggedItem: django>, <TaggedItem: python>]

  如果一个GenericRelation model在它的GenericForeignKey的ct_field or fk_field 使用了非默认值,例如:

    ct_field = "object_pk" , 你就要设置GenericRelation中的ct_field和fk_field:

    

comments = fields.GenericRelation(Comment, object_id_field="object_pk")

  同时请注意,如果你删除了一个具有 GenericRelation 的对象, 任何以GenericForeignKey 指向他的对象也会被删除. 在上面的例子中, 如果一个Bookmark 对象被删除了,任何指向它的 TaggedItem 对象也会被同时删除.

  不同于 ForeignKey , GenericForeignKey 并不接受一个 on_delete 参数来控制它的行为:如果你非常渴望这种可控制行为,你应该不使用GenericRelation 来避免这种级联删除,并且这种行为控制也可以通过pre_delete 信号来提供。

 

通用关系和聚合

   Django’s database aggregation API不能与 GenericRelation 配合使用。例如,您可能会试图尝试以下操作:

  

  Bookmark.objects.aggregate(Count('tags'))

  这是错误的。然而generic relation 添加了额外的查询集过滤来保证正确的内容类型,但是aggregate()方法并没有被考虑进来。现在,如果你需要再generic relations 使用聚合,你只能不通过聚合API来计算他们。

 

Generic relation在表单中的应用

  django.contrib.contenttypes.forms 模块提供:

      BaseGenericlnlineFormSet

       用于GenericForeignKey的表单工厂,generic_inlineformaset_factory().

    class BaseGenericInlineFormSet [source]

      自1.7版起已弃用:此类过去在 django.contrib.contenttypes.generic 中定义。将从Django 1.9中删除从此旧位置导入的支持。

    

      generic_inlineformset_factory (model, form=ModelForm,formset=BaseGenericInlineFormSet, 
                      ct_field="content_type", fk_field="object_id",fields=None,
                      exclude=None, extra=3, can_order=False, can_delete=True,
                      max_num = None,formfield_callback=None,validate_max=False,
                      for_concrete_model = True,min_num= None,validate_min = False)

 

    使用 modelformset_factory() 返回 GenericInlineFormSet.

    如果它们分别与默认值, content_type 和 object_id 不同,则必须提供 ct_field 和 fk_field 。

    其他参数与 modelformset_factory() 和 inlineformset_factory() 中记录的参数类似.

 

    for_concrete_model 参数对应于 GenericForeignKey 上的 for_concrete_model 参数。

    自1.7版起已弃用:此函数用于在 django.contrib.contenttypes.generic 中定义。

    将从Django 1.9中删除从此旧位置导入的支持。

    Changed in Django 1.7:

       min_num 和 validate_min.

 

管理中的通用关系

  django.contrib.contenttypes.admin 模块提供GenericTabularInlineGenericStackedInline(GenericInlineModel的子类别)

    这些类和函数确保了generic relations在forms 和 admin的使用。有关详细信息,请参阅model formset和admin文档。

    class GenericInlineModelAdmin [source]

      GenericInlineModelAdmin 类继承了来自 InlineModelAdmin 类的所有属性。但是,它添加了一些自己的用于处理通用关系:

      ct_field

        模型上的ContentType外键字段的名称.默认为content_type。

      ct_fk_field

        表示相关对象的ID的整数字段的名称。默认为object_id。

      自1.7版起已弃用:此类过去在 django.contrib.contenttypes.generic 中定义。将从Django 1.9中删除从此旧位置导入的支持。

    class GenericTabularInline [source]

    class GenericStackedInline [source]

    GenericInlineModelAdmin 的子类,分别具有堆叠和表格布局. 

 

    自1.7版起已弃用:这些类以前在 django.contrib.contenttypes.generic 中定义。将从Django 1.9中删除从此旧位置导入的支持.

     

  

 

posted @ 2018-04-24 11:58  Will_D_Zhang  阅读(364)  评论(0编辑  收藏  举报