溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

什么是Django contenttypes框架

發(fā)布時(shí)間:2020-08-25 15:54:47 來(lái)源:億速云 閱讀:131 作者:Leah 欄目:編程語(yǔ)言

什么是Django contenttypes框架?很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來(lái)學(xué)習(xí)下,希望你能有所收獲。

什么是Django ContentTypes?

Django ContentTypes是由Django框架提供的一個(gè)核心功能,它對(duì)當(dāng)前項(xiàng)目中所有基于Django驅(qū)動(dòng)的model提供了更高層次的抽象接口。

然而,對(duì)于Django ContentTypes不熟悉的人來(lái)說(shuō),上面這句話說(shuō)了跟沒(méi)說(shuō)一樣,因此,筆者將一步一步解釋Django ContentTypes在Django框架中做了什么,以及如何使用Django ContentTypes。

當(dāng)然,如果對(duì)于ContentTypes有了初步了解而只是不了解它的應(yīng)用場(chǎng)景,可以直接查閱以下這兩個(gè)鏈接:

Django official documentation:The contenttypes framework

stackoverflow: How exactly do Django content types work?

Django ContentTypes做了什么?

當(dāng)使用django-admin初始化一個(gè)django項(xiàng)目的時(shí)候,可以看到在默認(rèn)的INSTALL_APPS已經(jīng)包含了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是在django.contrib.auth之后,這是因?yàn)閍uth中的permission系統(tǒng)是根據(jù)contenttypes來(lái)實(shí)現(xiàn)的。

筆者緊接著查閱了一下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

大家可以看到ContentType就是一個(gè)簡(jiǎn)單的django model,而且它在數(shù)據(jù)庫(kù)中的表的名字為django_content_type。

有經(jīng)驗(yàn)的Django開(kāi)發(fā)者對(duì)于這個(gè)表的名字一般都不會(huì)陌生,在第一次對(duì)Django的model進(jìn)行migrate之后,就可以發(fā)現(xiàn)在數(shù)據(jù)庫(kù)中出現(xiàn)了一張默認(rèn)生成的名為django_content_type的表。

如果沒(méi)有建立任何的model,默認(rèn)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記錄了當(dāng)前的Django項(xiàng)目中所有model所屬的app(即app_label屬性)以及model的名字(即model屬性)。

當(dāng)然,django_content_type并不只是記錄屬性這么簡(jiǎn)單,在一開(kāi)始的時(shí)候筆者就提及了contenttypes是對(duì)model的一次封裝,因此可以通過(guò)contenttypes動(dòng)態(tài)的訪問(wèn)model類型,而不需要每次import具體的model類型。

·ContentType實(shí)例提供的接口

    ·ContentType.model_class()

        ·獲取當(dāng)前ContentType類型所代表的模型類

    ·ContentType.get_object_for_this_type()

        ·使用當(dāng)前ContentType類型所代表的模型類做一次get查詢

·ContentType管理器(manager)提供的接口

    ·ContentType.objects.get_for_id()

        ·通過(guò)id尋找ContentType類型,這個(gè)跟傳統(tǒng)的get方法的區(qū)別就是它跟get_for_model共享一個(gè)緩存,因此更為推薦。

    ·ContentType.objects.get_for_model()

        ·通過(guò)model或者model的實(shí)例來(lái)尋找ContentType類型

Django ContentTypes的使用場(chǎng)景

Permission對(duì)ContentType的使用

在之前,筆者簡(jiǎn)單地提及了auth中Permission有涉及到對(duì)ContentType的使用,下面來(lái)看一下Permission的model源碼:

class Permission(models.Model):
    """
    The permissions system provides a way to assign permissions to specific
    users and groups of users.
    The permission system is used by the Django admin site, but may also be
    useful in your own code. The Django admin site uses permissions as follows:
        - The "add" permission limits the user's ability to view the "add" form
          and add an object.
        - The "change" permission limits a user's ability to view the change
          list, view the "change" form and change an object.
        - The "delete" permission limits the ability to delete an object.
    Permissions are set globally per type of object, not per specific object
    instance. It is possible to say "Mary may change news stories," but it's
    not currently possible to say "Mary may change news stories, but only the
    ones she created herself" or "Mary may only change news stories that have a
    certain status or publication date."
    Three basic permissions -- add, change and delete -- are automatically
    created for each Django model.
    """
    name = models.CharField(_('name'), max_length=255)
    content_type = models.ForeignKey(ContentType,models.CASCADE,verbose_name=_('content type'),)
    codename = models.CharField(_('codename'), max_length=100)
    objects = PermissionManager()
    class Meta:
        verbose_name = _('permission')
        verbose_name_plural = _('permissions')
        unique_together = (('content_type', 'codename'),)
        ordering = ('content_type__app_label', 'content_type__model','codename')

大家可以看到Permission模型中設(shè)置了一個(gè)對(duì)ContentType的外鍵,這意味著每一個(gè)Permission的實(shí)例都具有關(guān)于一個(gè)ContentType的id作為外鍵,而ContentType的id恰恰代表著一個(gè)Model。

回想Permission模型在初始化的時(shí)候發(fā)生了什么,它為每個(gè)模型設(shè)置了三個(gè)權(quán)限,分別是add,change以及delete,那么它是如何跟每個(gè)模型聯(lián)系起來(lái)的呢?就是通過(guò)一個(gè)到ContentType的外鍵。

大家可以看一下Permission表,其中第二行就是content_type,然后將主鍵于django_content_type對(duì)比:

sqlite> select * from auth_permission;
1|1|add_logentry|Can add log entry
2|1|change_logentry|Can change log entry
3|1|delete_logentry|Can delete log entry
4|2|add_group|Can add group
5|2|change_group|Can change group
6|2|delete_group|Can delete group
7|3|add_user|Can add user
8|3|change_user|Can change user
9|3|delete_user|Can delete user
10|4|add_permission|Can add permission
11|4|change_permission|Can change permission
12|4|delete_permission|Can delete permission
13|5|add_contenttype|Can add content type
14|5|change_contenttype|Can change content type
15|5|delete_contenttype|Can delete content type
16|6|add_session|Can add session
17|6|change_session|Can change session
18|6|delete_session|Can delete session

如此,Permission模型借助ContentType表達(dá)了對(duì)一個(gè)model的權(quán)限操作。

ContentType的通用類型

筆者將引用在頂部的stackoverflow中回答的例子講述對(duì)通用類型的理解。

假設(shè)以下的應(yīng)用場(chǎng)景:

from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Post(models.Model):
    author = models.ForeignKey(User)
    title = models.CharField(max_length=75)
    slug = models.SlugField(unique=True)
    body = models.TextField(blank=True)
class Picture(models.Model):
    author = models.ForeignKey(User)
    image = models.ImageField()
    caption = models.TextField(blank=True)
class Comment(models.Model):
    author = models.ForeignKey(User)
    body = models.TextField(blank=True)
    post = models.ForeignKey(Post, null=True)
    picture = models.ForeignKey(Picture, null=True)

注意筆者這里跟原回答做了一些更改,在原回答中Comment中沒(méi)有null的選項(xiàng),筆者覺(jué)得回答者真正要表達(dá)的是Comment是分別和Picture或者Post中其中一個(gè)對(duì)應(yīng)即可,一個(gè)Comment并不既需要Post又需要Picture才能建立,可能是回答者寫錯(cuò)沒(méi)注意的緣故。

當(dāng)筆者對(duì)以上model進(jìn)行migrate之后,發(fā)現(xiàn)Comment表中的foreignkey是可以被設(shè)置為null的。

那么,如何通過(guò)Contenttype框架對(duì)以上代碼進(jìn)行改進(jìn)呢?

ContentType提供了一種GenericForeignKey的類型,通過(guò)這種類型可以實(shí)現(xiàn)在Comment對(duì)其余所有model的外鍵關(guān)系。

修改后的Comment模型如下:

class Comment(models.Model):
    author = models.ForeignKey(User)
    body = models.TextField(blank=True)
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = fields.GenericForeignKey()

在這里,通過(guò)使用一個(gè)content_type屬性代替了實(shí)際的model(如Post,Picture),而object_id則代表了實(shí)際model中的一個(gè)實(shí)例的主鍵,其中,content_type和object_id的字段命名都是作為字符串參數(shù)傳進(jìn)content_object的。即:

content_object = fields.GenericForeignKey('content_type', 'object_id')

筆者先準(zhǔn)備一些測(cè)試用的數(shù)據(jù):

user = User.objects.create_user(username='user1', password='2333')
post = Post.objects.create(author=user,title='title1',slug=slugify('title1'),body='')
picture = Picture.objects.create(author=user,image="http://www.picture1.com",caption='picture1')

接著在shell中創(chuàng)建Comment:

>>> from foreign.models import Post, Picture, Common
>>> from django.contrib.auth.models import User
>>> user = User.objects.get(username='user1')
>>> post = Post.objects.get(title='title1')
>>> c = Comment.objects.create(author=user, body='', content_object=post)
>>> c
<Comment: Comment object>
>>> c.content_type
<ContentType: post>
>>> c.object_id
1
>>> picture = Picture.objects.get(caption='picuture1')
>>> c = Comment.objects.create(author=user, body='', content_object=picture)
>>> c.content_type
<ContentType: picture>
>>> c.object_id
1

在django中,也提供從諸如Post,Picture訪問(wèn)Comment的查詢,通過(guò)GenericRelation類型。如:

class Post(models.Model):
    author = models.ForeignKey(User)
    title = models.CharField(max_length=75)
    slug = models.SlugField(unique=True)
    body = models.TextField(blank=True)
    comment = GenericRelation('Comment')

值得注意的是,如果在Post中定義了GenericRelation,刪除了一個(gè)實(shí)例,在Comment中所有的相關(guān)實(shí)例也會(huì)被刪除,GenericForeignKey不支持設(shè)置on_delete參數(shù)。

因此,如果對(duì)級(jí)聯(lián)刪除不滿意的話就不要設(shè)置GenericRelation。

看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝您對(duì)億速云的支持。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI