Django 之 contenttypes 框架詳解
標簽: Django學習專欄
Django 高級實戰編程
視頻分享地址:
https://study.163.com/course/introduction/1209407824.htm?share=2&shareId=400000000535031
使用上面的鏈接地址進入,聯系QQ 1064468942 可獲得 部分 學費退還 喔, 實際支出 將少于 所標識的價格
contenttypes框架
Django除了我們常見的admin
、auth
、session
等contrib框架,還包含一個contenttypes
框架,它可以跟蹤Django項目中安裝的所有模型(model
),為我們提供更高級的模型接口。默認情況下,它已經在settings中了,如果沒有,請手動添加:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes', # 看這里!!!!
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
平時還是盡量啟用contenttypes框架,因為Django的一些其它框架依賴它:
- Django的admin框架用它來記錄添加或更改對象的歷史記錄。
- Django的auth認證框架用它將用戶權限綁定到指定的模型。
contenttypes不是中間件,不是視圖,也不是模板,而是一些"額外的數據表"!所以,在使用它們之前,你需要執行 makemigrations 和 migrate 操作,為contenttypes框架創建它需要的數據表,用于保存特定的數據。這張表通常叫做django_content_type
,讓我們看看它在數據庫中的存在方式:
而表的結構形式則如下圖所示:
一共三個字段:
- id:表的主鍵,沒什么好說的
- app_label:模型所屬的app的名字
- model:具體對應的模型的名字。
表中的每一條記錄,其實就是Django項目中某個app下面的某個model模型。
概述
contenttypes框架的核心是 ContentType
模型,它位于django.contrib.contenttypes.models.ContentType
。ContentType實例表示和存儲Django項目中安裝的所有模型的信息。每當你的Django項目中創建了新的模型,會在ContentType
表中自動添加一條新的對應的記錄。
ContentType
模型的實例具有一系列方法,用于返回它們所記錄的模型類以及從這些模型查詢對象。ContentType
還有一個自定義的管理器,用于進行ContentType
實例相關的ORM操作。
ContentType模型
每個 ContentType
實例都有兩個字段(除了隱含的主鍵id)。
app_label
: 模型所屬app的名稱。通過模型的app_label
屬性自動獲取,僅包括Python導入路徑的最后部分。例如對于django.contrib.contenttypes
模型,自動獲取的app_label
就是最后的contenttypes
字符串部分。- model:模型類的名稱。(小寫)
此外,ContentType實例還有一個name
屬性,保存了ContentType
的人類可讀名稱。由模型的verbose_name
屬性值自動獲取。
例如,對于django.contrib.sites.models.Site
這個模型:
app_label
將被設置為’sites’(django.contrib.sites
的最后一部分)。model
將被設置為’site’(小寫)。
ContentType
的實例方法
每個ContentType
實例都有一些方法,允許你從ContentType
實例獲取它所對應的模型,或者從該模型中檢索對象:
ContentType.get_object_for_this_type(**kwargs)
提供一系列合法的參數,在對應的模型中,執行一個get()查詢操作,并返回相應的結果。
ContentType.model_class()
返回當前ContentType
實例表示的模型類 。
例如,我們可以在 ContentType
表中查詢auth
的 User
模型對應的那條ContentType
記錄:
>>> from django.contrib.contenttypes.models import ContentType
>>> user_type = ContentType.objects.get(app_label='auth', model='user') # 獲取到一條記錄
>>> user_type # 注意,這是contenttype的實例對象,不是User表的
<ContentType: user>
然后,就可以使用它來查詢特定的 User
,或者訪問User
模型類:
>>> user_type.model_class() # 獲取User類
<class 'django.contrib.auth.models.User'>
>>> user_type.get_object_for_this_type(username='Guido') # 獲取某個User表的實例
<User: Guido>
一起使用 get_object_for_this_type()
和model_class()
方法可以實現兩個特別重要的功能:
- 使用這些方法,你可以編寫對模型執行查詢操作的高級通用代碼 。不需要導入和使用某個特定模型類,只需要在運行時將
app_label
和model
參數傳入ContentType
的ORM方法,然后使用model_class()
方法就可以調用對應模型的ORM操作了。 - 還可以將另一個模型與
ContentType
關聯起來,作為將它的實例與特定模型類綁定的方法,并使用這些方法來訪問這些模型類。
不好理解,沒關系,往后接著看。
ContentType
還有一個自定義管理器,也就是ContentTypeManager
。它有下面的方法:
clear_cache()
:用于清除內部緩存 。一般不需要手動調用它,Django會在需要時自動調用它。get_for_id(id)
:通過id值查詢一個ContentType
實例。比ContentType.objects.get(pk=id)
的方式更優。get_for_model(model,for_concrete_model = True)
:獲取模型類或模型的實例,并返回表示該模型的ContentType
實例。設置參數for_concrete_model=False
允許獲取代理模型的ContentType。get_for_models(*model,for_concrete_model = True)
: 獲取可變數量的模型類,并返回模型類映射ContentType
實例的字典。get_by_natural_key(app_label, model)
:給定app標簽和模型名稱,返回唯一匹配的ContentType實例。
當你只想使用 ContentType
,但不想去獲取模型的元數據以執行手動查找時,get_for_model()
方法特別有用 :
>>> from django.contrib.auth.models import User
>>> ContentType.objects.get_for_model(User) # 提供model的名字,查詢出對應的contenttype實例。
<ContentType: user>
反向通用關系GenericRelation字段
既然前面使用GenericForeignKey字段可以幫我們正向查詢關聯的對象,那么就必然有一個對應的反向關聯類型,也就是GenericRelation
字段類型。
使用它可以幫助我們從關聯的對象反向查詢對象本身,也就是ORM中的反向關聯。
同樣的,這個字段也不會對數據表產生任何影響,僅僅用于ORM操作!
比如下面的例子,我要從書簽去反向查詢它所對應的標簽:
from django.contrib.contenttypes.fields import GenericRelation # 導入
from django.db import models
class Bookmark(models.Model):
url = models.URLField()
tags = GenericRelation(TaggedItem) # 看這里!!!!!!!!!!!!!
每個Bookmark
實例都有一個tags
字段,可以用來檢索它關聯的TaggedItems
對象:
>>> b = Bookmark(url='https://www.djangoproject.com/')
>>> b.save()
>>> t1 = TaggedItem(content_object=b, tag='django')
>>> t1.save()
>>> t2 = TaggedItem(content_object=b, tag='python')
>>> t2.save()
>>> b.tags.all() # 看這句!!!!!!!!!!!!
<QuerySet [<TaggedItem: django>, <TaggedItem: python>]>
上面的操作涉及到一個ORM新手非常容易犯的錯誤,那就是外鍵這種一對多的關系,究竟要怎么寫:
-
首先,要想明白是一個標簽可以對應多個書簽,還是一個書簽可以對應多個標簽?
-
這里定義的是一個書簽bookmark可以對應多個標簽tag,書簽是‘一’方,標簽是‘多’方。
-
所以,
ForeignKey
字段要寫在多的一方,也就是TaggedItem
模型中。 -
那么對于Bookmark模型對象,去查詢關聯的
tag
對象,就是屬于從一到多的反向查詢。 -
這也就是上面我們最后為什么是使用
b.tags.all()
這種查詢方法,而不是直接b.tags
!
這里請你自己做一件事,它有助于你理解為什么在ContentType框架中建議使用GenericForeignKey
和GenericRelation
這種關聯字段:
請用Django原生的ORM方法,執行上面的正向查詢和反向查詢操作!并與例子中的操作進行對比!
如果為GenericRelation
字段提供一個 related_query_name
參數值,比如下面的例子:
tags = GenericRelation(TaggedItem, related_query_name='bookmark')
那么將可以從TaggedItem對象過濾查詢關聯的BookMark對象,如下所示:
>>> # 查找所有屬于特定書簽模型的標簽,這些書簽必須包含`django`字符串。
>>> TaggedItem.objects.filter(bookmark__url__contains='django')
<QuerySet [<TaggedItem: django>, <TaggedItem: python>]>
當然,如果你不添加related_query_name
參數,也可以手動執行相同類型的查找:
>>> bookmarks = Bookmark.objects.filter(url__contains='django')
>>> bookmark_type = ContentType.objects.get_for_model(Bookmark)
>>> TaggedItem.objects.filter(content_type__pk=bookmark_type.id, object_id__in=bookmarks)
<QuerySet [<TaggedItem: django>, <TaggedItem: python>]>
比較一下,三行代碼和一行代碼的區別!
注意:GenericForeignKey
和GenericRelation
字段是匹配的, 如果你在定義GenericForeignKey
的時候使用了另外的content-type
和object-id
名字,那么在GenericRelation
定義中,你必須做同樣的變化。
例如,如果TaggedItem
模型使用content_type_fk
和 object_primary_key
創建content_object
字段,像下面這樣:
...
class TaggedItem(models.Model):
tag = models.SlugField()
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type_fk', 'object_primary_key') #看這里
...
那么在GenericRelation
中,你需要像這樣定義:
tags = GenericRelation(
TaggedItem,
content_type_field='content_type_fk',
object_id_field='object_primary_key',
)
另外還要注意:如果你刪除了一個具有GenericRelation
字段的對象,則任何具有GenericForeignKey
字段指向該對象的關聯對象也將被刪除。在上面的示例中,這意味著如果刪除了某個Bookmark對象,則會同時刪除指向該對象的任何TaggedItem對象。
不同于普通的ForeignKey
字段, GenericForeignKey
字段不接受on_delete
參數。如果需要,可以重寫 pre_delete
方法,不細說。
其它
Django的數據庫聚合API可用于 GenericRelation
。例如,你可以找出所有書簽的標簽數量:
>>> Bookmark.objects.aggregate(Count('tags'))
{'tags__count': 3}
除以上內容外,contenttypes框架還提供了django.contrib.contenttypes.forms
模塊用于處理表單相關內容,django.contrib.contenttypes.admin
模塊用于處理管理后臺相關內容,感興趣的可以自行查閱相關資料。
智能推薦
django學習之框架初識
1>django的下載和目錄介紹 安裝成功之后,pycharm再新建項目,就有Django的選項了,選擇Django,填寫如下, 之后會自動生成一堆的路徑和文件,如下 項目文件 manage.py ----- Django項目里面的工具,通過它可以調用django shell和數據庫等。 &nb...
django框架之簡單分頁
分頁功能的實現 models.py 分別往這幾個表中添加數據 views.py page目錄下的index.html views.py 顯示每一頁的詳情頁 page中的show.html...
Hue之Django框架簡介
背景簡介 Django是一個由Python語言編寫的開源的Web應用框架。Django框架本身是基于Mode(模型)+View(視圖)+Controller(控制器)的設計模式,準確來說應該是MVT(T代表Template),這種模式簡化了程序的修改和擴展,提高了代碼的復用率。Django實現了將前后端進行解耦,提高了開發效率,使得開發人員只需協商好兩者之間的接口就可以獨立開發自己的部分,避免了重...
Django詳解之models操作
更多字段 更多參數 狀態 外鍵關系: 3.開始創建數據 創建數據的時候有兩種方式: 第一種方式: 第二種方式: 在views.py中寫入數據: 運行Django 項目訪問指定文件創建數據: 4、了不起的雙下劃線之外鍵正向查找和基本操作 基本操作 進階操作(雙下劃線) 查 單表查詢: 結果: 獲取查詢結果的類型: ...
Python篇-Django框架詳解
一 : 科普一分鐘 Django 這個Web框架學習過的人無不知曉它的強大. Django是一個開放源代碼的Web應用框架,由Python寫成,采用了MT'V的框架模式.即Model,View,Template組成.許多成功的網站和APP都基于Django.說到底,其實Django內部就是對 Socket 連接的強大封裝. Django.jpg 二:Django配置和創建 創建 在這...
猜你喜歡
Django之DRF框架學習(Django REST framework)—— 初識DRF
DRF(Django REST framework) DRF的特點 提供了定義序列化器Serializer的方法,可以快速根據 Django ORM或者其他庫自動序列化/反序列化; 提供了豐富的類視圖 Mixin擴展類 簡化視圖的編寫; 豐富的定制層級: 函數視圖 類試圖 視圖集合到自動生成API, 滿足各種需要。 多種身份認證和權限認證方式的支持 內置限流系統; 直觀的 API web 界面 可...
freemarker + ItextRender 根據模板生成PDF文件
1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...
電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!
Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...
requests實現全自動PPT模板
http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...