Django中content_type的使用

1,137 阅读2分钟

contenttypes 是Django内置的一个应用,可以追踪项目中所有app和model的对应关系,并记录在ContentType表中。

models.py文件的表结构写好后,通过makemigrations和migrate两条命令迁移数据后,在数据库中会自动生成一个django_content_type表:

通过下边的示例来理解content_type的具体应用:

创建:

from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey,GenericRelation
# Create your models here.
class Food(models.Model):
    name = models.CharField(max_length=32)
    coupon = GenericRelation("Coupon")
class Cloth(models.Model):
    name = models.CharField(max_length=32)
    coupon = GenericRelation("Coupon")
class Coupon(models.Model):
    """
    id      food_id     cloth_id  ……
    1         null        null
    2           1         null
    """
    name = models.CharField("活动名称",max_length=64)
    brief = models.TextField(blank=True,null=True,verbose_name="优惠券介绍")
    content_type = models.ForeignKey(ContentType,blank=True,null=True) # 代表哪个app下的哪张表
    object_id = models.PositiveIntegerField("绑定商品",blank=True,null=True) # 代表哪张表中的对象id
    content_obj = GenericForeignKey("content_type","object_id") #不会生成额外的列

注意:ContentType只运用于1对多的关系!!!并且多的那张表中有多个ForeignKey字段。

创建记录和查询:

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


def test(request):
    if request.method == 'GET':
        # ContentType表对象有model_class() 方法,取到对应model
        content = ContentType.objects.filter(app_label='Api', model='bed').first()
        bed_class = content.model_class() # bed_class 就相当于models.Bed
        res = cloth_class.objects.all()
        print(res)

        # 为bed(id=2)创建一条优惠记录
        bed_obj = models.Bed.objects.filter(id=1).first()
        models.Coupon.objects.create(name='床の优惠券', content_object=bed_obj)

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

        # 查询bed(id=1)的所有优惠券
        res = bed_obj.coupons.all()
        print(res)

        # 查询obj的所有优惠券:如果没有定义反向查询字段,通过如下方式:
        content = ContentType.objects.filter(app_label='Api', model='model_name').first()
        res = models.OftenAskedQuestion.objects.filter(content_type=content, object_id=obj.pk).all()

        return HttpResponse('ok')

总结:

  当一张表作为多个表的FK,并且只能选择其中一个或者几个时,就可以使用content_type表;例如上面的优惠券表,被食物和床当作FK,数据库表一旦建立就难以更改,如果以后需要增加电器等表并把优惠券表作为FK表,这时就不能做到在优惠券表增加列字段electr_id,因为表只能增加行记录而不能增加列字段,因此就可以使用content_type表来将表与表中的对象进行关联,从而做到不增加列字段的情况下增加FK关系。

  在使用content_type表时,需要在FK表中增加content_type作为FK字段,并增加GenericForeignKey便于优惠券表记录的建立以及单个优惠券对象对应的其他商品的查找。在优惠券表关联的关系表中增加GenericRelation字段便于查找关联的优惠券记录的查找。