DJANGO的CONTENTTYPES和GENERIC RELATIONS

最近在作幫@Brian作一個小項目,藉助了 Mezzanine 這個Django的CMS系統。在 CMS 裏大量用到了 Django 裏的 Content Type 和 Generic Relations。雖然在Django的官方文檔裏有對此有還算詳細的描述,可是相關的中文資料比較少。因此打算彙總一下,結合個人經驗寫一個小教程,重要的是給出一些例子,而不是乾巴巴看說明。django

假使咱們要寫一個 Django 版本的 WordPress ,要定義的 Model 會有 Post,Page和URL,在是在WP裏最典型的3種內容形式。ide

from django.db import models

class Post(models.Model):
  title = models.CharField(max_length=100)
  pub_date = model.DateTimeField(auto_now_add=True)
  content = models.TextField()

class Url(models.Model):
  title = models.CharField(max_length=100)
  pub_date = models.DateTimeField(auto_now_add=True)
  url = models.URLField(blank=True, verify_exists=True)

這個時候我想在寫一個 Comment 的 Model,由於無論是 Post 或者 URL 均可以容許被評論。若是以前沒有接觸過Generic Relations,真的無所適從。有可能會寫兩個Model,一個Post_comments和一個URL_comments,或者是在Comment的model里加入兩組Foreign Key。函數

class Comment(models.Model):
  title = models.CharField(max_length=100)
  post = models.ForeignKey(Post, black=True, null=True)
  url = models.ForeignKey(Url, black=True, null=True)

估計寫出這樣的Model的話,想自殺的心都有了。引入正題,Generic Relation。咱們但願建立一個Comment的Model適用於全部內容類型,無論是Post,URL或者Page。Generic Relation能幫咱們實現這樣的Model。post

from django.db import models
from django.contrib.contenttypes import generic
from django.contrib.contenttypes.models import ContentType

class Comment(models.Model):
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey('content_type', 'object_id')
    content = models.CharField(max_length=1000)

在 Django 中,有一個記錄了項目中全部 model 元數據的表,就是 ContentType。表中一條記錄便對應着一個存在的model ,那麼咱們只要經過一個元數據表的 id 和一個具體數據表中的 id,即可以找到任何model中的任何記錄。this

Comment 中使用 GenericForeignKey() 來指向其它的 Model 實例。爲了使用它,還須要在 Model 中定義content_type 和 object_id 才能夠。其中 content_typ e來自 ContentType 這個 Model,記錄 Comment 所指向的其餘 Model 實例的名字。 object_id則是表示所指向的Model實例的id。url

實際上根據上面的解釋它只要有 content_type 和 object_id 兩個字段就夠了,不過咱們老是須要親自指定兩個字段的值。而 GenericForeignKey 出現的目的就是要把這個過程給自動化了,只要給 content_object 賦一個對象,就會自動得根據這個對象的元數據 ,給 content_type 和 object_id 賦值了。
GenericForeignKey 的構造函數接受兩個可選參數:
def __init__(self, ct_field=」content_type」, fk_field=」object_id」):
你能夠在構造 GenericForeignKey 時指定另外的字段名稱。spa

a = Post(title='post1')
a.save()
b = Url(title='url1')
b.save()
c = Comment(content_object=a, content="test1")
c.save()
c.content_object

d = Comment(content_object=b, content="test2")
d.save()
d.content_object
Comment.objects.all()

結果是[<Comment: test1>,  <Comment: test2>]翻譯

因爲GenericForeignKey()不是普通的外鍵,若是咱們想查找一個Post下的全部評論,無法用下面的這種方式對象

# This will fail
Comment.objects.filter(content_object=a)
# This will also fail
Comment.objects.get(content_object=a)

須要繞一步,略微複雜blog

a_type = ContentType.objects.get_for_model(a)
Comment.objects.filter(content_type__pk=a_type.id, object_id=a.id)

結果是[<Comment: test1>]

實際上是有辦法讓這個很正常的查詢變得簡單一些,Django 提供了 Reverse generic relations 的機制。
從新改一下 Post 這個 Model

class Post(models.Model):
  title = models.CharField(max_length=100)
  pub_date = model.DateTimeField(auto_now_add=True)
  content = models.TextField()
  comments = generic.GenericRelation(Comment)

這樣咱們就給 Post 這個 Model 添加了一個「逆向」的 generic relationship。每一個 Post 的實例都有了個 comments 的屬性,用於檢索與之有關的 comments。

a = Post(title='test2')
a.save()
c1 = Comment(content_object=a, content='comment1')
c1.save()
c2= Comment(content_object=a, content='comment2')
c2.save()
a.comments.all()

[<Comment: comment1>,  <Comment: comment2>]

這裏有有一段總結,寫的不錯,我就不翻譯了,直接粘貼過來。

A generic relationship is defined by two elements: a foreign key to the ContentType table, to determine the type of the related object, and an ID field, to identify the specific object to link to. Django uses these two elements to provide a content_object pseudo-field which, to the user, works similarly to a real ForeignKey field. And, again just like a ForeignKey, Django can helpfully provide a reverse relationship from the linked model back to the generic one, although you do need to explicitly define this using generic.GenericRelation to make Django aware of it.

Reference

Django 官方文檔 The contenttypes framework

efficient generic relations

Generic Relation在django的使用

Django And Generic Relations

相關文章
相關標籤/搜索