• django 的 ContentType


    Django官网文档 https://docs.djangoproject.com/zh-hans/2.2/ref/contrib/contenttypes/


    ContentType model

    主要是 app_labelmodel 这两个字段,表示哪个 app 下的 model, 定位到表, 源码如下

    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.app_labeled_name
    
        @property
        def name(self):
            model = self.model_class()
            if not model:
                return self.model
            return str(model._meta.verbose_name)
    
        @property
        def app_labeled_name(self):
            model = self.model_class()
            if not model:
                return self.model
            return '%s | %s' % (model._meta.app_label, model._meta.verbose_name)
    
        def model_class(self):
            """Return the model class for this type of content."""
            try:
                return apps.get_model(self.app_label, self.model)
            except LookupError:
                return None
    
        def get_object_for_this_type(self, **kwargs):
            """
            Return an object of this type for the keyword arguments given.
            Basically, this is a proxy around this object_type's get_object() model
            method. The ObjectNotExist exception, if thrown, will not be caught,
            so code that calls this method should catch it.
            """
            return self.model_class()._base_manager.using(self._state.db).get(**kwargs)
    
        def get_all_objects_for_this_type(self, **kwargs):
            """
            Return all objects of this type for the keyword arguments given.
            """
            return self.model_class()._base_manager.using(self._state.db).filter(**kwargs)
    
        def natural_key(self):
            return (self.app_label, self.model)
    

    环境搭建

    以用户的收藏为例,这里涉及到的表结构如下

    from django.db import models
    from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
    from django.contrib.contenttypes.models import ContentType
    
    
    # Create your models here.
    
    class User(models.Model):
        name = models.CharField(max_length=10, verbose_name='姓名')
        desc = models.CharField(max_length=200, verbose_name='简介')
    
        def __str__(self):
            return self.name
    
        class Meta:
            verbose_name = '姓名'
            verbose_name_plural = verbose_name
    
    
    class Fav(models.Model):
        user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='用户')
        content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
        object_id = models.IntegerField()
        value = GenericForeignKey()
    
        def __str__(self):
            return f'{self.user} 收藏了 {self.value}'
    
        class Meta:
            verbose_name = '收藏表'
            verbose_name_plural = verbose_name
    
    
    class Org(models.Model):
        name = models.CharField(max_length=10, verbose_name='名称')
        desc = models.CharField(max_length=200, verbose_name='简介')
        fav = GenericRelation(Fav, related_query_name='org')
    
        def __str__(self):
            return self.name
    
        class Meta:
            verbose_name = '机构'
            verbose_name_plural = verbose_name
    
    
    class Teacher(models.Model):
        name = models.CharField(max_length=10, verbose_name='姓名')
        desc = models.CharField(max_length=100, verbose_name='简介')
        fav = GenericRelation(Fav, related_query_name='teacher')
    
        def __str__(self):
            return self.name
    
        class Meta:
            verbose_name = '老师'
            verbose_name_plural = verbose_name
    
    
    class Course(models.Model):
        name = models.CharField(max_length=10, verbose_name='课程名')
        desc = models.CharField(max_length=100, verbose_name='简介')
        fav = GenericRelation(Fav, related_query_name='course')
    
        def __str__(self):
            return self.name
    
        class Meta:
            verbose_name = '课程'
            verbose_name_plural = verbose_name
    

    GenericForeignKey

    这里看一下收藏表的结构,通过 content_type 来定位到表, 再通过 object_id 来定位到具体的记录值

    class Fav(models.Model):
        user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='用户')
        content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
        object_id = models.IntegerField()
        value = GenericForeignKey()
    
        def __str__(self):
            return f'{self.user} 收藏了 {self.value}'
    
        class Meta:
            verbose_name = '收藏表'
            verbose_name_plural = verbose_name
    

    如果不使用 content_type 的话,表结构的设计可能就成下面这个样子了, 也差不多,收藏类型就是 哪张表 ,需要事先确定一下字段代表的具体值

    TYPE_CHOICES = (
            (1, "课程"),
            (2, "机构"),
            (3, "老师")
    )
    user = models.ForeignKey(User, verbose_name="用户", on_delete=models.CASCADE)
    fav_type = models.IntegerField(choices=TYPE_CHOICES, default=1, verbose_name="收藏类型")
    fav_id = models.IntegerField(default=0)
    

    具体操作

    添加收藏

    前面使用 GenericForeignKey 来关联 content_typeobject_id

    # 获取对象
    user = User.objects.first()
    course = Course.objects.first()
    teacher = Teacher.objects.first()
    org = Org.objects.first()
    
    # 空表
    Fav.objects.all()
    <QuerySet []>
    
    # 添加收藏, value是表结构中定义的,命名有点问题,影响不大
    Fav.objects.create(user=user, value=org)
    <Fav: 用户1 收藏了 机构1>
    Fav.objects.create(user=user, value=teacher)
    <Fav: 用户1 收藏了 讲师1>
    Fav.objects.create(user=user, value=course)
    <Fav: 用户1 收藏了 课程1>
    
    # 表已经有数据了
    Fav.objects.all()
    <QuerySet [<Fav: 用户1 收藏了 机构1>, <Fav: 用户1 收藏了 讲师1>, <Fav: 用户1 收藏了 课程1>
    

    查询收藏

    先再添加一个收藏

    user_last = User.objects.last()
    user_last
    <User: 用户5>
    
    Fav.objects.create(user=user_last, value=course)
    <Fav: 用户5 收藏了 课程1>
    

    查询收藏了 课程1 的用户, 刚好是录入的两条记录

    Fav.objects.filter(course=course)
    <QuerySet [<Fav: 用户1 收藏了 课程1>, <Fav: 用户5 收藏了 课程1>]>
    

    GenericRelation

    这里的过滤条件在 fav 这个表结构中并没有出现,能使用的原因是在 Course 表中的 GenericRelation 定义了这个反向查询 related_query_name
    前面的表结构中都有一个 fav 字段来关联到 Fav, 其中定义了 related_query_name 来允许反向查询, 如果没有定义 related_query_name ,就无法进行反向查询, 查询就会报错
    这个字段并不会在数据库中生成

    如果没有写这个 related_query_name 的话,就要手动查询了

    course = Course.objects.first()
    value_type = ContentType.objects.get_for_model(Course)
    
    Fav.objects.filter(content_type__pk=value_type.id, object_id=course.id)
    <QuerySet [<Fav: 用户1 收藏了 课程1>, <Fav: 用户5 收藏了 课程1>]>
    
    Fav.objects.filter(content_type=value_type, object_id=course.id)
    <QuerySet [<Fav: 用户1 收藏了 课程1>, <Fav: 用户5 收藏了 课程1>]>
    

    查询多个的话也可以用这种

    courses = Course.objects.all()
    Fav.objects.filter(content_type=value_type, object_id__in=courses)
    <QuerySet [<Fav: 用户1 收藏了 课程1>, <Fav: 用户5 收藏了 课程1>]>
    

    聚合查询

    Course.objects.aggregate(Count('fav'))
    {'fav__count': 3}
    
    Fav.objects.all()
    <QuerySet [<Fav: 用户1 收藏了 机构1>, <Fav: 用户1 收藏了 讲师1>, <Fav: 用户5 收藏了 课程1>, <Fav: 用户5 收藏了 课程5>, <Fav: 用户5 收藏了 课程5>]>
    
    Org.objects.aggregate(Count('fav'))
    {'fav__count': 1}
    
    Course.objects.last()
    <Course: 课程5>
    
    Course.objects.last().fav
    <django.contrib.contenttypes.fields.create_generic_related_manager.<locals>.GenericRelatedObjectManager object at 0x000001EC8319AD88>
    
    Course.objects.last().fav.count()
    2
    
    Course.objects.last().fav.all()
    <QuerySet [<Fav: 用户5 收藏了 课程5>, <Fav: 用户5 收藏了 课程5>]>
    

  • 相关阅读:
    C#CreateGraphics方法的三种实现方式
    二叉树的性质和常用操作代码集合
    《Java程序设计基础》 第8章手记Part 2
    《Java程序设计基础》 第8章手记Part 1
    STL 算法罗列 (转)
    STL 练习
    STL所有算法简介 (转) http://www.cnblogs.com/yuehui/archive/2012/06/19/2554300.html
    linux 解压命令
    杭电1016
    杭电1257
  • 原文地址:https://www.cnblogs.com/gaoyongjian/p/12720255.html
Copyright © 2020-2023  润新知