1. 程式人生 > >[Django學習] Django基礎(10)_ContentType學習總結

[Django學習] Django基礎(10)_ContentType學習總結

classname 使用 strong eric ref 現在 相關 查找 pos

一. 什麽是ContentTypes

  Django ContentTypes是由Django框架提供的一個核心功能。Django ContentTypes是一個記錄了項目中所有model元數據的表,表中一條記錄對應著一個存在的model。

  當使用django-admin初始化一個django項目的時候,可以看到在默認的INSTALL_APPS已經包含了django.contrib.contenttypes:  

INSTALLED_APPS = [
    ‘django.contrib.admin‘,
    ‘django.contrib.auth‘,
    ‘django.contrib.contenttypes‘,
‘django.contrib.sessions‘, ‘django.contrib.messages‘, ‘django.contrib.staticfiles‘, ]

  django.contrib.contenttypes.models文件:

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

  在第一次對Django的model進行migrate之後,就可以發現在數據庫中出現了一張默認生成的名為django_content_type的表。 如果沒有建立任何的model,默認django_content_type是這樣的:

sqlite> select * from django_content_type;
1|admin|logentry
2|auth|group
3|auth|user
4|auth|permission
5|contenttypes|contenttype
6|sessions|session

  django_content_type記錄了當前的Django項目中所有model所屬的app(即app_label屬性)以及model的名字(即model屬性)。 所以可以通過一個ContentType表的id和一個具體表中的id找到任何記錄,即先通過ContenType表的id可以得到某個model,再通過model的id得到具體的對象。

二. ContentType實例的基本方法

1. ContentType.get_object_for_this_type(**kwargs)

  為ContentType表示的模型獲取一組有效的查找參數,並執行get()查找該模型,返回相應的對象。

2. ContentType.model_class()
  返回由這個ContentType實例表示的模型類。
>>> from django.contrib.contenttypes.models import ContentType
>>> user_type = ContentType.objects.get(app_label="auth", model="user")
<ContentType: user>
>>>
>>> user_type.model_class()
<class ‘django.contrib.auth.models.User‘>
>>>
>>> user_type.get_object_for_this_type(username=‘Guido‘)
<User: Guido>

  使用這些方法,您可以編寫高級通用代碼執行查詢任何安裝模型——而不是import和使用一個特定的模型類;再運行時,您可以傳遞app_label和model到一個ContentType的查詢中,然後就可以使用查詢結果中的model class或檢索model中的對象。

  您可以將另一個模型與ContentType關聯起來,將它的實例與特定的模型類綁定在一起,並使用這些方法訪問這些模型類。

三. 自定義管理器ContentTypeManager

1. get_for_id(id)

  通過id來查詢ContentType

3. get_for_model(model, for_concrete_model=True)

  獲取模型類或模型實例,並返回表示該模型的ContentType實例

4. get_for_models(*models, for_concrete_models=True)

  獲取模型類的可變數量,並返回一個字典,該字典將模型類映射到表示它們的ContentType實例。

5. get_by_natural_key(app_label, model)

  返回由給定的應用程序標簽和模型名稱唯一標識的ContentType實例。

>>> ct=ContentType.objects.all()
>>> ct
<QuerySet [<ContentType: log entry>, <ContentType: permission>, <ContentType: group>, <ContentType: user>, 
<ContentType: content type>, <ContentType: session>, <ContentType: blog>, <ContentType: blog type>, <ContentType: readnum>, 
<ContentType: read num>, <ContentType: read detail num>]>
>>>
>>> ContentType.objects.get_for_id(1)
<ContentType: log entry>
>>>
>>> from blog.models import Blog
>>> from blog.models import BlogType
>>> ContentType.objects.get_for_model(Blog)
<ContentType: blog>
>>> ContentType.objects.get_for_models(Blog,BlogType)
{<class ‘blog.models.Blog‘>: <ContentType: blog>, <class ‘blog.models.BlogType‘>: <ContentType: blog type>}
>>> ContentType.objects.get_by_natural_key(app_label=‘blogstatistics‘,model=‘readnum‘)
<ContentType: read num>

四. Generic relations

  從某個模型中添加外鍵到ContentType可以使您的模型有效地綁定到另一個模型類,從而使用ContentType來啟用模型之間的真正泛型(有時稱為“多態”)關系。

from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models

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‘, ‘object_id‘)

    def __str__(self):
        return self.tag

  普通的ForeignKey只能“指向”另一個模型,這意味著如果TaggedItem模型使用了ForeignKey,那麽它必須選擇一個特定模型來存儲Tag。contenttypes應用程序提供了一個特殊的字段類型(GenericForeignKey),它並允許與任何模型的關系。

  一個標準的GenericForeignKey由三部分組成:

  • 在model中定義ForeignKey字段,並關聯到ContentType表。通常這個字段命名為“content_type”
  • 在model中定義PositiveIntegerField字段,用來存儲關聯表中的主鍵。通常這個字段命名為“object_id”
  • 在model中定義GenericForeignKey字段,傳入上述兩個字段的名字。

  這將使API與普通的ForeignKey相似;每個TaggedItem都有一個content_object字段,該字段返回與之相關的對象,您也可以將其分配給該字段,或者在創建TaggedItem時使用: 

五. Reverse generic relations

  如果您知道您將最經常使用哪些模型,您還可以添加一個“反向”泛型關系來啟用一個額外的API。

from django.contrib.contenttypes.fields import GenericRelation
from django.db import models

class Bookmark(models.Model):
    url = models.URLField()
    tags = GenericRelation(TaggedItem) #不會在數據庫中創建字段

  Bookmark實例將每個實例都有一個Tags屬性,可以用來檢索它們關聯的taggeditem:

>>> 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>]>

From: Django文檔

[Django學習] Django基礎(10)_ContentType學習總結