博客五部曲之二 - 高级博客
1448 浏览 5 years, 11 months
16 评论回复
版权声明: 转载请注明出处 http://www.codingsoho.com/前面一节介绍了如何去创建一个评论,这个评论时基于帖子的。但是有的时候,我们打算对别人的评论做一下自己的评论,这就用到了评论回复功能。
因为评论会关联自己,所以在model里我们添加一个指向自己的ForeignKey, 并贴添加了相关的函数和属性。children用于查找所有的子评论。is_parent判断当前评论是不是根评论。
class Comment(models.Model):
…
parent = models.ForeignKey('self', null=True, blank=True)
def __unicode__(self):
return str(self.user.username)
def children(self):
return Comment.objects.filter(parent=self)
@property
def is_parent(self):
if self.parent is not None:
return False
else:
return True
同时更新了ModelManger,获取评论列表时,我们会去掉子评论,这些评论会在父评论后嵌套显示,这个后面会讲。
class CommentManager(models.Manager):
def all(self):
qs = super(CommentManager,self).filter(parent=None)
return qs
def filter_by_instance(self, instance):
content_type = ContentType.objects.get_for_model(instance.__class__)
obj_id = instance.id
qs = super(CommentManager,self).filter(content_type=content_type,object_id=obj_id,parent=None)
return qs
添加了新的字段parent_id后,在视图里处理这个字段,在创建新的评论时储存这个值。
def post_detail(request, slug=None):
comments = Comment.objects.filter_by_instance(instance)
initial_data = {
"content_type": instance.get_content_type,
"object_id":instance.id,
}
comment_form = CommentForm(request.POST or None, initial=initial_data)
if comment_form.is_valid():
c_type = comment_form.cleaned_data.get("content_type")
content_type = ContentType.objects.get(model=c_type)
obj_id = comment_form.cleaned_data.get('object_id')
content_data = comment_form.cleaned_data.get('content')
try:
parent_id = int(request.POST.get("parent_id")) # not in form
except:
parent_id = None
parent_obj = None
if parent_id:
parent_qs = Comment.objects.filter(id=parent_id)
if parent_qs.exists() and parent_qs.count()==1:
parent_obj = parent_qs.first()
new_comment, created = Comment.objects.get_or_create(
user=request.user,
content_type=content_type,
object_id=obj_id,
content=content_data,
parent=parent_obj,
)
return HttpResponseRedirect(new_comment.content_object.get_absolute_url())
注意到parent_id不是通过cleaned_data获取的,因为它并没有在form里定义。我们有很多form,parent_id的值也各不相同,对于其他的两个隐藏字段,我们直接在初始化的时候就赋值了。
对于parent_id,对于每个form都在view里赋值很麻烦代码也不优美,可以直接在模板里赋值
视图里根据parent_id过滤到它的父评论,这个评论应该只有一个,如果满足条件,则认为当前评论是一个子评论,将父评论存储到parent字段。
最后,修改post_detail.html模板,渲染子评论内容
{% for comment in comments %}
{{comment.content_object}}
<blockquote>
<p>{{comment.content}}</p>
<footer>via {{comment.user }} | {{comment.timestamp|timesince}} ago</footer>
{% for child_comment in comment.children %}
<blockquote>
<p>{{child_comment.content}}</p>
<footer>via {{child_comment.user }} | {{child_comment.timestamp|timesince}} ago</footer>
</blockquote>
{% endfor %}
<form method="post" action="">{% csrf_token %}
{{comment_form|crispy}}
<input type="hidden" name="parent_id" value="{{comment.id}}">
<input type="submit" value="Post Comments" class="btn btn-default">
</form>
</blockquote>
<hr>
{% endfor %}
其中代码包括两部分。首先是表单输入框,与上面评论框不同的是,它里面新加了隐藏字段parent_id,如前面所述,这个在赋值字段parent时会用。
另外一个是加入了子评论显示,循环所有子评论并显示出来。
目前子评论只有一级,还不能做到递归嵌套。