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

能夠看到,它其實就是一張描述咱們所建的模型和 Django 內置模型的信息表。而咱們經過 Comment 表中的 content_type 字段與之關聯,就能夠標識出某條評論屬於哪一個模型(在這裏描述的就是某條評論,是對做者,仍是對書籍)。再經過 object_id 字段,關聯到模型的主鍵,就能夠標識出某條評論屬於哪一個模型的哪條數據(在這裏描述的就是某條評論,是對某個做者,仍是對某本書籍)。api

新增數據

初始化測試數據:測試

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 模型以下:spa

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 字段,此時咱們再來進行新增操做:code

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='永遠的夜晚?')

效果:orm

能夠看到,相對於以前的新增操做,這裏直接把要評論的對象賦值給 comment_to 字段,而後 contenttypes 會自動根據該對象的映射關係給 object_id 和 content_type 賦上值,不只簡化了操做,還讓操做更加直觀明瞭。對象

注意:這裏添加的 comment_to 列僅僅用於操做,並不會在數據庫生成對應列。blog

查詢數據

正向查詢

假如要查詢出每條評論對應的評論對象,很顯然咱們須要先根據評論的 content_type 找到評論的對象類型(做者或書籍),而後從該對象類型對應的表中找到 pk 值爲 object_id 值對應的那條數據。 django.contrib.contenttypes.fields.GenericForeignKey 字段除了給咱們提供新增數據時的便利,查詢數據的時候也是,以下:get

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 列同樣,都不會在數據庫中生成對應列。

相關文章
相關標籤/搜索