连锁学科。相关学科。

评论的 Model 设计

blog/models.py

class BlogComment(models.Model):
    user_name = models.CharField('评论者名字', max_length=100)
    user_email = models.EmailField('评论者邮箱', max_length=255)
    body = models.TextField('评论内容')
    created_time = models.DateTimeField('评论发表时间', auto_now_add=True)
    article = models.ForeignKey('Article', verbose_name='评论所属文章', on_delete=models.CASCADE)

    def __str__(self):
        return self.body[:20]

参考大部分博客评论的体裁,我们的 BlogComment Model 包含这些字段:

user_name:用户以评价前先行要填他们感念采取的昵称

user_email:用户在评价前先要填写他们想利用的邮箱

body:用户提交的品内容

created_time:评论提交时

article:评论关联的文章,因为一个评只能干有一样篇稿子,而相同篇文章下或者发生多独评价,因此是平针对性多之关系,使用
ForeignKey

咱俩后续给博客添加效果,以及改进前面不客观的片段。本课程将牵动您完了 Blog
的详情页面,即用户点击首页的稿子标题或者看全文按钮将跨反到文章的详情页面来读书整篇文章。其次将调动片目录结构为要其以尽以中进一步客观。

由此前四周的年月我们出了一个略的私有 Blog,相关课程:

上周我们完成了博客的 Model 部分,以及 Blog 的首页视图 IndexView。

Django学习小组简介

django学习小组凡是一个推 django 新手互相学习、互相帮助的组织。

小组以单方面读书 django 的以以同步好几个类型,包括:

  • 一个简练的 django 博客,用于发布小组每周的上与支出文档;
  • django中国社区,为国内的 django 开发者们提供一个马拉松保护的
    django 社区;

地方所说之斯社区类于 segmentfault 和 stackoverflow
,但更注意(只在意让 django 开发之问题)。

再度多的音信要关注我们的 github
组织,本学科项目的并行关源代码也已经上传到
GitHub 的 blog-tutorial
分支
上,请点击链接获取。

与此同时,你也可在我们的邮件列表
django_study@groups.163.com
,随时关注我们的动态。我们见面拿每周的事无巨细支出文档和代码通过邮件列表发出。

只要产生任何建议,欢迎提 Issue,欢迎 fork,pr,当然为转变忘了 star 哦!

连锁课程:
第一周: Django 学习小组:博客开发实战第一周教程 —— 编写博客的
Model 与首页面
第三周:Django
学习小组:博客开发实战第三宏观教程——文章列表分页和代码语法高亮
第四周:Django
学习小组:博客开发实战第四周——标签云与文章归档
第五周:Django
学习小组:博客开发实战第五圆——基于类的通用视图详解(一)
第六周:Django 学习小组:博客开发实战第六周教程 ——
实现评论功能

准课程内容早已不合时宜,更新版教程请访问: Django
博客开发入门教程

增产文章详情页

class ArticleDetailView(DetailView):
# Django有基于类的视图DetailView,用于显示一个对象的详情页,我们继承它
    model = Article
    # 指定视图获取哪个model

    template_name = "blog/detail.html"
    # 指定要渲染的模板文件

    context_object_name = "article"
    # 在模板中需要使用的上下文名字

    pk_url_kwarg = 'article_id'
    # 这里注意,pk_url_kwarg用于接收一个来自url中的主键,然后会根据这个主键进行查询
    # 我们之前在urlpatterns已经捕获article_id

    # 指定以上几个属性,已经能够返回一个DetailView视图了,为了让文章以markdown形式展现,我们重写get_object()方法。
    def get_object(self):
        obj = super(ArticleDetailView, self).get_object()
        obj.body = markdown2.markdown(obj.body)
        return obj

get_object() 返回该视图如来得的靶子。如果发安 queryset,该queryset
将用来对象的来;否则,将下get_queryset().
get_object()从视图的有所参数中追寻 pk_url_kwarg 参数;
如果找到了之参数,该方式应用此参数的价执行一个因主键的询问。

新建一个模板 detail.html 来显示我们的章详情

blog/templates/blog/detail.html

{% extends 'base.html' %}
{% block content %}
    <div id="bd" class="wrp clear-fix">
        <div id="main">
            <div id="detail-title">
                <ul id="single-nav">
                    <li><a href="{% url 'blog:index' %}">首页</a></li>
                    <li>></li>
                    <li>
                        <ul class="post-categories">
                            <li><a href="" title=""
                                   rel="category">{{ article.category.name }}</a>
                            </li>
                        </ul>
                    </li>
                    <li>></li>
                    <li class="title-active"><a href="{% url 'blog:detail' article.pk %}"
                                                rel="bookmark">{{ article.title }}</a>
                    </li>
                </ul>
            </div>
            <div id="post-1951"
                 class="post-1951 post type-post status-publish format-standard hentry category-meida-report">
                <div class="post-hd">
                    <h1 class="title">{{ article.title }}</h1>
                </div>
                <div class="date-read">
                    <i class="icon-date"></i>{{ article.last_modified_time|date:"Y年n月d日" }}
                </div>
                <div class="post-bd">
                    {{ article.body |safe }}
                </div>
            </div>
        </div>
    </div>
    <div id="previous-next-nav">
    </div>
{% endblock %}

浑实施流程虽是如此的:

假使用户一旦拜访某个篇稿子,比如他点击了某篇文章的标题,在模板文件中(首页的沙盘,代码可以参见
GitHub 的 blog-tutorial
分支上之
index.html),他点击的就算是如此一个签:

<h1 class="title">
  <a href="{% url 'blog:detail' article.pk %}">{{ article.title }}</a>
</h1>

<a> 标签是一个超链接,用户点击后会见越反到由 href 指定的
url,这里我们使用了 django 自带的沙盘标签 url 标签,它会自行分析
blog:detail 这个视图函数对应的 url,并且把
article.pk(文章的主键)传递给detail 视图函数 。detail 的 url
是这样定义之:

url(r'^blog/article/(?P<article_id>\d+)$', views.ArticleDetailView.as_view(), name='detail')

如果用户点击了第三篇稿子,那么该 url 会被解析成:/blog/article/3,其中 3
被传送让了详情页面视图函数。

而今视图函数被调用,它首先冲传被它的参数获自动调用 get_object
方法得到到文章的 model,然后根据 context_object_name = “article” 把
article
加入到上下文中(可以知道吧携带着是变量和其值并使传送让模板文件的对象,模板文件由夫目标被取出模板变量对应的值并替换。),之后渲染
template_name = “blog/detail.html”
指定的模版文件,至此用户就是越反至了稿子详情页,效果如下:

文章详情页

本周咱们用贯彻 blog 文章的评头品足功能。

照课程内容已经不合时宜,更新版教程请访问: Django
博客开发入门教程

URL 设置

urlpatterns = [
    url(r'^$', views.IndexView.as_view(), name='index'),
    ...
    # 设置评论视图对应的 url
    url(r'^article/(?P<article_id>\d+)/comment/$', views.CommentPostView.as_view(), name='comment'),
]

增产分类视图

点击某个分类,展示该分类下有所文章,其逻辑与首页显示周文章列表是相同的,唯一不同的凡咱沾之非是整套章,而是该分类下之章。代码如下:

class CategoryView(ListView):
# 继承自ListView,用于展示一个列表

    template_name = "blog/index.html"
    # 指定需要渲染的模板

    context_object_name = "article_list"
    # 指定模板中需要使用的上下文对象的名字

    def get_queryset(self):
        #get_queryset 的作用已在第一篇中有介绍,不再赘述
        article_list = Article.objects.filter(category=self.kwargs['cate_id'],status='p')
        # 注意在url里我们捕获了分类的id作为关键字参数(cate_id)传递给了CategoryView,传递的参数在kwargs属性中获取。
        for article in article_list:
            article.body = markdown2.markdown(article.body, )
        return article_list

    # 给视图增加额外的数据
    def get_context_data(self, **kwargs):
        kwargs['category_list'] = Category.objects.all().order_by('name')
        # 增加一个category_list,用于在页面显示所有分类,按照名字排序
        return super(CategoryView, self).get_context_data(**kwargs)

这里我们复用的凡主页的沙盘(因为展示的东西还是平等的),点击相应的归类,展示该分类下拥有文章。同样生成忘了而只要用户点击分类按钮跳反到分类页面的话,要指定
<a> 标签的 href 属性,善用 url 模板标签,防止硬编码 url,像这么:

<li class="cat-item">
  <a href="{% url 'blog:category' category.pk %}">{{ category.name }}</a>
</li>

分类显示效果如下,显示分类二下蛋的凡事稿子:

分类显示效果图

第一周:Django 学习小组:博客开发实战第一周教程 —— 编写博客的 Model
和首页面
第二周:Django 学习小组:博客开发实战第二周教程 ——
博客详情页面和分类页面
第三周:Django
学习小组:博客开发实战第三完善教程——文章列表分页和代码语法高亮
第四周:Django
学习小组:博客开发实战第四全面——标签云与文章归档
第五周:Django
学习小组:博客开发实战第五到——基于类的通用视图详解(一)

Django学习小组简介

django学习小组是一个推动 django 新手互相学习、互相帮助的团。

小组以单方面念书 django 的同时以联手就几独品种,包括:

  • 一个简单易行的 django 博客,用于发布小组每周的读书与支出文档;
  • django中国社区,为国内的 django 开发者们提供一个长期保护的
    django 社区;

点所说的这社区类于 segmentfault 和 stackoverflow
,但更加注意(只专注让 django 开发之题材)。

手上小组正在就第一个类型,本文就是欠类型第二全面的相关文档。

又多的消息要关注我们的 github
组织,本课程项目的竞相关源代码也都上传到
GitHub 的 blog-tutorial
分支
上。

并且,你吗可以参加我们的邮件列表
django_study@groups.163.com
,随时关心我们的动态。我们会用每周的详尽支出文档和代码通过邮件列表发出。

倘发生其他建议,欢迎提 issue,欢迎 fork,pr,当然也变忘了 star 哦!

心想事成思路

首先需吗评价(Comment)设计一个数库表,并编制相应的
Model,将评论和文章提到,再修上评论的视图,设置相应的 url 即可。

重写URL

对此一个发差不多个 app 的色,把富有的 urlpatterns 都在项目之 urls.py
似乎未是一个挺适合的选择,为之我们得以 blog 文件夹下新建一个文书
urls.py ,把同这 app 相关的 urlpatterns 都位居这文件里。
文本中之 urlpatterns 看不懂得暂时没关系,下面很快即见面介绍其。

# blog/urls.py
from django.conf.urls import url
from blog import views

urlpatterns = [
    url(r'^blog/$', views.IndexView.as_view(), name='index'),
    url(r'^blog/article/(?P<article_id>\d+)$', views.ArticleDetailView.as_view(), name='detail'),
    url(r'^blog/category/(?P<cate_id>\d+)$', views.CategoryView.as_view(), name='category'),
]
# 使用(?P<>\d+)的形式捕获值给<>中得参数,比如(?P<article_id>\d+),当访问/blog/article/3时,将会将3捕获给article_id,这个值会传到views.ArticleDetailView,这样我们就可以判断展示哪个Article了

接下来于路之 urls.py 中蕴藏(include)它:

# DjangoBlog/blog_project/urls.py
from django.conf.urls import url, include
from django.contrib import admin
from blog import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'', include('blog.urls', namespace='blog', app_name='blog'))
]
# 其中namespace参数为我们指定了命名空间,这说明这个urls.py中的url是blog app下的,这样即使不同的app下有相同url也不会冲突了。

这般,我们就算还写了 URL,看起是匪是重复起系统了?

唤醒:在阅读教程的进程遭到,如产生其他问题要访问我们种之
GithHub
或臧否留言为抱帮助,本课程的连带代码都整及传在 GitHub 的
blog-tutorial
分支
上,请点击链接获取。如果你对咱的教程或者项目起外改善建议,请您随时告知我们。更多交流请在我们的邮件列表
django_study@groups.163.com
和关怀我们以
GithHub
上之路。

本文首发于编程派微信公众号:编程派(微信号:codingpy)是一个留意Python编程的公众号,每天更新有关Python的国外教程以及优质书籍等选干货,欢迎关注。

属下去做什么?

迄今,我们完成了博客的首页,详情展示页和分类功能,基本的框架算是功德圆满了。接下来我们会为我们的
Blog
添加更多高档的功用,包括有标签云、文章归档、文章分页等。敬请期待我们下一样周到之课程。如果您盼也而的
Blog 添加另外更为新鲜之效能,也请随时告诉我们。代码获取请点击:GitHub
的 blog-tutorial
分支

评的表单

表单用来受服务器后台提交用户填写的数据,例如平时咱们看看底填登录、注册信息的页面就是一个报到、注册表单,用户填写表单信息后,点击提交按钮,表单中填入的始末就是见面卷入发送给服务器后台。我们要呢用户填写评论设置一个表单,django
的 form
模块为我们提供了自动生成表单的职能,如果对表单不熟悉请参见:法定文档:表单概述
,以了解基本的表单使用方法(如果你针对表单感觉甚生疏的话语)。下面我们利用
Django 的 ModelForm ( django ModelForm
介绍
)类为我们自动生成表单。首先以 blog 目录下新建一个 forms.py (和
models.py 同一目录)文件用来存放在 form 的代码:

blog/forms.py

from django import forms
from .models import Article, BlogComment


class BlogCommentForm(forms.ModelForm):
    class Meta:
        """指定一些 Meta 选项以改变 form 被渲染后的样式"""
        model = BlogComment # form 关联的 Model

        fields = ['user_name', 'user_email', 'body']
        # fields 表示需要渲染的字段,这里需要渲染user_name、user_email、body
        # 这样渲染后表单会有三个文本输入框,分别是输入user_name、user_email、body的输入框

        widgets = {
            # 为各个需要渲染的字段指定渲染成什么html组件,主要是为了添加css样式。
            # 例如 user_name 渲染后的html组件如下:
            # <input type="text" class="form-control" placeholder="Username" aria-describedby="sizing-addon1">

            'user_name': forms.TextInput(attrs={
                'class': 'form-control',
                'placeholder': "请输入昵称",
                'aria-describedby': "sizing-addon1",
            }),
            'user_email': forms.TextInput(attrs={
                'class': 'form-control',
                'placeholder': "请输入邮箱",
                'aria-describedby': "sizing-addon1",
            }),
            'body': forms.Textarea(attrs={'placeholder': '我来评两句~'}),
        }

协作推广

依照系列教程将首发于 编程派
微信公众号,编程派(微信号:codingpy)是一个注意Python编程的公众号,每天更新有关Python的海外教程以及优质书籍等选项干货,扫描下方二维码以关注:

编程派

看来图函数

此我们还是坚持以基于类的通用视图,由于涉及到评论表单的提交处理,因此我们利用
FormView。这里对 FormView 的采用稍作讲解。

每当 Django 的根据函数的视图中,涉及表单的处理的视图其逻辑一般是如此的:

def post_comment(request):
    if request.method =='POST':
        form = BlogCommentForm(request.POST)
        if form.is_valid():
            ...
        else:
            ...
     else:
        ...

就,首先判断用户是否由此表单 POST 了数量恢复,如果是,则冲 POST
过来的数额构建一个表单,如果数量证实合法(form.is_valid),则开创评论,否则回表单提交页。如果没
POST 数据,则开另外相应的作业。FormView 把这些逻辑做了咬合,无需写那多
if else 语句:

blog/views.py

from django.views.generic.edit import FormView

...

class CommentPostView(FormView):
    form_class = BlogCommentForm # 指定使用的是哪个form
    template_name = 'blog/detail.html' 
    # 指定评论提交成功后跳转渲染的模板文件。
    # 我们的评论表单放在detail.html中,评论成功后返回到原始提交页面。

    def form_valid(self, form):
        """提交的数据验证合法后的逻辑"""
        # 首先根据 url 传入的参数(在 self.kwargs 中)获取到被评论的文章
        target_article = get_object_or_404(Article, pk=self.kwargs['article_id'])

        # 调用ModelForm的save方法保存评论,设置commit=False则先不保存到数据库,
        # 而是返回生成的comment实例,直到真正调用save方法时才保存到数据库。
        comment = form.save(commit=False)

        # 把评论和文章关联
        comment.article = target_article
        comment.save()

        # 评论生成成功,重定向到被评论的文章页面,get_absolute_url 请看下面的讲解。
        self.success_url = target_article.get_absolute_url()
        return HttpResponseRedirect(self.success_url)

    def form_invalid(self, form):
        """提交的数据验证不合法后的逻辑"""
        target_article = get_object_or_404(Article, pk=self.kwargs['article_id'])

        # 不保存评论,回到原来提交评论的文章详情页面
        return render(self.request, 'blog/detail.html', {
            'form': form,
            'article': target_article,
            'comment_list': target_article.blogcomment_set.all(),
        })

为好地重定向回本提交评论的稿子详情页面,我们也文章(Article)的模子新增一个主意:get_absolute_url,调用该措施以抱该
Article 对应的 url,例如这是文章 1 的
url:http://localhost:8000/article/1,则调用后归来
/article/1,这样调用 HttpResponseRedirect 后以回该 url
下之章详情页。

blog/models.py

from django.core.urlresolvers import reverse

class Article(models.Model):
    STATUS_CHOICES = (
        ('d', 'Draft'),
        ('p', 'Published'),
    )

    ...

    class Meta:
        ordering = ['-last_modified_time']

    # 新增 get_absolute_url 方法
    def get_absolute_url(self):
        # 这里 reverse 解析 blog:detail 视图函数对应的 url
        return reverse('blog:detail', kwargs={'article_id': self.pk})

再者为了以详情页渲染一个评头论足表单,稍微修改一下 ArticleDetailView
的视图函数,把评论表单 form 插入模板上下文中:

blog/views.py

class ArticleDetailView(DetailView):
    model = Article
    template_name = "blog/detail.html"
    context_object_name = "article"
    pk_url_kwarg = 'article_id'

    def get_object(self, queryset=None):
        obj = super(ArticleDetailView, self).get_object()
        obj.body = markdown2.markdown(obj.body, extras=['fenced-code-blocks'], )
        return obj

    # 新增 form 到 context
    def get_context_data(self, **kwargs):
        kwargs['comment_list'] = self.object.blogcomment_set.all()
        kwargs['form'] = BlogCommentForm()
        return super(ArticleDetailView, self).get_context_data(**kwargs)

提示:在翻阅教程的过程中,如发生另问题要看我们种之
GithHub
或臧否留言为得帮助,本课程的相干代码已经整整上传在 GitHub 的
blog-tutorial
分支
。如果你针对咱的科目或者项目有任何改善建议,请你通过天天告知我们。更多交流请在我们的邮件列表
django_study@groups.163.com。

装模板文件

新增了一个 comment.html 文件为渲染评论表单和评价列表,并且修改了
detail.html 文件为以文章详情页显示评论表单和评价列表,修改了blog/tatic
下的 style.css 为评价添加样式,由于代码比较多,就不粘出来了,主要是 html
和 css 的前端相关代码,请到 GitHub 的 blog-tutorial
分支
更新相关的沙盘与静态资源文件。

迄今,整个评论功能的框架做好了,显示效果如下:

评说功能演示图

本这无非是一个评的框架,很多细节有待处理同周到,但好歹,用户可以吧咱的章发表评论意见了。

相关文章