0%

DRF学习笔记之Content Type

ContentType组件

需求背景

(本文来自网上看的老男孩某期学习视频做的笔记)

现在我们有这样一个需求~ 我们的商城里有很多的商品~~ 节日要来了~ 我们要搞活动~~

那么我们就要设计优惠券~~ 优惠券都有什么类型呢~~ 满减的~ 折扣的~ 立减的~~

我们对应着我们活动类型~ 对我们的某类商品设计优惠券~~ 比如~~

#!/usr/bin/env python
# -*-coding:utf8-*-
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实现的表,类似如下结构:

id			app_label		model

用于解决一张表需要跟多张表建立外键关系的情况

当我们建立表并且数据迁移的时候,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)
# 第一步,跟ContentType表建立外键关系 目的定位表
content_type = models.ForeignKey(to=ContentType)
# 第二步,定位对象id
object_id = models.IntegerField()
# 第三步,给表以及对象id绑定关系
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>]>

方便很多。

欢迎关注我的其它发布渠道