Django 之 ContentType组件

一、什么是 ContentTypes

ContentTypesDjango 内置的一个应用,它可以追踪记录项目中所有 appmodel 的对应关系,并记录在 django_content_type 表中。

二、ContentTypes 的应用场景

ContentTypes 适用于一张表与多张表相关关联的场景,如:一个卖课程的网站,它主要售卖两类课程(普通课程和学位课程)。不同课程之间因学习周期不同,价格也不尽相同。因此就形成了每个课程可能有一个或多个价格策略。类似于下面这种:

如果我们自己设计表结构可能就会这样设计:

这样设计有个隐患,那就是当课程数目越来越多时,则意味着价格策略表 PricePolicy 必然要与多张表外键关联,导致越来越复杂,对查找造成很大的不便。

为此我们可以采取仅在 PricePolicy 中存储关联的表名称与相应课程 ID 的方式:

这样一来不论新增多少课程种类,我们只需在 PricePolicy 中新增表名和 ID 即可,其实这一步 ContentTypes 早已帮我们实现。

三、实践一:课程

需求一

  • 为学位课 DegreeCourse Python 全栈添加一条价格策略:1个月、12元
  • 为普泰课 Course rest_framework 添加一个新的价格策略:1个月、8 元

1、app/models.py

from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey

class Course(models.Model):
    """普通课程"""
    title = models.CharField(max_length=64, verbose_name='课程名')

class DegreeCourse(models.Model):
    """学位课程"""
    title = models.CharField(max_length=64, verbose_name='课程名')

class PricePolicy(models.Model):
    """价格策略"""
    price = models.IntegerField()
    period = models.IntegerField()

    # 自己实现
    # table_name = models.CharField(verbose_name='关联的表名称')
    # object_id = models.CharField(verbose_name='关联的表中数据行的 ID')

    # 使用 ContentType
    content_type = models.ForeignKey(ContentType, verbose_name='关联普通课程或者学位课程', on_delete=models.CASCADE)
    object_id = models.IntegerField(verbose_name='关联普通或学位课中的课程 ID')

    # 帮助快速实现 content_type 操作
    content_object = GenericForeignKey('content_type', 'object_id')     # 不会添加字段,只是为了插入或者查询时使用

2、app/urls.py

from django.urls import path, re_path
from api import views

urlpatterns = [
    path('test/', views.test, name='test'),
]

3、app/views.py

from django.shortcuts import render
from django.http import HttpResponse
from app import models

from .models import DegreeCourse, ContentType, PricePolicy

def test(request):
    # 自己实现
    # obj = DegreeCourse.objects.filter(title='Python 全栈').first()
    # # content_type_id
    # cobj = ContentType.objects.filter(model='course').first()
    # # object_id
    # PricePolicy.objects.create(price='11', period=3, content_type_id=cobj.id, object_id=obj.id)

    # 利用 contenttype 实现
    # 为学位课 Python 全栈添加一个新的价格策略:一月 12 元
    # obj1 = models.DegreeCourse.objects.filter(title='Python 全栈').first()
    # models.PricePolicy.objects.create(price=12, period=2.5, content_object=obj1)

    # 为普通课 rest_framework 添加一个新的价格策略:一月 8 元
    obj2 = models.Course.objects.filter(title='rest_framework').first()
    models.PricePolicy.objects.create(price=8, period=1, content_object=obj2)

    return HttpResponse('OK')

由上可见,如果我们自己实现的话,需要先查找 content_type_idobject_id,而使用 ContentType 只需给 content_object 传值即可。



需求二

已知某个课程 ID,获取该课程,并获取该课程所有价格策略

1、app/models.py

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

class Course(models.Model):
    """普通课程"""
    title = models.CharField(max_length=64, verbose_name='课程名')

    # 不会创建数据表列,仅用于反向查找
    price_policy_list = GenericRelation('PricePolicy')      # 新增

class DegreeCourse(models.Model):
    """学位课程"""
    title = models.CharField(max_length=64, verbose_name='课程名')

    # 不会创建数据表列,仅用于反向查找
    price_policy_list = GenericRelation('PricePolicy')

2、app/views.py

def test(request):
    # 已知某个课程 ID,获取该课程,并获取该课程所有价格策略
    course_obj = models.DegreeCourse.objects.filter(id=1).first()
    price_policy = course_obj.price_policy_list.all()

    print(price_policy)

    for i in price_policy:
        # 模型名称
        model_name = models.ContentType.objects.filter(id=i.content_type_id).first().model
        # 相应表字段ID,学位课程名
        degree_course_name = models.DegreeCourse.objects.filter(id=i.object_id).first().title
        # 价格
        price = i.price
        # 周期
        period = i.period

        print(model_name, degree_course_name, price, period)

    return HttpResponse('OK')

四、实践二:商品优惠券

网上商城购物时,会有各种各样的优惠券,比如通用优惠券,满减券,或者是仅限特定品类的优惠券。在数据库中,可以通过外键将优惠券和不同品类的商品表关联起来:

1、app./models.py

from django.db import models

class Electrics(models.Model):
    """
    id  name
    1   日立冰箱
    2   三星电视
    3   小天鹅洗衣机
    """
    name = models.CharField(max_length=32)

class Foods(models.Model):
    """
    id   name
    1    面包
    2    烤鸭
    """
    name = models.CharField(max_length=32)

class Clothes(models.Model):
    name = models.CharField(max_length=32)

class Coupon(models.Model):
    """
    id     name            Electrics        Foods           Clothes        more...
    1     通用优惠券       null              null            null
    2     冰箱满减券         2               null            null
    3     面包狂欢节        null              1              null

    """
    name = models.CharField(max_length=32)
    electric_obj = models.ForeignKey(to='Electrics', null=True)
    food_obj = models.ForeignKey(to='Foods', null=True)
    cloth_obj = models.ForeignKey(to='Clothes', null=True)

有些商品有优惠券,而有些商品没有,我们只用到模型中的一个或多个字段,就要给每个模型都添加外键关联。若商品类别有很多时,那么就需要添加很多个外键关联,显然这样是不行的。

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

class Electrics(models.Model):
    """
    id  name
    1   日立冰箱
    2   三星电视
    3   小天鹅洗衣机
    """
    name = models.CharField(max_length=32)

    coupons = GenericRelation(to='Coupon')      # 添加反向关联,用于查找

class Foods(models.Model):
    """
    id   name
    1    面包
    2    烤鸭
    """
    name = models.CharField(max_length=32)

    coupons = GenericRelation(to='Coupon')

class Clothes(models.Model):
    name = models.CharField(max_length=32)

    coupons = GenericRelation(to='Coupon')

class Coupon(models.Model):
    """
    id     name            Electrics        Foods           Clothes        more...
    1     通用优惠券       null              null            null
    2     冰箱满减券         2               null            null
    3     面包狂欢节        null              1              null

    """
    name = models.CharField(max_length=32)

    content_type = models.ForeignKey(to=ContentType) # step 1 添加一个字段,关联ContentType表中的某一个表
    object_id = models.PositiveIntegerField() # step 2 添加一个id字段,就是‘某一张’表在ContentType里的对应的id
    content_object = GenericForeignKey('content_type', 'object_id') # step 3 不会添加字段,只是为了插入或者查询时使用

2、app/views.py

from django.shortcuts import render, HttpResponse
from app import models
from django.contrib.contenttypes.models import ContentType

def test(request):
    content = ContentType.objects.filter(app_label='app', model='eletrics').first()
    electrics_class = content.model_class()     # electrics_class : models.Electrics
    res = electrics_class.objects.all()

    # 为三星电视创建优惠券
    s_tv = models.Electrics.objects.filter(id=2).first()
    models.Coupon.objects.create(name='电视优惠券', content_object=s_tv)

    # 查询优惠券 id = 1 绑定了哪些商品
    coupon_obj = models.Coupon.objects.filter(id=1).first()
    prod = coupon_obj.content_object

    return HttpResponse('OK')

五、总结

  • 当一张表与多张表关联的时候可以考虑使用 ContentType
  • GenericForeignKey 仅为了辅助插入操作,不会添加新的字段
  • 如果要反向查找,可在模型中添加 GenericRelation,关联要反向查找的模型

原文地址:https://www.cnblogs.com/midworld/p/11380202.html

时间: 2024-11-09 03:13:32

Django 之 ContentType组件的相关文章

Django之ContentType组件

一.需求 给商品创建优惠券: 看看下面表结构: class Food(models.Model): """ id name 1 面条 """ name = models.CharField(max_length=32) def __str__(self): return self.name class Fruit(models.Model): """ id name 1 苹果 """ n

【Django】ContentType组件

目录 理解 表结构 使用 @ 好,现在我们有这样一个需求,我们的商城里有很多的商品,然而节日要来了,我们要搞活动. 那么,我们就要设计优惠券,优惠券都有什么类型呢?满减的.折扣的.立减的.等等等... 我们对应着活动类型,对某类商品设计优惠卷,比如: 家电是一类商品.食物是一类商品,那么我们就可以设计家电折扣优惠券,以及食物满减优惠券等. 所以,我们一顺手,表结构就出来了: from django.db import models is_true = {'null': True, 'blank'

【Django】ContentType组件 -- 2019-08-08 18:03:22

目录 理解 表结构 使用 原文: http://106.13.73.98/__/72/ @ 好,现在我们有这样一个需求,我们的商城里有很多的商品,然而节日要来了,我们要搞活动. 那么,我们就要设计优惠券,优惠券都有什么类型呢?满减的.折扣的.立减的.等等等... 我们对应着活动类型,对某类商品设计优惠卷,比如: 家电是一类商品.食物是一类商品,那么我们就可以设计家电折扣优惠券,以及食物满减优惠券等. 所以,我们一顺手,表结构就出来了: from django.db import models i

Django 使用Contenttype组件创建多关联数据库表

from django.db import models '''contenttype使用意义:如果使用contenttypes就不需要再使用多个Foreignkey, 因为在django_content_type表已经存储了app名和model名, 所以我们只需要将我们创建的模型与django_content_type表关联起来, 然后再存储一个实例 id 即可准确定位到某个app中某个模型的具体实例 ''' '''使用Django的Contenttype字段.''' # 导入必需模块 fro

django ContentType组件

一,需求 给商品创建优惠券: 看看下面表结构: class Food(models.Model): """ id name 1 面条 """ name = models.CharField(max_length=32) def __str__(self): return self.name class Fruit(models.Model): """ id name 1 苹果 """ n

contenttype组件、Django缓存机制以及跨域请求

1 昨日回顾 版本控制 *** (1)url=127.0.0.1/course/?version=v100000 1 versioning_class=QueryParameterVersioning 'VERSION_PARAM':'version', 'DEFAULT_VERSION':'v2', 'ALLOWED_VERSIONS':['v1','v2'] 2 配置成全局:在setting里:QueryParameterVersioning (2)重要(以后建议用这种):127.0.0.1

Django ContentType组件 需求

ContentType组件 遇到这一张表要跟多张表进行外键关联的时候~我们Django提供了ContentType组件~ ContentType是Django的内置的一个应用,可以追踪项目中所有的APP和model的对应关系,并记录在ContentType表中. 当我们的项目做数据迁移后,会有很多django自带的表,其中就有django_content_type表,我们可以去看下~~~ ContentType组件应用: -- 在model中定义ForeignKey字段,并关联到ContentT

django——contentType组件

contentType组件 django内置的一个组件,方便我们快速的进行连表操作,查询,插入数据 使用方法: 在course表中: policy = GenericRelation('PricePolicy', object_id_field='course_id', content_type_field='table_id') 在价格策略表中: content_obj = GenericForeignKey('table_id','course_id') --加的这两个字段都不会在数据库中生

Django框架(二十七)—— ContentType组件

目录 ContentType组件 一.什么是ContentType组件 二.使用ContentType 三.使用场景总结 ContentType组件 一.什么是ContentType组件 contentType是Django内置的组件,可以方便我们快速的连表查询. 可以追踪项目中所有app和model的对应关系,并记录在ContentType表中 models.py文件的表结构写好后,通过makemigrations和migrate两条命令迁移数据后,在数据库中会自动生成一个django_con