接下来,需要创建一个专注于特定主题的页面。
1.定义URL模式
显示特定主题的页面的URL模式与前面的所有URL模式都稍有不同,因为它使用主题的id 属性来指出请求的是哪个主题。例如,如果用户要查看主题Chess(其id 为1)的详细页面,URL将为http://localhost:8000/topics/1/。下面是与这个URL匹配的模式,应将其放在learning_logs/ urls.py中:
urls.py
--snip--
urlpatterns = [
--snip--
# 特定主题的详细页面。
path('topics/<int:topic_id>/', views.topic, name='topic'),
]
其中,字符串'topics/<int:topic_id>/' 的第一部分让Django查找在基础URL后包含单词topics的URL,第二部分(/<int:topic_id>/ )与包含在两个斜杠内的整数匹配,并将这个整数存储在一个名为topic_id 的实参中。
发现URL与这个模式匹配时,Django将调用视图函数topic() ,并将存储在topic_id 中的值作为实参传递给它。在这个函数中,将使用topic_id 的值来获取相应的主题。
2.视图
views.py
--snip--
❶ def topic(request, topic_id):
"""显示单个主题及其所有的条目。"""
❷ topic = Topic.objects.get(id=topic_id)
❸ entries = topic.entry_set.order_by('-date_added')
❹ context = {'topic': topic, 'entries': entries}
❺ return render(request, 'learning_logs/topic.html', context)
这个函数接受表达式/<int:topic_id>/ 捕获的值,并将其存储到topic_id 中(见❶)。在❷处,使用get() 来获取指定的主题,就像前面在Django shell中所做的那样。在❸处,获取与该主题相关联的条目,并根据date_added 进行排序:date_added 前面的减号指定按降序排列,即先显示最近的条目。将主题和条目都存储在字典context 中(见❹),再将这个字典发送给模板topic.html(见❺)。
注意 ❷处和❸处的代码称为查询 ,因为它们向数据库查询了特定的信息。在自己的项目中编写这样的查询时,先在Django shell中进行尝试大有裨益。比起先编写视图和模板、再在浏览器中检查结果,在shell中执行代码可更快获得反馈。
3.模板
这个模板需要显示主题的名称和条目的内容。如果当前主题不包含任何条目,还需向用户指出这一点。新建topic.html:
topic.html
{% extends 'learning_logs/base.html' %}
{% block content %}
❶ <p>Topic: {{ topic }}</p>
<p>Entries:</p>
❷ <ul>
❸ {% for entry in entries %}
<li>
❹ <p>{{ entry.date_added|date:'M d, Y H:i' }}</p>
❺ <p>{{ entry.text|linebreaks }}</p>
</li>
❻ {% empty %}
<li>There are no entries for this topic yet.</li>
{% endfor %}
</ul>
{% endblock content %}
像这个项目的其他页面一样,这里也继承了base.html。接下来,显示当前的主题(见❶),它存储在模板变量{{ topic }} 中。为什么可以使用变量topic 呢?因为它包含在字典context 中。接下来,定义一个显示每个条目的项目列表(见❷),并像前面显示所有主题一样遍历条目(见❸)。
每个项目列表项都将列出两项信息:条目的时间戳和完整的文本。为列出时间戳(见❹),我们显示属性date_added 的值。在Django模板中,竖线(| )表示模板过滤器 ,即对模板变量的值进行修改的函数。过滤器date: 'M d, Y H:i' 以类似于这样的格式显示时间戳:January 1, 2018 23:00。接下来的一行显示text 的完整值,而不仅仅是前50字符。过滤器linebreaks (见❺)将包含换行符的长条目转换为浏览器能够理解的格式,以免显示为不间断的文本块。在❻处,使用模板标签{% empty %} 打印一条消息,告诉用户当前主题还没有条目。
4.将显示所有主题的页面中的主题设置为链接
在浏览器中查看显示特定主题的页面前,需要修改模板topics.html,让每个主题都链接到相应的页面。
topics.html
--snip--
{% for topic in topics %}
<li>
<a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}</a>
</li>
{% empty %}
--snip--
我们使用模板标签url 根据learning_logs 中名为'topic' 的URL模式生成了合适的链接。这个URL模式要求提供实参topic_id ,因此在模板标签url 中添加了属性topic.id 。现在,主题列表中的每个主题都是链接了,链接到显示相应主题的页面,如http://localhost:8000/ topics/1/。
如果现在刷新显示所有主题的页面,再单击其中的一个主题,查看效果。
注意 topic.id和topic_id之间存在细微而重要的差别。表达式topic.id检查主题并获取其ID值,而在代码中,变量topic_id是指向该ID的引用。使用ID时如果出现错误,请确保正确地使用了这两个表达式。