需求简述
- 需要给文章添加可评论的功能
- 评论可回复(评论一个评论)
简单分析
- 评论最起码需要评论两种类型: 文章(Post)和评论(Comment), 显然外键得有两个
- 如果引入两个外键会有以下问题:
- 过度冗余
- 扩展性差(如果又要给新的教程(Tutorial)类型添加评论,又加一个外键吗?)
- 我们需要的是,不管评论的对象是什么东西, 我们的评论都可以适用.
解决方案
- 其实我们需要的一个通用外键, 这个外键可以代表任何类型的对象, 这就是django内置的 Contenttype和GenericRelation, 即通用类型和通用关系.
详细说明
- 我们要评论的对象即确定又不确定, 即需要确定唯一的一个对象, 但又不能确定是哪个唯一的对象. 只要能正确的锁定这个唯一的对象, 那么我们的评论就可以作用于这个对象
- 在数据库中, 唯一确定一个对象,需要提供两个条件: 表名(Table,django中的模型名)和主键(PK), 有了这两个条件, 就能唯一确定一个对象
- django中有一张表保存了所有的模型名字, 并且映射成了模型,并提供接口供使用. Contenttype
因此, 在我们的评论模型(Comment)中,只需要这样写:
from django.db import models
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import \
GenericForeignKey, GenericRelation
class Comment(models.Model):
author = models.ForeignKey(User,
verbose_name=u'评论者',
related_name='comments')
created = models.DateTimeField(u'时间', auto_now=True)
content = models.TextField(u'评论内容')
likes = models.IntegerField(u'顶', default=0)
user_likes = models.ManyToManyField(User,
verbose_name=u'点赞的人',
related_name='comments_liked')
# 自己对自己的评论, 也就是评论的回复
comments = GenericRelation('Comment')
# 外键为通用类型, 什么类型都有可能
content_type = models.ForeignKey(ContentType)
# 类型的id
object_id = models.PositiveIntegerField()
# 类型和id共同唯一确定一个对象
content_object = GenericForeignKey('content_type', 'object_id')
def __str__(self, ):
return '%s对%s的评论' % (self.author.username, self.content_object)
在博客文章(Post)中这样写:
from django.db import models
from django.contrib.contenttypes.fields import GenericRelation
class Post(models.Model):
comments = GenericRelation(Comment)
这样, 不管什么地方需要评论,只需要使用GenericRelation来添加评论就行了.
获取评论:
p = Post.objects.all()[0]
comments = p.comments.all()