Fork me on GitHub

Django之ContentType组件

一、ContentType概述

ContentType位于django.contrib.contenttypes.models.ContentType,ContentType表示和存储有关项目中安装的模型的信息的实例, 以及安装ContentType新模型时自动创建的新实例 。

ContentType具有返回它们所代表的模型类以及从这些模型查询对象的方法的实例。

对于模型之间的关系,ContentType也可用于启用某个模型的实例与已安装的任何模型的实例之间的“通用”关系。

(一)ContentType模型

1、安装contenttypes框架

使用ContentType模型,首先需要安装contenttypes框架,contenttypes框架包含在由其INSTALLED_APPS创建的默认 列表中:

 

2、ContentType模型

当在settings中安装了contenttypes框架后,在生成模型时:

python manage.py makemigrations
python manage.py migrate

会自动生成ContentType模型表:

 

它以app_label以及model共同描述这个模型,其中app_label表示模型所属app的名称,model表示每一个建立的模型(数据表)名称。

3、ContentType模型实例方法

  • 从ContentType实例获取 它所代表的模型
#得到的就是一个<ContentType: book>
    book = ContentType.objects.get(app_label='app01',model='book')
  • get_object_for_this_type

          查找具体Book对象

 #<class 'app01.models.Book'>
    book_obj = book.get_object_for_this_type(title='数学')
    print(type(book_obj))
  •  model_class

         访问Book模型类

 #<class 'app01.models.Book'>
    book_type=book.model_class()
    print(book_type)

 (二)通用关系

1、正向通用关系

从自己的模型中添加外键,以 ContentType允许模型有效地将自身绑定到另一个模型类。

class PricePolicy(models.Model):

    price = models.IntegerField()
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) 
    object_id = models.PositiveIntegerField()   
    content_object = GenericForeignKey('content_type', 'object_id')

设置一个有三个部分 GenericForeignKey:

  • 给模型ForeignKey来content_type。该字段的通常名称是“content_type”。

  • 给模型提供一个字段,该字段可以存储您要与之相关的模型中的主键值。对于大多数模型,这意味着一个PositiveIntegerField。该字段的通常名称是“object_id”。

  • 给模型 GenericForeignKey,并传递上述两个字段的名称。如果这些字段名为“content_type”和“object_id”,则可以省略 - 这些是GenericForeignKey将要查找的默认字段名称 。

2、反向通用关系

class ScienceBook(models.Model):
    title = models.CharField(max_length=32)
    # 仅用于反向查找
    price_policy_list = GenericRelation("PricePolicy")

反向通用关系顾名思义就是ForeignKey的反向查找,它使得反向操作变得简单,下面以实例说明。

 二、ContentType的应用

(一)应用场景

假设现在有两类书籍,分别为自然书籍以及科学书籍,它们都有自己的价格策略,一般情况建模型是这样的:

class NatureBook(models.Model):
    title = models.CharField(max_length=32)

class ScienceBook(models.Model):
    title = models.CharField(max_length=32)

class PricePolicy(models.Model):
    price = models.IntegerField()
    naturebook = models.ForeignKey(to='NatureBook',on_delete=models.CASCADE)
    sciencebook = models.ForeignKey(to='ScienceBook',on_delete=models.CASCADE)

这样比每一类书籍都建一张价格表要好一些,至少节省了一张表,但是在价格策略表中这样产生的后果就是如下的情况:

"""
Id   price  naturebook_id   sciencebook_id
 1     12      1               0
 2     15      2               0
 3     20      0               4
 4     22      0               2
"""

显然如果还有其它的书籍类还需要添加一列外键,这种情况已经改变了原有表的结构,但是如果应用ContentType就可以很好的解决这个问题:

"""
Id   price  model_name        id
 1     12   naturebook         1
 2     15   naturebook         2
 3     20   sciencebook        1
 4     22   sciencebook        2
"""

这是什么意思呢?举个列子:

"""
NatureBook
id    title
1       a1
2       a2
"""

如果给a1这本书添加价格等于12的策略,只需要在价格策略表中添加上它所在的表名以及这本a1书籍的id即可,显然后续如果还有其它的外键关联只需要添加表名和表中对象id就可以了,这样没有改变价格策略表的结构。表名恰好保存在ContentType模型表中。

(二)实例

from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType

# Create your models here.
class NatureBook(models.Model):
    title = models.CharField(max_length=32)
    # 用于反向查找,不会生成字段
    price_policy_list = GenericRelation("PricePolicy")

class ScienceBook(models.Model):
    title = models.CharField(max_length=32)
    # 用于反向查找,不会生成字段
    price_policy_list = GenericRelation("PricePolicy")

class PricePolicy(models.Model):
    """
    价格策略
    """
    price = models.IntegerField()
    #ContentType中已经有每一个模型表的名称
    content_type = models.ForeignKey(ContentType, verbose_name='关联的表名称',on_delete=models.CASCADE) 
    object_id = models.PositiveIntegerField(verbose_name='关联的表中每一行数据的id')   
    # 实现content_type操作
    content_object = GenericForeignKey('content_type', 'object_id')

生成的表结构:

1、为NatureBook添加一个价格策略(正向操作)

obj = NatureBook.objects.filter(title='a1').first()
PricePolicy.objects.create(price='12',content_object=obj)
obj = NatureBook.objects.filter(title='a1').first()
cobj = ContentType.objects.filter(model='NatureBook').first()
PricePolicy.objects.create(price='12',content_type_id=cobj.id,object_id=obj.id)
普通实现方法

2、根据NatureBook实例找出所有的价格策略(反向操作)

 nbobj= models.NatureBook.objects.filter(id=1).first()
 price_policys = nbobj.price_policy_list.all()

参考文档:https://docs.djangoproject.com/zh-hans/2.0/ref/contrib/contenttypes/

 

posted @ 2019-09-15 22:32  iveBoy  阅读(495)  评论(0编辑  收藏  举报
TOP