最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 一文读懂Django contenttypes框架

    正文概述    2020-04-14   391

    一文读懂Django contenttypes框架

    什么是Django ContentTypes?

    Django ContentTypes是由Django框架提供的一个核心功能,它对当前项目中所有基于Django驱动的model提供了更高层次的抽象接口。

    然而,对于Django ContentTypes不熟悉的人来说,上面这句话说了跟没说一样,因此,笔者将一步一步解释Django ContentTypes在Django框架中做了什么,以及如何使用Django ContentTypes。

    当然,如果对于ContentTypes有了初步了解而只是不了解它的应用场景,可以直接查阅以下这两个链接:

    Django official documentation:The contenttypes framework

    stackoverflow: How exactly do Django content types work?

    Django ContentTypes做了什么?

    当使用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是在django.contrib.auth之后,这是因为auth中的permission系统是根据contenttypes来实现的。

    笔者紧接着查阅了一下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就是一个简单的django model,而且它在数据库中的表的名字为django_content_type。

    有经验的Django开发者对于这个表的名字一般都不会陌生,在第一次对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属性)。

    当然,django_content_type并不只是记录属性这么简单,在一开始的时候笔者就提及了contenttypes是对model的一次封装,因此可以通过contenttypes动态的访问model类型,而不需要每次import具体的model类型。

    ·ContentType实例提供的接口

        ·ContentType.model_class()

            ·获取当前ContentType类型所代表的模型类

        ·ContentType.get_object_for_this_type()

            ·使用当前ContentType类型所代表的模型类做一次get查询

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

        ·ContentType.objects.get_for_id()

            ·通过id寻找ContentType类型,这个跟传统的get方法的区别就是它跟get_for_model共享一个缓存,因此更为推荐。

        ·ContentType.objects.get_for_model()

            ·通过model或者model的实例来寻找ContentType类型

    Django ContentTypes的使用场景

    Permission对ContentType的使用

    在之前,笔者简单地提及了auth中Permission有涉及到对ContentType的使用,下面来看一下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模型中设置了一个对ContentType的外键,这意味着每一个Permission的实例都具有关于一个ContentType的id作为外键,而ContentType的id恰恰代表着一个Model。

    回想Permission模型在初始化的时候发生了什么,它为每个模型设置了三个权限,分别是add,change以及delete,那么它是如何跟每个模型联系起来的呢?就是通过一个到ContentType的外键。

    大家可以看一下Permission表,其中第二行就是content_type,然后将主键于django_content_type对比:

    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表达了对一个model的权限操作。

    ContentType的通用类型

    笔者将引用在顶部的stackoverflow中回答的例子讲述对通用类型的理解。

    假设以下的应用场景:

    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中没有null的选项,笔者觉得回答者真正要表达的是Comment是分别和Picture或者Post中其中一个对应即可,一个Comment并不既需要Post又需要Picture才能建立,可能是回答者写错没注意的缘故。

    当笔者对以上model进行migrate之后,发现Comment表中的foreignkey是可以被设置为null的。

    那么,如何通过Contenttype框架对以上代码进行改进呢?

    ContentType提供了一种GenericForeignKey的类型,通过这种类型可以实现在Comment对其余所有model的外键关系。

    修改后的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()

    在这里,通过使用一个content_type属性代替了实际的model(如Post,Picture),而object_id则代表了实际model中的一个实例的主键,其中,content_type和object_id的字段命名都是作为字符串参数传进content_object的。即:

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

    笔者先准备一些测试用的数据:

    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中创建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访问Comment的查询,通过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,删除了一个实例,在Comment中所有的相关实例也会被删除,GenericForeignKey不支持设置on_delete参数。

    因此,如果对级联删除不满意的话就不要设置GenericRelation。

    众多python培训视频,尽在python学习网,欢迎在线学习!


    起源地下载网 » 一文读懂Django contenttypes框架

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元