用django搭建自己的博客(四)-设置评论(转)

使用高级特性来优化你的博客

在上一章中,你创建了一个基础的博客应用。现在你将要改造它成为一个功能更加齐全的博客,利用一些高级的特性例如添加评论,给帖子打上tag,检索出相似的帖子。在本章中,你将会学习以下几点:

  • 通过models创建表单
  • 构建复杂的QuerySets

现在我们准备为博客创建一个评论系统,这样用户可以在帖子上进行评论。你需要做到以下几点来创建一个评论系统:

  • 创建一个modle保存评论
  • 创建一表单用来提交平路你和验证输入的数据
  • 添加一个view来处理表单和保存新的评论到数据库中
  • 编辑帖子的详情template来展示评论列表以及用来添加新评论的表单

首先,让我们创建一个model来存储评论。打开你博客应用下的models.py文件添加如下代码:

class Comment(models.Model):
    name = models.CharField(max_length=80)
    post = models.ForeignKey(Post, related_name='comments')
    email = models.EmailField()
    body = models.TextField()
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    active = models.BooleanField(default=True)

    class Meta:
        ordering = ('created',)

    def __str__(self):
        return 'Comment by {} on {}'.format(self.name, self.post)

以上就是我们的Comment model。它包含了一个外键用来关联一个单独的帖子。在Comment model中定义多对单的关系是因为每一条评论只能在一个帖子下生成,而每一个帖子又可能包含多个评论。related_name属性允许我们命名这个属性这样我们就可以使用这个关系从有关联的对象来读取这儿。定义好这个之后,我们可以从一条评论来取到对应的帖子通过使用 comment.post和取回一个帖子所有的评论通过使用post.comments.all(。如果你没有定义related_name属性,Django会使用这个model的命名加上_set(例如:comment_set)来命名关联对象读取这儿的manager。

你可以学习更多关于多对单的关系通过访问 多对单的关系

我们有包含一个active布尔字段用来手动使不好的评论无效不展示。我们使用created字段使评论默认根据创建时间来进行排序。

你刚创建的这个新的Comment model并没有同步到数据库中。运行以下命令通过新的model生成一个新的数据迁移,并创建数据库:

python manage.py makemigrations blog
python manage.py migrate

添加站点管理

现在,我们可以添加我们新的model到管理站点中通过简单的接口来管理评论。打开博客应用下的admin.py文件,添加如下内容:

from .models import Post, Comment

class CommentAdmin(admin.ModelAdmin):
    list_display = ('name', 'email', 'post', 'created', 'active')
    list_filter = ('active', 'created', 'updated')
    search_fields = ('name', 'email', 'body')

admin.site.register(Comment, CommentAdmin)

访问 http://127.0.0.1:8000/admin/ 会看多多出一个 Comments

通过models创建表单

我们仍然需要创建一个表单可以让我们的用户在博客帖子下进行评论。请记住,Django有两个用来创建表单的基础类:Form和ModelForm。你已经使用过前者可以让用户通过email来分享帖子。在下面的例子中,你将需要使用ModelForm因为你必须从你的Comment model中创建一个动态的表单。编辑博客应用下的forms.py,添加如下代码:

from .models import Comment

class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = ('name', 'email', 'body')

从model中创建表单,我们只需要在这个表单的Meta类中声明使用哪个model来构建表单。Django将会解析model并为我们动态的创建表单。每一种model字段类型都有对应的默认表单字段类型。默认的,Django创建的表单包含model中包含的每个字段。当然,你可以明确的告诉框架你想在你的表单中包含哪些字段通过使用fields列,或者定义哪些字段你不需要的通过使用exclude列。对于我们的CommentForm,我们在表单中只需要name,email,和body字段,因为我们只需要用到这3个字段让我们的用户来填写。

在views中操作ModelForms

我们会使用帖子的详情view来实例化表单,能更简单的处理它。编辑models.py文件,导入Comment modle和CommentForm表单,并且修改post_detail view如下所示:

from .forms import EmailPostForm, CommentForm
from django.views.generic import ListView

class PostListView(ListView):
    queryset = Post.published.all()
    context_object_name = 'posts'
    paginate_by = 3
    template_name = 'blog/post/list.html'

def post_detail(request, year, month, day, post):
   post = get_object_or_404(Post, slug=post,
                                  status='published',
                                  publish__year=year,
                                  publish__month=month,
                                  publish__day=day)
   # List of active comments for this post
   comments = post.comments.filter(active=True)

   if request.method == 'POST':
       comment_form = CommentForm(data=request.POST)
       if comment_form.is_valid():
           new_comment = comment_form.save(commit=False)
           new_comment.post = post
           new_comment.save()
   else:
       comment_form = CommentForm()
   return render(request,'blog/post/detail.html',{'post': post, 'comments': comments, 'comment_form': comment_form})

让我们来回顾下我们刚才对对我的view添加了哪些操作。我们使用post_detail view来显示帖子和对应的评论。我们添加了一个QuerySet来显示这个帖子所有有效的评论:

comments = post.comments.filter(active=True)

我们从post对象开始构建这个QuerySet。使用在Comment model中通过related_name属性定义的关系啦返回所有有关系的对象给comments*。

我们还在这个view中让我们的用户添加一条新的评论。如果调用这个view通过GET请求,就我们创建了一个表单实例通过使用comment_fomr = commentForm()
。如果请求是一个已经完成验证的POST,我们实例化表单来使用提交的数据并且验证数据通过使用is_valid()方法。如果这个表单是无效的,我们会在template中渲染验证错误的信息。如果表单通过验证,我们会做以下的操作:

  1. 我们创建一个新的Comment对象通过调用这个表单的save()方法,如下所示:
new_comment = comment_form.save(commit=False)

saven()方法创建了一个model的实例用来保存表单的数据到数据库中。如果你调用这个方法通过设置comment=False
,你创建的model实例不会即时保存到数据中。这是非常方便的当你想在最终保存之前修改这个model对象,我们接下来将做这一步骤。save()方法是给ModelForm用的,但不是给表单实例们用的,因为它们没有关联上任何model。

  1. 我们分配一个帖子给我们刚才创建的评论:
new_comment.post = post

通过以上动作,我们指定新的评论是属于分配的帖子。
3.我们保存新的评论到数据库,如下所示:

new_comment.save()

我们的view已经准备好显示和处理新的评论了。

在帖子详情template中添加评论

我们为帖子创建了一个管理评论的功能。现在我们需要修改我们的post_detail.html template来适应这个功能,通过做到以下步骤:

  • 显示这个帖子的评论总数
  • 显示评论的列
  • 显示一个表单给用户来添加新的评论

首先,我们来添加评论的总数。打开blog/detail.html template在content区块中添加如下代码:

    {% with comments.count as total_comments %}
        <h2>
            {{ total_comments }} comment{{ total_comments|pluralize }}
        </h2>
    {% endwith %}

我们在template中使用Django ORM执行comments.count() QuerySet。注意,在Django template语言中调用方法时不使用圆括号。{% with %} tag允许我们分配一个值给新的变量,这个变量可以一直使用直到遇到{% endwith %}

{% whti %} template tag是非常有用的可以避开直接使用数据库或花费大量的时间在处理复杂的方法。

我们使用pluralize template filter在单词comment的后面展示total_comments的复数形式。Template filters使输入的变量值经过应用然后返回计算后的值。我们将会更多的讨论tempalte filters在第三章 扩展你的博客应用中。

这pluralize template filter 会在值的末尾显示一个"s"如果值不为 1。在之前的文本将会渲染成类似: 0 comments, 1 comment 或者 N comments。Django内置大量的template tags 和 filters来帮助你通过各种方法展示各类信息。

现在,让我们加入评论列。在blog/detail.html中之前的代码后面加入以下内容:

    {% for comment in comments %}
        <div class="comment">
            <p class="info">
                Comment {{ forloop.counter }} by {{ comment.name }}
                {{ comment.created }}
            </p>
            {{ comment.body|linebreaks }}
        </div>
    {% empty %}
        <p>There are no comments yet.</p>
    {% endfor %}

我们使用{% for %}template tag来循环所有的评论。我们会显示一个默认的信息如果comments列为空,告诉我们的用户这篇帖子还没有任何评论。我们通过使用{{ forloop.counter }}变量来枚举评论,该变量包含在每次迭代的循环计数中。之后我们显示发送评论的用户名,日期,和评论的内容。

最后,你需要渲染表单或者显示一条成功的信息来代替表单当表单提交成功后。在之前的代码后面添加如下内容:

    {% if new_comment %}
        <h2>Your comment has been added.</h2>
    {% else %}
        <h2>Add a new comment</h2>
        <form action="." method="post">
            {{ comment_form.as_p }}
            {% csrf_token %}
            <p><input type="submit" value="Add comment"></p>
        </form>
    {% endif %}

这段代码非常简洁明了:如果存在new_comment对象,我们会展示一条成功信息因为成功创建了一条新评论。否则,我们通过一个段落<p>
元素渲染表单中每一个字段,并且表明这个POST请求包含有CSRF标记。在浏览器中打开 http://127.0.0.1:8000/blog/ 然后点击任意一篇帖子的标题进入它的详情页面。你会看到如下页面展示:

comments

邮箱一定要是正确格式,不然提交没反应。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容