Django contenttypes的作用

 

1.django.contrib.contenttypes

Django创建项目后,在settings.py中默认加载了以下几个app:

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

其中有个app为django.contrib.contenttypes,在使用了python manage.py makemigrations 和 python manage.py migrate命令后,这个app会在数据库创建表 django_content_type,数据表字段如下:

 

django_content_type表存储了用户所提交的所有model名称与model所在的app名称

其中的model字段为用户定义的模型类的名称,app_label为该模型所在app名称

2.实例分析

那么这个表的意义在哪?我们可以用这个表来干嘛?引用网上诸多博文使用的例子,现在有四个model,我们自己创建的Post,Picture和Comment,加上django原生的User。

django原生的User model即用户表

Comment为用户提交的评论表

Picture为存储图片信息的表

Post为存储文章信息的表

 

场景如下:用户可以对图片或者文章提交评论,那么Comment中首先要存储的是,该评论对应的用户,再就是该评论是针对哪张图片或者哪篇文章的评论,代码如下:

from django.db import models
from django.contrib.auth.models import User


class Post(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    body = models.TextField(blank=True)

class Picture(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    body = models.TextField(blank=True)

class Comment(models.Model):
    author = models.ForeignKey(to=User, on_delete=models.CASCADE)
    body = models.TextField(blank=True)
    pic = models.ForeignKey(to=Picture, on_delete=models.CASCADE, null=True)
    post = models.ForeignKey(to=Post, on_delete=models.CASCADE, null=True)

为了建立Comment与Post或者Picture的关系,我们就需要创建外键,如果该Comment与Picture有关联,那么pic字段是有值的,而其余的关联字段为null。这样实现的缺点是,每增加一种被评论的类型,在Comment model中就需要多新增一个外键,扩展性极差。

3.使用contenttypes来解决问题

如果使用contenttypes就不需要再使用多个Foreignkey,因为在django_content_type表已经存储了app名和model名,所以我们只需要将我们创建的模型与django_content_type表关联起来,然后再存储一个实例 id 即可准确定位到某个app中某个模型的具体实例。代码如下:

from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import User
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation


class Post(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    body = models.TextField(blank=True)
    comments = GenericRelation('Comment')  # 使用GenericRelation可以建立该类与Comment类的反向关联,
                                           # 那么可以通过该类的实例来创建对应的comment

class Picture(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    body = models.TextField(blank=True)
    comments = GenericRelation('Comment')

class Comment(models.Model):
    #  user_name = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    author = models.ForeignKey(to=User, on_delete=models.CASCADE)

    body = models.TextField(blank=True)

    # step1
    # ForeignKey为外键,即在Comment类中,content_type对应了django_content_type表中的某个对象
    content_type = models.ForeignKey(to=ContentType, on_delete=models.CASCADE)  # 与数据库中的django_content_type表关联起来

    # step2
    object_id = models.PositiveIntegerField()  # 正整数类型,在被关联的表中的实例id,以此来定位具体的实例

    # step3
    content_object = GenericForeignKey()  # 根据content_type和object_id来指向一个模型中的具体实例

 

首先看Comment这个模型,与contenttypes相关的就三个字段:content_tpye   object_id    content_object

content_type字段为外键,指向ContentType这个模型,也就是上面提到的django_content_type表

object_id为一个整数,存储了实例id,实例id不理解可以看下面的数据表结构分析

content_object为GenericForeignKey类型,主要作用是根据content_type字段和object_id字段来定位某个模型中具体某个实例

所以实际上在使用GenericForeignKey()函数时需要传入两个参数,即content_type和object_id字段的名字,注意是名字不是value。但是默认在该函数的构造函数中已经赋予了默认值,即"content_type"和"object_id",所以如果我们定义的变量名为这两个,那么我们可以省略该参数。该字段不会存储到数据库中,在查询的时候ORM会用到。

GenericForeignKey构造函数如下:

def __init__(self, ct_field='content_type', fk_field='object_id', for_concrete_model=True):
    self.ct_field = ct_field
    self.fk_field = fk_field
    self.for_concrete_model = for_concrete_model
    self.editable = False
    self.rel = None
    self.column = None

 

将模型同步到数据库,并创建几个测试数据,使用manage.py自带的shell可以轻松的创建测试数据。

python manage.py shell

from app1.models import Post, Picture, Comment  # app1即为你自己所创建的app名
from django.contrib.auth.models import User  # django原生用户模型
user = User.objects.get(username='admin-x')
post = Post.objects.create(author=user, body='admin-x post test')
picture = Picture.objects.create(author=user, body='admin-x pic test')

pic = Picture.objects.get(id=1) # 获取id为1的pic对象
c1 = Comment.objects.create(author=user, body='test', content_object=pic) # 创建一个评论并与上述的pic对象关联起来

 

还可以注意到,在Post和Picture模型中我们定义了comments = GenericRelation('Comment')

GenericRelation的作用是建立该model与Comment的反向关联,我们这样就可以通过Post实例或者Picture实例直接查询属于该实例的所有评论,或者创建一个属于该实例的新评论。

pic = Picture.objects.get(id=1) # 获取id为1的pic对象

pic.comments.create(author=user, body='test02')  # 创建一个属于pic对象的评论

pic.comments.all()  # 获取属于pic对象的所有评论

 

数据表结构分析,这样应该一目了然了吧

实际上在数据表中,根据content_type_id就可以在django_content_type这个表中定位到哪个app的哪个model,然后根据object_id定位到该model中具体实例。

 

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值