Django ContentTypes是由Django框架提供的一個核心功能,它對當前項目中全部基於Django驅動的model提供了更高層次的抽象接口。主要用來建立模型間的通用關係(generic relation)。python
進一步瞭解ContentTypes能夠直接查閱如下這兩個連接:數據庫
當建立一個django項目時,能夠看到在默認的INSTALL_APPS已經包含了django.contrib.contenttypes。django
# Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01.apps.App01Config', ]
注意:django.contrib.contenttypes是在django.contrib.auth以後,這是由於auth中的permission系統是根據contenttypes來實現的。緩存
導入contenttypes組件:session
from django.contrib.contenttypes.models import ContentType
查看django.contrib.contenttypes.models.ContentType類的內容:app
class ContentType(models.Model): app_label = models.CharField(max_length=100) model = models.CharField(_('python model class name'), max_length=100) objects = ContentTypeManager() class Meta: verbose_name = _('content type') verbose_name_plural = _('content types') db_table = 'django_content_type' unique_together = (('app_label', 'model'),) def __str__(self): return self.name
能夠看到ContentType就是一個簡單的django model,並且它在數據庫中的表的名字爲django_content_type。 框架
在第一次對Django的model進行migrate以後,就能夠發如今數據庫中出現了一張默認生成的名爲django_content_type的表。
若是沒有創建任何的model,默認django_content_type是前六項:優化
django_content_type記錄了當前的Django項目中全部model所屬的app(即app_label屬性)以及model的名字(即model屬性)。 ui
django_content_type並不僅是記錄屬性這麼簡單.了contenttypes是對model的一次封裝,所以能夠經過contenttypes動態的訪問model類型,而不須要每次import具體的model類型。this
獲取當前ContentType類型所表明的模型類
使用當前ContentType類型所表明的模型類作一次get查詢
假設咱們建立以下模型,裏面包含學位課程、專題課程、價格策略。
價格策略既能夠是專題課程的價格策略,也能夠是學位課程的價格策略。須要在pricepolicy對象裏添加很是多的ForeignKey。示例以下所示:
class Food(models.Model): """ id title 1 麪包 2 牛奶 """ title = models.CharField(max_length=32) # 不會生成字段 只用於反向查詢 coupons = GenericRelation(to="Coupon") class Fruit(models.Model): """ id title 1 蘋果 2 香蕉 """ title = models.CharField(max_length=32) # 若是有40張表,則每個都要創建外鍵關係 class Coupon(models.Model): """ id title food_id fruit_id 1 麪包九五折 1 null 2 香蕉滿10元減5元 null 2 """ title = models.CharField(max_length=32) food = models.ForeignKey(to="Food") fruit = models.ForeignKey(to="Fruit")
這樣作很傻,會形成代碼重複和字段浪費。有一種優化的方案是:用兩個字段去定位對象不用去建立多個外鍵關係。
# 方法二:用兩個字段去定位對象不用去建立多個外鍵關係 class Coupon(models.Model): """ id title table_id object_id(對應表對應對象的ID) 1 麪包九五折 1 1 2 香蕉滿10元減5元 2 2 """ title = models.CharField(max_length=32) table = models.ForeignKey(to="Table") # 與table表創建外鍵關係 object_id = models.IntegerField() # 由object_id定位到表中的某一個對象,但沒有創建外鍵關係 class Table(models.Model): """ id app_name table_name 1 demo food 2 demo fruit """ app_name = models.CharField(max_length=32) table_name = models.CharField(max_length=32)
最好的方式是,只有當你須要對某個對象或模型進行評論時,才建立pricepolicy與那個模型的關係。示例以下所示:
# 方法三:基於ContentTypes建立表結構 class Coupon(models.Model): title = models.CharField(max_length=32) # 優惠券名稱 # 第一步:與ContentType表綁定外鍵關係 content_type = models.ForeignKey(to=ContentType, on_delete=None) # 第二步:創建對象id object_id = models.IntegerField() # 第三步:content_type和object_id綁定外鍵關係 content_object = GenericForeignKey("content_type", "object_id")
學位課程、專題課程、價格策略基於django contenttypes建立表結構以下所示:
from django.db import models from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation class DegreeCourse(models.Model): """學位課程""" name = models.CharField(max_length=128, unique=True) course_img = models.CharField(max_length=255, verbose_name="縮略圖") brief = models.TextField(verbose_name="學位課程簡介", ) class Course(models.Model): """專題課程""" name = models.CharField(max_length=128, unique=True) course_img = models.CharField(max_length=255) # 不會在數據庫生成列,只用於幫助你進行查詢 policy_list = GenericRelation("PricePolicy") class PricePolicy(models.Model): """價格策略表""" content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) # 關聯course or degree_course object_id = models.PositiveIntegerField() # 正整數PositiveInteger # GenericForeignKey不會在數據庫生成列,只用於幫助你進行添加和查詢 content_object = GenericForeignKey('content_type', 'object_id') # 將兩個字段放在這個對象中 # 週期 valid_period_choices = ( (1, '1天'), (3, '3天'), (7, '1周'), (14, '2周'), (30, '1個月'), (60, '2個月'), (90, '3個月'), (180, '6個月'), (210, '12個月'), (540, '18個月'), (720, '24個月'), ) # 價格 valid_period = models.SmallIntegerField(choices=valid_period_choices) price = models.FloatField()
Django ContentType提供了一種GenericForeignKey的類型,經過這種類型能夠指定content_object。
GenericForeignKey不會在數據庫生成列,只用於幫助你進行添加和查詢。
GenericRelation不會在數據庫生成列,只用於幫助你進行查詢。
content_type: 內容類型,表明了模型的名字(好比Course,DegreeCourse)
object_id: 傳入對象的id
content_object: 傳入的實例化對象,其包含兩個屬性content_type和object_id。
from django.shortcuts import render, HttpResponse from app01 import models from django.contrib.contenttypes.models import ContentType def test(request):
# 方法一: models.PricePolicy.objects.create( valid_period=7, price=6.6, content_type=ContentType.objects.get(model="course"), object_id=1 ) # 方法二: models.PricePolicy.objects.create( valid_period=14, price=9.9, content_object=models.Course.objects.get(id=1) # 'content_type', 'object_id' ) return HttpResponse("...")
訪問http://127.0.0.1:8000/test/ 後,查看價格策略表保存的數據:
這裏以查看管理課程名稱爲例:
from django.shortcuts import render, HttpResponse from app01 import models from django.contrib.contenttypes.models import ContentType def test(request): price = models.PricePolicy.objects.get(id=2) print(price.content_object.name) # 21天入門python 即自動幫忙找到對應的對象 return HttpResponse("...")
注意這裏須要利用到GenericRelation。
from django.shortcuts import render, HttpResponse from app01 import models from django.contrib.contenttypes.models import ContentType def test(request): obj = models.Course.objects.get(id=1) print(obj.policy_list.all()) # <QuerySet [<PricePolicy: PricePolicy object (1)>, <PricePolicy: PricePolicy object (2)>]> return HttpResponse("...")
查詢結果是一個QuerySet對象,若是想讓查詢結果更加清楚:
from django.shortcuts import render, HttpResponse from app01 import models from django.contrib.contenttypes.models import ContentType def test(request): obj = models.Course.objects.get(id=1) for item in obj.policy_list.all(): print(item.id, item.valid_period, item.price) """ 1 7 6.6 2 14 9.9 """ return HttpResponse("...")
若是一張表與N張表動態地要建立Foreign Key關係,若是建立 Foreign key 將生成不少列,這樣不少都是空的,形成嚴重浪費空間。只要是一張表要和多張表創建外鍵關係的狀況,均可以考慮使用django的ContentType組件來幫助實現,以簡化表結構的設計。
ContentType組件的做用:能夠經過兩個字段(GenericForeignKey, GenericRelation),在保證列數不變的狀況下,讓一張表和N張表作Foreign Key關係。