我们现在做的不是那种单独给某人使用的博客系统,而是一个多用户的系统了,即每个注册用户都可以发布文章。那么,如果读者想查看某个作者的全部文章,该如何做?这是一个非常合理的功能需求,如下图所示。
既然要查阅某个作者的文章,那么就需要在请求时提供该作者的用户名,这种查找方式我们已经有经验了,读者如果忘记了过去的经验,请查阅本书前面的相关内容。
4.2.1 查看某作者的文章列表
首先要修改./article/list_views.py文件中的article_titles()视图函数,代码如下。
1 def article_titles(request,username=None): #①
2 if username:
3 user = User.objects.get(username=username) #②
4 article_title = ArticlePost.objects.filter(author=user) #③
5 else:
6 article_title = ArticlePost.objects.all()
语句①的参数列表用了username=None,这种方式兼顾两种可能,一种是直接请求所有文章标题,另一种是请求某个用户(作者)的文章标题。
语句②用于得到某个用户(作者)对象,并且用在语句③中,因为在ArticlePost类中,有author=models.ForeignKey(User,related_name="article")。
在模板文件中对作者名字增加超链接,实现单击作者名字查看该作者全部文章标题的功能。编辑./templates/article/list/article_titles.html文件,for循环块内的代码修改如下。
1 <div class="list-group">
2 <a href="{{article.get_url_path}}" class="list-group-item active">
3 <h4 class="list-group-item-heading"></h4></a>
4 <p class="list-group-item-text">作者:<a href="{% url 'article:author_articles'article.author.username %}">
5 {{article.title}}{{article.author.username}}</a></p>
6 <p class="list-group-item-text">概要:{{article.body|slice:'70'|linebreaks}}</p>
7
8</div>
这里依然使用了熟悉的{% url 'article:author_articles' article.author.username %}方式,并通过article.author.username传递username的值。
接着在./article/urls.py文件中增加URL配置代码。
1 re_path('list-article-titles/(?P<username>[-\w]+)/$',list_view.article_titles,name="author_articles"),
上述步骤完成后,检查Django服务是否已经运行。在服务运行的情况下访问项目,单击导航栏中的“文章”选项,可以查看所有文章标题。在每篇文章标题下面,作者的名称已经是超链接了,单击它可以查看该作者的全部文章标题,如下图所示。
上述步骤完成后,检查Django服务是否已经进行。在服务运行的情况下访问项目,单击导航栏中的“文章”选项,可以查看所有文章标题。在每篇文章标题下面,作者的名称已经是超链接了,单击它可以查看该作者的全部文章标题,如下图所示。
请认真观察浏览器地址栏中URL的特点。
至此,还没有结束。当用户查看某篇文章内容时,里面也有作者名称,还要做同样的链接。所以,要编辑./templates/article/list/article_detail.html文件,找到显示用户名的HTML标记符,用跟上面一样的处理方法,增加<a href="{% url 'article:author_articles' article.author.username %}">超链接。
不要忘记保存。刷新页面后测试新功能,这样就实现了本节开头所提出的功能需求。
其实,这里所呈现的作者文章标题列表页可以继续美化,比如显示该作者的有关信息,我们曾经在用户个人信息设置中让用户填写了不少内容呢!要实现这个功能,就需要对视图函数进行重写,再次编辑./article/list_views.py文件中的article_titles()函数。为了避免读者在反复修改时出现差错,下面列出全部代码。
1 def article_titles(request,username=None):
2 if username:
3 user = User.objects.get(username=username)
4 article_title = ArticlePost.objects.filter(author=user)
5 try:
6 userinfo = user.userinfo #④
7 except:
8 userinfo = None
9 else:
10 article_title = ArticlePost.objects.all()
11
12 paginator = Paginator(article_title,2)
13 page = request.GET.get('page')
14 try:
15 current_page = paginator.page(page)
16 articles = current_page.object_list
17 except PageNotAnInteger:
18 current_page = paginator.page(1)
19 articles = current_page.object_list
20 except EmptyPage:
21 current_page = paginator.page(paginator.num_pages)
22 articles = current_page.object_list
23
24 if username:
25 return render(request,"article/list/author_articles.html",{"articles":articles,"page":current_page,
26 "userinfo":userinfo,"user":user}) #⑤
27 return render(request,"article/list/article_titles.html",{"articles":articles,"page":current_page})
语句④用于创建该用户的UserInfo对象实例。读者是否还记得我们在编写关于用户信息的数据模型时,创建了UserInfo数据模型类,并且跟User类建立了“一对一”的关系。要通过User类的实例得到UserInfo类的信息,可以使用类似user.userinfo的模式,其中user是User类的实例,而userinfo就是UserInfo,只不过这里一定要用英文小写,这是规定。
在这里使用了try...except...,主要原因是有的用户可能没有填写自己的个人信息。
语句⑤则是当判断要访问某个用户的文章标题列表时,将userinfo也传到模板文件里。
接下来创建一个新的模板文件。在./templates/article/list目录中创建名为author_articles.html的文件,并输入如下代码。
1 {% extends "base.html" %}
2 {% load staticfiles %}
3 {% block title %}articles{% endblock %}
4 {% block content %}
5 <div class="row text-center vertical-middle-sm">
6 <h1>阅读,丰富头脑,善化行为</h1>
7 </div>
8 <div class="container">
9 <div class="col-md-8">
10 {% for article in articles %}
11 <div class="list-group">
12 <a href="{{article.get_url_path}}" class="list-group-item active">
13 <h4 class="list-group-item-heading">{{article.title}}</h4>
14 </a>
15 <p class="list-group-item-text">作者:<a href="{% url 'article:author_articles' article.author.username %}">{{article.author.username}}</a> </p>
16 <p class="list-group-item-text">概要:{{article.body|slice:'70'|linebreaks}}</p>
17 </div>
18 {% endfor %}
19 {% include "paginator.html" %}
20 </div>
21 <div class="col-md-4">
22 <div>
23 {% if userinfo.photo %}
24 <img src="{{ userinfo.photo | striptags }}" class="img-circle" id="my_photo" name="user_face">
25 {% else %}
26 <img name="user_face" src="{% static 'images/newton.jpg' %}" class="img-circle" style = "width:150px;" id="my_photo">
27 {% endif %}
28 </div>
29 <div>
30 <p>{{ user.username }}</p>
31 {% if userinfo %}
32 <p>{{ userinfo.company }}</p>
33 <p>{{ userinfo.aboutme }}</p>
34 {% else %}
35 <p>这个作者太懒了,什么也没有留下。</p>
36 {% endif %}
37 </div>
38 </div>
39 </div>
40 {% endblock %}
在文章标题列表页面的右边,显示该作者的某些信息,相关代码与作者的个人信息代码相似。
最后还要看一下效果,如下图所示。
4.2.2 知识点
1、数据库简介
作为一名网站开发程序员,必须要了解数据库知识,不要认为在Django中通过ORM操作数据库就足够了,这仅适用于某些项目,可能在有其他要求的醒目上没救不能使用ORM了,因此必须了解数据库并使用SQL。
- SQLite
SQLite(http://www.sqlite.org)是一个轻型数据库,它不需要独立的数据库服务器,可以在所有主要的操作系统上运行。在本书的实例中就采用了这种数据库。读者可以查询项目根目录,它的操作与文件操作一样。
虽然SQLite比较袖珍,但可以支持高达2TB大小的数据库,每个数据库都是以单个文件的形式存在的,这些数据以B-Tree数据结构形式存储在磁盘上。尽管如此,读者也不要在真是的项目中使用它,除非保存的数据确实很少。
- PostgreSQL
PostgreSQL(https://www.postgresql.org)号称是“世界上最先进的开源数据库”,之所以如此,首先因为它是开源的,曾经还有一个号称开源的MySQL,但“它变了”,现在只有PostgreSQL能扛起开源的大旗了。其次,PostgreSQL的确有它的特点,比如它的数据类型非常丰富,很多数据库都比不上它;它有专门的IP类型,别的数据库没有。再次,也是最值得一提的,PostgreSQL有用一支非常货源的开发队伍,目前的提交人员已经超过三十人,特别是有众多hacker的积极参与。PostgreSQL的稳定性非常高,受到了开发者的青睐。“稳定压倒一切”,是我们选择数据库的一个重要标准。当然,一般数据库有的PostgreSQL一样也不少,比如适合众多操作系统、有众多接口等。
总之,PostgreSQL值得学习研究,更值得拥有。
- MySQL
MySQL(https://www.mysql.com)曾经是做网站开发的程序员最喜欢的数据库,虽然现在仍有很多网站中哎使用它,但毕竟是属于Oracle旗下的产品了,不再是自由软件了。目前,Oracle采用了双授权政策,将MySQL分为社区版和商业版,一些中小网站也在使用它。MySQL有着很多优点,所以在网站开发中依然有着举足轻重的地位,特别是在Linux作为操作系统、Apache或Nginx作为Web服务器、MySQL作为数据库、PHP/Perl/Python作为服务器端脚本解释器这种绝配组合中,地位相当稳固。
2、数据库设置
本书中示例的项目采用了Django项目创建之后的默认数据库SQLite,除这个数据库外,Django项目还支持使用常见的PostgreSQL、MySQL和Oracle。另外,通过第三方模块,还可以支持IBM DB2、Microsoft SQL Server等。
数据库的位置在settings.py(在本书的项目中,位置在./testsite/settings.py),设置DATABASES变量的值即可。下面是默认的SQLite数据库配置。
1 DATABASES = {
2 'default': {
3 'ENGINE': 'django.db.backends.sqlite3',
4 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
5 }
6 }
如果是PostgreSQL数据库,则可以参考如下配置方式。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql', #数据库服务器
'NAME': 'mydatabase', #自己为数据库确定一个名称
'USER':'mydatabaseuser', #登录数据库的用户名
'PASSWORD':'mypassword', #上述用户登录数据库的密码
'HOST':'127.0.0.1',
'PORT':'5432',
}
}