BLOG
Enjoy when you can, and endure when you must.
APR 10, 2016/Django
用 Django 构建简易博客(三):专注功能的实现之博客列表

在我建立这个网站之初,我就发了一个系列的博客《Django 博客系统开发》,当时的想法是将自己所学和所实践的一些东西整理一下、记录下来。时至今日,三年的时光已在眨眼间过去,我发现这几篇文章在我网站的访问量排行中依然居高不下。说明这几年大家对 Python 和 Django 的关注度确实比较高并且有很多新的开发者加入其中,这当然要数是一个非常好的趋势。但技术是不断发展的,特别 Python 和 Django 都一直处于快速发展期,当年的文章中提到的方法很多已不再适用。因此萌发了做一个更新的想法,让更多的朋友关注最新的技术,而不是面对一个旧版冥思苦想。

用 Django 来快速搭建一个简易的博客,我一直认为是一个自我实践的好方法。就像我自己的网站一样,我在多个版本迭代中出现了一个从简到繁、又从繁到简的过程,这是我不断的在尝试和优化。只要愿意关注细节、乐意去思考,即使是一个很小的东西,也能从中收获不少。

当然,这里我用到了一个名词:实践。是的,要想做出一样东西,即使再简单,也需要基础来支撑。因此在开始实践前,务必要首先学习 Python 语言本身并对 Django 开发框架有一定的了解。
这也明确了本文所面向的读者:具备 Python 基础,对 Django 开发框架有一定了解,想要利用他来做一些基础实践的开发者。如果你还处于零基础阶段,建议阅读一下 Mark Lutz 的《Learning Python》以及 《The Django Book》两本书籍,他们能够引领你进入这个奇妙的世界。

另外,文章中会包含一些我个人的编码风格、见解与主张。因此并不一定要遵循。如果对其有一些独到的看法和改进意见,我们都可以随时沟通、共同改进。

 

第三篇:专注功能的实现之博客列表的展示

 

通过前面所做的各种铺垫,我们现在可以来专注功能的实现了,即剩下的 V 层和 T 层。这一部分我们着重关注博客列表页。首先来回顾一下需求:

需要有一个“博客列表”页来呈现发布的博客。博客要按发布时间的倒序来排列,每个博客都要包含标题、作者、分类、发布时间的显示(年-月-日 时:分)及节选的正文内容(前 100 个字)。点击单独的博客可以进入其详情页。

在实际的开发前,我们应能看到产品部门针对各页面所做的原型图,而 UI 部门也会遵循此完成设计图。因此我们就能清楚的知道页面中包含哪些元素、应如何摆放。但这里因为只是一个简单的演示,所以请允许我“索性随意发挥”了。


建立视图

视图就是所谓的 V 层,他负责分析并处理来自用户的请求,然后返回所需的结果。“博客列表”的视图显然是将数据库中的数据按需求中所需的发布时间的倒序取出,然后构造模板,最终将结果返回给用户。现在就让我们来编写它吧。

from django.shortcuts import render

from .models import Blog


def get_blogs(request):
    ctx = {
        'blogs': Blog.objects.all().order_by('-created')
    }
    return render(request, 'blog-list.html', ctx)

这里,我们使用了 Django 的 ORM 对博客数据进行查询,并依发布时间的倒序进行排列,然后将结果集(一个 QuerySet 对象)放在一个字典对象 ctx 的 blogs 中。然后调用 render 方法来渲染模板,其第一个参数始终接收 request 对象,第二个参数是要渲染的模板文件,第三个参数则是可选的上下文参数。注意观察视图函数的入参,其第一个参数一定是 request 对象,“这是一个不变的真理”。


定义模板

现在就只剩下 T 层没有完成了。废话不多说,在工程根目录下新建一个 templates 文件夹,并在其中新增一个 blog-list.html 文件,这个文件的名字要和之前视图函数中 render 方法的入参中定义的相对应。 然后打开并写入以下代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>My Blogs</title>
    <style>
        .blog {
            padding: 20px 0px;
        }
        .blog .info span {
            padding-right: 10px;
        }
        .blog .summary {
            padding-top: 20px;
        }
    </style>
</head>
<body>

<div class="header">
    <h1>My Blogs</h1>
</div>

{% for blog in blogs %}
    <div class="blog">
        <div class="title">
            <a href="#"><h2>{{ blog.title }}</h2></a>
        </div>
        <div class="info">
            <span class="category" style="color: #ff9900;">{{ blog.category.name }}</span>
            <span class="author" style="color: #4a86e8">{{ blog.author }}</span>
            <span class="created" style="color: #6aa84f">{{ blog.created|date:"Y-m-d H:i" }}</span>
        </div>
        <div class="summary">
            {{ blog.content|truncatewords:100 }}
        </div>
    </div>
{% endfor %}

</body>
</html>

正如你所看到的,我们作为上下文传入的参数在模板中可以以变量的形式直接访问。这里我们对 blogs 进行了一次 for 迭代,然后为每一篇博客生成一个 div 容器供其展示。在 div 内部又通过变量属性的访问来获取诸如博客标题、分类名称、作者、正文和发布时间等内容。注意这里用到了很多有意思的模板标签,这些都值得你去一步步挖掘。
另外,在博客标题上有一个 <a> 标签,其中的 href 属性只填写了一个固定的“#”。理论上来说,这里应该填入每篇博客对应的详情页链接,但因为到目前为止我们还没有实现出“博客详情”页,所以先这么临时处理,待之后再完善。

 

完成后续工作

现在,MTV 三层我们都已完成了,是时候验证一下到目前为止的成果。不过在此之前还有一个很重要的工作就是为这个视图指定一个发现它的路径,这样用户才能通过在浏览器的地址栏中输入地址并正确访问到他。这个重要的工作是由 URLConf 来帮助我们完成的。打开 myblog/urls.py 并在 urlpatterns 中增加一条:

urlpatterns = [
    ...

    url(r'^$', get_blogs, name='blog_get_blogs'),
]

这里定义的是在根路径下就可以访问到博客列表页。现在让我们重新运行开发服务器并打开浏览器尝试访问一下博客列表页。不过,如果我没猜错的话,这时屏幕上出现的内容并不是我们期待的。

这是 DEBUG 模式下(默认是开启的,通过 settings 中的 DEBUG 配置项来定义)的错误提示页,也就是传说中的 500 错误。在这种模式下,Django 会为我们打印出问题的根源所在:TemplateDoesNotExist。嗯?之前我们不是都定义好了的吗?为什么会说找不到呢?很简单,因为我们没有真正告诉 Django 应到哪里去寻找我们定义的模板文件。这需要在 settings 中的 TEMPLATES 配置项中声明。现在打开 myblog/settings.py 文件并将 TEMPLATES 配置项修改成如下这样:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': ['templates/'],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

重启开发服务器并再次访问,这时应该能看到久违的效果了:

是的!获得成就感就是这么简单。

 

发现更多

 

其实并没有那么简单

 

在阅读本章之后,可能会觉得很多地方非常含糊地一笔带过。其实我是故意的。在最初的时候我就说过“在开始实践前,务必要首先学习 Python 语言本身并对 Django 开发框架有一定的了解”,否则定会举步维艰,不知所措。要想实现一个功能,即使再简单(就像这里的博客列表,其示例代码仅有几行而已),也一定会用到很多基础的元素(比如为什么视图函数的第一个参数总是 request 对象)。而这些东西就是要事先从基础学习中去知晓的。


多样的模板标签

 

Django 的模板其实是非常有趣的,它提供了大量的内置模板标签和模板语言,值得去挖掘更多让实际使用更多样化。

 

面向对象的视图

 

在之前的示例代码中,视图是通过视图函数来定义的。但在 Django 1.5 之后,基于类的视图逐步取代了传统的视图函数。这是向 OOP 的思想靠拢,而基于类的视图也确实体现出了众多优势。并且通过挖掘 View 类的内在,可以更好的帮助我们理解 Django 在这一块的运行机制。所以推荐各位对其做一些深入的了解并将其运用到自己的项目中。

 

分页的实现

 

像“博客列表”这样的页面有一个很不可控的因素就是博客的数量。因此有必要考虑在这里做一个分页处理,让过多的内容分布到多个页面显示。这样可以减轻服务器的负担、带宽成本并提高用户的浏览体验。一个基础的分页可以借助 Django 提供的 pagination 模块来实现。可以通过查询官方文档获取更多的内容。

 

继续阅读下一篇《用 Django 构建简易博客(四):专注功能的实现之博客详情与评论​

COMMENTS
03/11From James

兄弟,坑有点深,才爬到静态文件这里,nginx不会 = = What's a fuck static!!

22/08From hgq

回答前面的人的问题:
views.py里有个错误:get_blogs里的blog应改为blogs,因为html文件里的for循环用的是blogs

11/08From Figger

为什么页面打开只有My Blogs呢

25/07From liping

最后只有MY BLOG 是因为数据库中没有内容嘛?用/admin/登录的话到底要改哪些表呢(都是自动生成的)

11/04From Liang

最后面访问服务器,显示页面只有Myblog?

06/11From From elsa

新增了几条博客内容,MyBlog页面只有Title,没有Bloglist?

25/09From q

确实没有定义,在第一行加上from blog.views import get_blogs

17/09From Execution

同样遇到function没定义的问题,我后来改成了
url(r'^index/$', 'blog.views.get_blogs'),
就可以了。。。

07/09From Danny

@liuxv: 是不是没有导入 get_blogs?

04/09From liuxv

url(r'^$', get_blogs, name='blog_get_blogs'),其中的 get_blogs 没有定义啊(我是初学者,在电脑上自己实践就这样显示的)

LEAVE COMMNT