ContentType组件
需求背景
(本文来自网上看的老男孩某期学习视频做的笔记)
现在我们有这样一个需求~ 我们的商城里有很多的商品~~ 节日要来了~ 我们要搞活动~~
那么我们就要设计优惠券~~ 优惠券都有什么类型呢~~ 满减的~ 折扣的~ 立减的~~
我们对应着我们活动类型~ 对我们的某类商品设计优惠券~~ 比如~~
from django.db import models
class Appliance(): """ 家用电器表 id name 1 冰箱 2 电视 3 洗衣机 """ name = models.CharField(max_length=64)
class Food(models.Model): """ 食物表 id name 1 面包 2 牛奶 """ name = models.CharField(max_length=32)
class Fruit(models.Model): """ 水果表 id name 1 苹果 2 香蕉 """ name = models.CharField(max_length=32)
|
好了,现在我们有这么多商品,然后我们来做优惠券模型吧:
class Coupon(models.Model): """ 优惠券表 id name appliance_id food_id fruit_id 1 通用优惠券 null null null 2 冰箱折扣券 1 null null 3 电视折扣券 2 null null 4 苹果满减卷 null null 1 我每增加一张表就要多增加一个字段 """ name = models.CharField(max_length=32) table_name = models.CharField(max_length=32) appliance = models.ForeignKey(to="Appliance", null=True, blank=True) food = models.ForeignKey(to="Food", null=True, blank=True) fruit = models.ForeignKey(to="Fruit", null=True, blank=True) 。。。。。。
|
实际上我们商品的种类会特别的多,导致我们这张表外键越来越多, 现在还好,只有三种商品,如果你有300种呢?你还能设计300个外键么?
有人说那我不这么设计了,我搞coupon_food, coupon_fruit, coupon_xxx....
每种商品我都设计一个对应的优惠券模型,外键关联到对应的商品行么?–那你每个coupon表的外键倒是不多了,但是数据模型有点多(300多个呢),看得人脑瓜子嗡嗡的…
解决思路
对于这个问题你看这样设计好不好
class Coupon(models.Model): """ id title table_id object_id 1 烤肠 1 1 2 芒果 2 1 """ title = models.CharField(max_length=32) table = models.ForeignKey(to="MyTable") object_id = models.IntegerField()
class MyTable(models.Model): """ id app_name table_name 1 Demo food 2 Demo fruit 3 Demo appliance """ app_name = models.CharField(max_length=32) table_name = models.CharField(max_length=32)
|
- 我们建立一个名叫
MyTable
的模型,里面存放一个app_name
, 以及对应app_name
中每个table_name
的映射关系
- 我们再建立一个
Coupon
模型,把它的table
字段和这个MyTable
模型建立外键关联, 这样我们可以轻松查询到这个优惠券对应的是哪个app_name的哪一个table_name模型
- 我们再在
Coupon
模型中建立一个object_id的字段,指明对应table_name商品分类中哪种object_id商品的优惠
- 这样我们可以通过Coupon模型轻松查找出对应app_name中的对应table_name,以及对应table_name中的哪一个商品(object_id), 反向亦然
事实上,这个MyTable我们功能丰富的Django已经帮忙设计好了,不需要我们自己创建,这就是ContentType组件
ContentType组件
ContentType实际上是一张Django实现的表,类似如下结构:
用于解决一张表需要跟多张表建立外键关系的情况
当我们建立表并且数据迁移的时候,Django会把我们的表自动加入ContentType
使用
导入
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
|
第一步,跟ContentType建立外键关系
content_type = model.ForeignKey(to=ContentType)
第二步, 对象id
object_id = model.IntergerFeild()
第三步,表以及对象id绑定关系
content_obj = GernicForeignKey("content_type", "object_id")
例子
模型对象的创建
设立Coupon模型对象
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
class Coupon(models.Model): title = model.CharField(max_length=32) content_type = models.ForeignKey(to=ContentType) object_id = models.IntegerField() content_obj = GenericForeignKey('content_type', 'object_id')
|
生成反向查询字段
class Fruit(models.Model): name = models.CharField(max_length=32) coupons = GenericRelation(to="Coupon")
|
为Fruit添加Coupon记录
In [1]: ct = ContentType.objects.get(app_label="mall", model="fruit") # 获得对应表名的对象
In [2]: fruit = Fruit.objects.create(**{"name": "banana"}) # 获得水果对象 Out[2]: <Fruit: Fruit object>
In [3]: Coupon.objects.create(title="banana 30% discount!", content_type=ct, object_id=fruit.id) # 添加优惠券记录 Out[3]: <Coupon: Coupon object>
|
查看数据库中数据:
MariaDB [message_center]> select * from django_content_type; +----+--------------+----------------+ | id | app_label | model | +----+--------------+----------------+ | 1 | admin | logentry | | 2 | auth | group | | 3 | auth | permission | | 4 | auth | user | | 5 | contenttypes | contenttype | | 9 | mall | appliance | | 10 | mall | coupon | | 11 | mall | food | | 12 | mall | fruit | | 7 | message | project | | 8 | message | projectmsgtype | | 6 | sessions | session | +----+--------------+----------------+
|
MariaDB [message_center]> select * from mall_coupon; +----+------------------------+-----------+-----------------+ | id | title | object_id | content_type_id | +----+------------------------+-----------+-----------------+ | 1 | Sandwich 70% discount! | 1 | 11 | | 2 | banana 30% discount! | 1 | 12 | | 3 | apple 90% discount! | 2 | 12 | +----+------------------------+-----------+-----------------+
|
对应优惠券信息已经添加
正向查询
查询优惠券id绑定了哪个商品:
In [1]: coupon_obj = Coupon.objects.filter(id=1).first() # 查询到id为1的优惠券
In [2]: coupon_obj.title Out[2]: u'Sandwich 70% discount!'
In [3]: coupon_obj.content_type # 查询对应表名 Out[3]: <ContentType: food>
In [4]: f1 = coupon_obj.content_obj # 查询对应对象
In [5]: f1.name Out[5]: u'Sandwich'
|
反向查询
即看商品绑定了哪些优惠券
反向查询分两种情况: 事先没有定义GenericRelation
和事先定义了GenericRelation
的
怕忘了回顾一下:
class Food(models.Model): name = models.CharField(max_length=32) coupons = GenericRelation(to="Coupon")
|
In [1]: f2 = Food.objects.first()
In [2]: c = ContentType.objects.get(app_label="mall", model="fruit") # 先去contenttype中找对应模型项
In [3]: Coupon.objects.filter(content_type=c, object_id=f2.id).all() # 通过contenttype中对应模型找到模型对应的优惠券列表 Out[3]: <QuerySet [<Coupon: Coupon object>]>
In [4]: coupon = Coupon.objects.filter(content_type=c, object_id=f2.id).all()[0] # 通过Contenttype中的模型和对应的object_id确定对应object_id的优惠券
In [5]: coupon.name Out[5]: u'banana 30% discount!'
|
In [6]: f2.coupons.all() Out[6]: <QuerySet [<Coupon: Coupon object>]>
|
方便很多。