Django之ContentType详解

1、Django之ContentType简介

  contenttypes 是Django内置的一个应用,可以追踪项目中所有app和model的对应关系,并记录在ContentType表中。
models.py文件的表结构写好后,通过makemigrations和migrate两条命令迁移数据后,在数据库中会自动生成一个django_content_type表:

 

 

 

每当我们创建了新的model并执行数据库迁移后,ContentType表中就会自动新增一条记录。比如我在应用api的models.py中创建表class Electrics(models.Model): pass。

从数据库查看ContentType表,显示如下:

 

 

 那么这个表有什么作用呢?这里提供一个场景,网上商城购物时,会有各种各样的优惠券,比如通用优惠券,满减券,或者是仅限特定品类的优惠券。

在数据库中,可以通过外键将优惠券和不同品类的商品表关联起来:

 

# Create your models here.
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation


class Electrics(models.Model):
    name = models.CharField(max_length=32)
    price = models.IntegerField(default=100)
    coupons = GenericRelation(to='Coupon')  # 用于反向查询,不会生成表字段

    def __str__(self):
        return self.name


class Foods(models.Model):
    name = models.CharField(max_length=32)
    price = models.IntegerField(default=100)
    coupons = GenericRelation(to='Coupon')

    def __str__(self):
        return self.name


class Clothes(models.Model):
    name = models.CharField(max_length=32)
    price = models.IntegerField(default=100)
    coupons = GenericRelation(to='Coupon')

    def __str__(self):
        return self.name


class bed(models.Model):
    name = models.CharField(max_length=32)
    price = models.IntegerField(default=100)
    coupons = GenericRelation(to='Coupon')


class Coupon(models.Model):
    """
    Coupon
        id                 name                      electric_id                     food_id    more... # 每增加一张表,关系表的结构就要多加一个字段。
                      格力空调满减券                      1                         null
                      洛川红富士立减券                    null                                2
                     格力空调立减券                       null                                null 
    """
    name = models.CharField(max_length=32)
  electric = models.ForeignKey(to='Electrics', null=True) 
  food = models.ForeignKey(to='Foods', null=True)
  cloth = models.ForeignKey(to='Clothes', null=True)
def __str__(self): return self.name

 

 

 如果是通用优惠券,那么所有的ForeignKey为null,如果仅限某些商品,那么对应商品ForeignKey记录该商品的id,不相关的记录为null。

但是这样做是有问题的:实际中商品品类繁多,而且很可能还会持续增加,那么优惠券表中的外键将越来越多,但是每条记录仅使用其中的一个或某几个外键字段。

 

1.1contenttypes 应用


通过使用contenttypes 应用中提供的特殊字段GenericForeignKey,我们可以很好的解决这个问题。只需要以下三步:  

  • 在model中定义ForeignKey字段,并关联到ContentType表。通常这个字段命名为“content_type”
  • 在model中定义PositiveIntegerField字段,用来存储关联表中的主键。通常这个字段命名为“object_id”
  • 在model中定义GenericForeignKey字段,传入上述两个字段的名字。


为了更方便查询商品的优惠券,我们还可以在商品类中通过GenericRelation字段定义反向关系

# Create your models here.
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation


class Electrics(models.Model):
    name = models.CharField(max_length=32)
    price = models.IntegerField(default=100)
    coupons = GenericRelation(to='Coupon')  # 用于反向查询,不会生成表字段

    def __str__(self):
        return self.name


class Foods(models.Model):
    name = models.CharField(max_length=32)
    price = models.IntegerField(default=100)
    coupons = GenericRelation(to='Coupon')

    def __str__(self):
        return self.name


class Clothes(models.Model):
    name = models.CharField(max_length=32)
    price = models.IntegerField(default=100)
    coupons = GenericRelation(to='Coupon')

    def __str__(self):
        return self.name


class bed(models.Model):
    name = models.CharField(max_length=32)
    price = models.IntegerField(default=100)
    coupons = GenericRelation(to='Coupon')


class Coupon(models.Model):
    """
    Coupon
        id                 name                      content_type_id                 object_id_id
                      格力空调满减券                      10(电器表electrics)             1
                      洛川红富士立减券                    11                                2
                     格力空调立减券                       10                                1
    """
    name = models.CharField(max_length=32)

    content_type = models.ForeignKey(to=ContentType, on_delete=models.CASCADE)  # step 1
    object_id = models.PositiveIntegerField()  # step 2
    # 用于查询
    content_object = GenericForeignKey('content_type', 'object_id')  # step 3

    def __str__(self):
        return self.name

 

 

总结:  当一张表和多个表FK关联,并且多个FK中只能选择其中一个或其中n个时,可以利用contenttypes app,只需定义三个字段就搞定!

 

1.2查询示例

 

 

 

 

 查询格力空调满减券对应的电器的name,price

示例一:

 

 

 

 

 

 

 

 

 

 

 

示例二(使用):

 

 

 

 

 

 

 

 

 

 

posted @ 2019-09-23 22:34  Mr_Yun  阅读(459)  评论(0)    收藏  举报