博客五部曲之二 - 高级博客


1251 浏览 5 years, 3 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时会用。
另外一个是加入了子评论显示,循环所有子评论并显示出来。

目前子评论只有一级,还不能做到递归嵌套。