Django ContentTypes组件

前言:

            contenttypes不是中间件,不是视图,也不是模板,而是一些"额外的数据表"!所以,在使用它们之前,你需要执行makemigrationsmigrate操作,为contenttypes框架创建它需要的数据表,用于保存特定的数据。这张通常叫做django_content_type

django_content_type表字段说明:

  • 该表一共有三个字段
  • id:表的主键
  • app_label:模型所属的app的名字:即你在settings.py文件的INSTALLED_APPS中注册的应用
  • model:具体对应的模型的名字

ContentType的实例方法

  1. ContentType.model_class() :获取模型类
  2. ContentType.get_object_for_this_type(**kwargs):根据模型类的字段获取具体实例对象

示例:

  1. 首先根据应用(app)名,应用下的模型名,获取一个ContentType对象
from django.contrib.contenttypes.models import ContentType
article_type = ContentType.objects.get(app_label='literature', model='article') # 获取到一条记录
  1. 使用它来查询 Article模型类,或者访问具体的Article实例对象
article_class = article_type.model_class()  # 获取到Article模型类

# 从literature应用下的Article模型对象中获取到 title=谁的青春不迷茫 的模型对象实例
article_obj = article_type.get_object_for_this_type(title='谁的青春不迷茫')  

小结:

            上述示例中我们要查找Article模型对象的实例,但我们并未导入该模型对象,所以,我们可以通过ContentType组件直接获取到任何一个模型的实例对象,而不用通过导入该模型对象,来通过它查询出具体实例。当然这不是重点,正常情况下我们也没必要这么做,因为这样增加了我们的工作量,在此我只是为了说明ContentType组件可以做很多事情,接下来我们继续往下看。

ContentType配合GenericForeignKey的使用:

            具体情况如下:我们现在有一个综合类网站,上面有视频文章美术…,而每一个应用模型都要让用户可以对其评论,我们应该怎么做?

方式一:

            可以通过每个应用模型都单独建一张评论表,与对应应用模型外键关联,但这样做数据库表会显得很冗余,如果只有一两个对象需要评论功能还好,但要评论的对象有上百个,我们依然建对应数量的评论表吗?显然这种方法不合适。

方式二:
            建一张评论表,然后将所有带有评论的应用模型都关联到该评论表,即该评论表存在很多的外键,为了确保互不影响,这些外键必须都默认允许为空,种方式不但要写重复的代码,而且效率低,安全差,也不利于后期维护。
在这里插入图片描述

方式三:
            当你需要对某个对象或模型进行评论时,才创建comment与那个模型的关系。这时你就需要使用django contenttypes了。
在这里插入图片描述

  • app_label:应用名称
  • 根据content_type_id字段可以在contenttype表中确定模型对象
  • 根据object_id可以确定该模型对象的具体实例

示例讲解:

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


class Article(models.Model):
    title = models.CharField(max_length=36)
    content = models.TextField(blank=True)

    def __str__(self):
        return 'article: {0}'.format(self.title)


class Video(models.Model):
    name = models.CharField(max_length=36)
    url = models.URLField()

    def __str__(self):
        return 'article: {0}'.format(self.name)


class Comment(models.Model):
    object_ct = models.ForeignKey(ContentType, on_delete=models.CASCADE)  # 记录关联对象的类型
    object_id = models.PositiveIntegerField()  # 记录关联对象的主键

    # 具体实例对象:比如:id为2的文章对象
    object = GenericForeignKey('content_type', 'object_id')

    text = models.TextField()  # 评论内容

    def __str__(self):
        return 'comment: {0}'.format(self.text)


给id=2的文章插入一条评论

from django.shortcuts import get_object_or_404

article = get_object_or_404(Article, pk=2)
comment = Comment(object=article, text='行云流水,妙趣横生')
comment.save()

ContentType还有一个自定义管理器,也就是ContentTypeManager。它有下面的方法:

  • clear_cache():用于清除内部缓存 。一般不需要手动调用它,Django会在需要时自动调用它
  • get_for_id(id):通过id值查询一个ContentType实例。比ContentType.objects.get(pk=id)的方式更优
  • get_for_model(model,for_concrete_model = True):获取模型类或模型的实例,并返回表示该模型的ContentType 实例。设置参数for_concrete_model=False允许获取代理模型的ContentType
  • get_for_models(*model,for_concrete_model = True): 获取可变数量的模型类,并返回模型类映射ContentType实例的字典
  • get_by_natural_key(app_label, model):给定app标签和模型名称,返回唯一匹配的ContentType实例

GenericForeignKey:通用关系

            普通的ForeignKey字段只能指向另外唯一一个模型,这意味着如果Comment模型使用了ForeignKey,则必须选择一个且只有一个模型来存储标签。contenttypes框架提供了一个特殊的字段类型(GenericForeignKey),它可以解决这个问题,并允许关联任何模型。

正向查找 :

需求:根据评论找对象,即这条评论是对哪个对象的评论
conmmnet = Comment.objects.first()
obj = conmmnet.object

GenericRelation反向通用关系

            既然前面使用GenericForeignKey字段可以帮我们正向查询关联的对象,那么就必然有一个对应的反向关联类型,也就是GenericRelation字段类型。使用它可以帮助我们从关联的对象反向查询对象本身,也就是ORM中的反向关联。

反向查找:

需求一:根据具体文章对象,找出该的所有评论

方式一:直接查找

  • 无法直接从其他对象找到Comment对应的评论集
  • 必须通过ContentType表作为中间跳板
article = Article.objects.first()
article_object_ct = ContentType.objects.get_for_model(Article)  # 找到ContentType文章模型记录
# 通过Comment自身的筛选查询得到
comments = Comment.objects.filter(content_type=article_content_type, object_id=article.id)

方式二:通过反向关系进行查找

  • 可以直接从其他对象找到Comment对应的评论集
  • 拿文章Article模型做演示
from django.db import models
from django.contrib.contenttypes.fields import GenericRelation


class Article(models.Model):
    title = models.CharField(max_length=36)
    content = models.TextField(blank=True)
	
	comments = GenericRelation('Comment')
	
    def __str__(self):
        return 'article: {0}'.format(self.title)
article = Article.objects.first()
comments = article.comments.all()
  • 上述示例反向关系只能通过单个对象反向查询
  • 如果文章对象是一个查询集时则不适用
  • 此时就需要反向引用关系的出现了:related_query_name
需求二:找出文章名包含青春的所有文章的评论

方式一:直接查找

  • 无法直接从其他对象找到Comment对应的评论集
  • 必须通过ContentType表作为中间跳板
# 获取符合查询条件的文章id列表
article_ids = Article.objects.filter(title__contains='青春').values_list('id', flat=True)
# 找到ContentType文章模型记录
article_object_ct = ContentType.objects.get_for_model(Article)  
# 通过Comment自身的筛选查询得到
comments = Comment.objects.filter(content_type=article_content_type, object_id__in=article_ids)

方式二:通过反向引用进行查找

  • 可以直接从其他对象找到Comment对应的评论集
  • 拿文章Article模型做演示
from django.db import models
from django.contrib.contenttypes.fields import GenericRelation


class Article(models.Model):
    title = models.CharField(max_length=36)
    content = models.TextField(blank=True)
	
	comments = GenericRelation('Comment', related_query_name='article')
	
    def __str__(self):
        return 'article: {0}'.format(self.title)
comments = article.objects.filter(article__title__contains='青春')
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值