zoukankan      html  css  js  c++  java
  • python框架之Django(15)-contenttype模块

    假如有一个书城系统,需要给作者和书籍加上评论功能。如果给每个表单独建一个评论表,那么我们以后要扩展其它模块评论功能的时候,还需要随之新建一张评论表,会显得很冗余。对于这种情况,Django 给我们提供了解决方案,那就是 contenttypes 模块。

    模型

    from django.db import models
    from django.contrib.contenttypes.models import ContentType
    
    
    class Book(models.Model):
        '''
        书籍表
        '''
        title = models.CharField(max_length=32)  # 书籍标题
    
    
    class Author(models.Model):
        name = models.CharField(max_length=32)  # 作者名称
    
    
    class Comment(models.Model):
        '''
        评论表
        '''
        content_type = models.ForeignKey(ContentType, on_delete=None)  # 被评论表(对哪张表进行评论)
        object_id = models.PositiveIntegerField()  # 被评论表中数据的id
        content = models.CharField(max_length=200)  # 评论内容

    上述的表结构很简单,但是注意 Comment 表中的 content_type 字段,它关联的是 django.contrib.contenttypes.models.ContentType 这张表,而这张表就是 Django 为我们提供的,初始化 DB 时就会自动生成。看一下它的数据:

    可以看到,它其实就是一张描述我们所建的模型和 Django 内置模型的信息表。而我们通过 Comment 表中的 content_type 字段与之关联,就可以标识出某条评论属于哪个模型(在这里描述的就是某条评论,是对作者,还是对书籍)。再通过 object_id 字段,关联到模型的主键,就可以标识出某条评论属于哪个模型的哪条数据(在这里描述的就是某条评论,是对某个作者,还是对某本书籍)。

    新增数据

    初始化测试数据:

    import os
    
    if __name__ == '__main__':
        os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_test.settings')
        import django
    
        django.setup()
    
        from api import models
        from django.contrib.contenttypes.models import ContentType
    
        models.Author.objects.create(name='鲁迅')
        models.Author.objects.create(name='郭德纲')
        models.Book.objects.create(title='傻子是这样炼成的')
        models.Book.objects.create(title='永夜')
        models.Book.objects.create(title='武则天传奇')
    
        models.Comment.objects.create(object_id=1, content_type=ContentType.objects.get(id=7), content='鲁大哥,666666')  # 鲁迅
        models.Comment.objects.create(object_id=1, content_type=ContentType.objects.get(id=7), content='鲁大哥 我占了二楼')  # 鲁迅
        models.Comment.objects.create(object_id=2, content_type=ContentType.objects.get(id=7), content='老郭没得话说。。')  # 郭德纲
    
        models.Comment.objects.create(object_id=1, content_type=ContentType.objects.get(id=8), content='这本书好神奇')  # 傻子是这样炼成的
        models.Comment.objects.create(object_id=2, content_type=ContentType.objects.get(id=8), content='永夜是什么夜')  # 永夜
        models.Comment.objects.create(object_id=3, content_type=ContentType.objects.get(id=8), content='武媚娘 武则天')  # 武则天传奇

    在新增评论表数据的时候我们会发现会有很繁琐的操作,例如我要给郭德纲评论,我需要先在 django_content_type 表中找到作者表,让 content_type 与之关联,再在作者表中找到郭德纲的主键,让其和 object_id 关联,最后才是评论内容。为了简化这种操作,contenttypes 给我们提供了 django.contrib.contenttypes.fields.GenericForeignKey 字段。修改 Comment 模型如下:

    from django.contrib.contenttypes.fields import GenericForeignKey
    from django.db import models
    
    from django.contrib.contenttypes.models import ContentType
    
    
    class Comment(models.Model):
        '''
        评论表
        '''
        content_type = models.ForeignKey(ContentType, on_delete=None)  # 被评论表(对哪张表 进行评论)
        object_id = models.PositiveIntegerField()  # 被评论表中数据的id
        content = models.CharField(max_length=200)  # 评论内容
        comment_to = GenericForeignKey('content_type', 'object_id')  # 被评论对象

    可以看到 Comment 模型中新增了一个 comment_to 字段,此时我们再来进行新增操作:

    models.Comment.objects.create(comment_to=models.Author.objects.get(id=2), content='老郭 我又来了~!')
    models.Comment.objects.create(comment_to=models.Book.objects.get(id=2), content='永远的夜晚?')

    效果:

    可以看到,相对于之前的新增操作,这里直接把要评论的对象赋值给 comment_to 字段,然后 contenttypes 会自动根据该对象的映射关系给 object_id 和 content_type 赋上值,不仅简化了操作,还让操作更加直观明了。

    注意:这里添加的 comment_to 列仅仅用于操作,并不会在数据库生成对应列。

    查询数据

    正向查询

    假如要查询出每条评论对应的评论对象,很显然我们需要先根据评论的 content_type 找到评论的对象类型(作者或书籍),然后从该对象类型对应的表中找到 pk 值为 object_id 值对应的那条数据。 django.contrib.contenttypes.fields.GenericForeignKey 字段除了给我们提供新增数据时的便利,查询数据的时候也是,如下:

    for comment in models.Comment.objects.all():
        print('{}=>{}'.format(comment.content,comment.comment_to))

    效果:

    可以看到每个评论的 comment_to 属性直接对应了评论的对象,是不是很给力~

    反向查询

    假如要查询出郭德纲(作者)和永夜(书籍)所有的评论,对于这种一对多的查询操作, contenttypes 给我们提供了另外一个字段—— django.contrib.contenttypes.fields.GenericRelation 。修改模型如下:

    from django.contrib.contenttypes.fields import GenericRelation, GenericForeignKey
    from django.db import models
    
    from django.contrib.contenttypes.models import ContentType
    
    
    class Book(models.Model):
        title = models.CharField(max_length=32)  # 书籍标题
        comments = GenericRelation("Comment")  # 所有评论
    
    
    class Author(models.Model):
        name = models.CharField(max_length=32)  # 作者名称
        comments = GenericRelation("Comment")  # 所有评论
    
    
    class Comment(models.Model):
        content_type = models.ForeignKey(ContentType, on_delete=None)  # 被评论表(对哪张表 进行评论)
        object_id = models.PositiveIntegerField()  # 被评论表中数据的id
        comment_to = GenericForeignKey('content_type', 'object_id')  # 被评论对象
        content = models.CharField(max_length=200)  # 评论内容

    如上,给模型 Book 和 Author 个各添加了一个 comments 字段,都对应 Comment 模型,此时我们完成上述要求的查询就很简单了,如下:

        print('作者【郭德纲】的评论:')
        [print('    '+comment.content) for comment in models.Author.objects.get(name='郭德纲').comments.all()]
        print('书籍【永夜】的评论:')
        [print('    '+comment.content) for comment in models.Book.objects.get(title='永夜').comments.all()]

    效果:

    注意:这里添加的 comments 列与上面加的 comment_to 列一样,都不会在数据库中生成对应列。

  • 相关阅读:
    Three.js源码阅读笔记4
    算法导论11.动态规划上
    leetcode刷题笔录1
    JavaScript的模块化:封装(闭包),继承(原型)
    leetcode刷题笔录5
    算法导论1.排序算法
    算法导论3.递归部分习题选
    Three.js Demo源码分析1.MorphTargets与BufferGeometry
    算法导论10.顺序统计树与区间树习题
    算法导论4.随机快速排序与线性时间排序
  • 原文地址:https://www.cnblogs.com/zze46/p/10050722.html
Copyright © 2011-2022 走看看