Django中content_type的使用

Jarvis_You發表於2019-03-04

contenttypes 是Django內建的一個應用,可以追蹤專案中所有app和model的對應關係,並記錄在ContentType表中。

models.py檔案的表結構寫好後,通過makemigrations和migrate兩條命令遷移資料後,在資料庫中會自動生成一個django_content_type表:

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欄位便於查詢關聯的優惠券記錄的查詢。

相關文章