Python Django入门(创建其他网页)

在本章中,你将学习如何使用

Django(http://djangoproject.com/ )来开发一个名为"学习笔记"(Learning Log)的项目,这是一个在线日志系统,让你能够记录所学习的有关特定主题的知识。

我们将为这个项目制定规范,然后为应用程序使用的数据定义模型。我们将使用Django的管理系统来输入一些初始数据,再学习编写视图和模板,让Django能够为我们

的网站创建网页。

Django是一个Web框架 ------一套用于帮助开发交互式网站的工具。Django能够响应网页请求,还能让你更轻松地读写数据库、管理用户等。

创建其他网页

制定创建网页的流程后,可以开始扩充"学习笔记"项目了。我们将创建两个显示数据的网页,其中一个列出所有的主题,另一个显示特定主题的所有条目。对于每个网页,我们

都将指定URL模式,编写一个视图函数,并编写一个模板。但这样做之前,我们先创建一个父模板,项目中的其他模板都将继承它。

模板继承

创建网站时,几乎都有一些所有网页都将包含的元素。在这种情况下,可编写一个包含通用元素的父模板,并让每个网页都继承这个模板,而不必在每个网页中重复定义这些通

用元素。这种方法能让你专注于开发每个网页的独特方面,还能让修改项目的整体外观容易得多。

父模板

我们首先来创建一个名为base.html的模板,并将其存储在index.html所在的目录中。这个文件包含所有页面都有的元素;其他的模板都继承base.html。当前,所有页面都包含的元素

只有顶端的标题。我们将在每个页面中包含这个模板,因此我们将这个标题设置为到主页的链接:

复制代码
<p>
❶ <a href="{% url 'learning_logs:index' %}">Learning Log</a>
</p>
❷ {% block content %}{% endblock content %}

这个文件的第一部分创建一个包含项目名的段落,该段落也是一个到主页的链接。为创建链接,我们使用了一个模板标签 ,它是用大括号和百分号({% %} )表示的。模板标

签是一小段代码,生成要在网页中显示的信息。在这个实例中,模板标签{% url 'learning_logs:index' %} 生成一个URL,该URL与learning_logs/urls.py中定义的名

为index 的URL模式匹配(见❶)。在这个示例中,learning_logs 是一个命名空间 ,而index 是该命名空间中一个名称独特的URL模式。

在简单的HTML页面中,链接是使用锚 标签定义的:

复制代码
<a href="link_url">link text</a>

让模板标签来生成URL,可让链接保持最新容易得多。要修改项目中的URL,只需修改urls.py中的URL模式,这样网页被请求时,Django将自动插入修改后的URL。在我们的项目

中,每个网页都将继承base.html,因此从现在开始,每个网页都包含到主页的链接。

在❶处,我们插入了一对块标签。这个块名为content ,是一个占位符,其中包含的信息将由子模板指定。

子模板并非必须定义父模板中的每个块,因此在父模板中,可使用任意多个块来预留空间,而子模板可根据需要定义相应数量的块。

注意  在Python代码中,我们几乎总是缩进四个空格。相比于Python文件,模板文件的缩进层级更多,因此每个层级通常只缩进两个空格。

子模板

现在需要重新编写index.html,使其继承base.html,如下所示:

复制代码
❶ {% extends "learning_logs/base.html" %}
❷ {% block content %}
<p>Learning Log helps you keep track of your learning, for any topic you're
learning about.</p>
❸ {% endblock content %}

如果将这些代码与原来的index.html进行比较,可发现我们将标题Learning Log替换成了从父模板那里继承的代码(见❶)。子模板的第一行必须包含标签{% extends %} ,让

Django知道它继承了哪个父模板。文件base.html位于文件夹learning_logs中,因此父模板路径中包含learning_logs。这行代码导入模板base.html的所有内容,让index.html能够指定要

在content 块预留的空间中添加的内容。

在❷处,我们插入了一个名为content 的{% block %} 标签,以定义content 块。不是从父模板继承的内容都包含在content 块中,在这里是一个描述项目"学习笔记"的

段落。在❸处,我们使用标签{% endblock content %} 指出了内容定义的结束位置。

模板继承的优点开始显现出来了:在子模板中,只需包含当前网页特有的内容。这不仅简化了每个模板,还使得网站修改起来容易得多。要修改很多网页都包含的元素,只需在

父模板中修改该元素,你所做的修改将传导到继承该父模板的每个页面。在包含数十乃至数百个网页的项目中,这种结构使得网站改进起来容易而且快捷得多。

注意  在大型项目中,通常有一个用于整个网站的父模板------base.html,且网站的每个主要部分都有一个父模板。每个部分的父模板都继承base.html,而网站的每个网

页都继承相应部分的父模板。这让你能够轻松地修改整个网站的外观、网站任何一部分的外观以及任何一个网页的外观。这种配置提供了一种效率极高的工作方式,

让你乐意不断地去改进网站。

显示所有主题的页面

有了高效的网页创建方法,就能专注于另外两个网页了:显示全部主题的网页以及显示特定主题中条目的网页。所有主题页面显示用户创建的所有主题,它是第一个需要使用数

据的网页。

URL模式

首先,我们来定义显示所有主题的页面的URL。通常,使用一个简单的URL片段来指出网页显示的信息;我们将使用单词topics,因此URL http://localhost:8000/topics/将返回显示所有

主题的页面。下面演示了该如何修改learning_logs/urls.py:

复制代码
"""为learning_logs定义URL模式"""
--snip--
urlpatterns = [
# 主页
url(r'^$', views.index, name='index'),
# 显示所有的主题
❶ url(r'^topics/$', views.topics, name='topics'),
]

我们只是在用于主页URL的正则表达式中添加了topics/ (见❶)。Django检查请求的URL时,这个模式与这样的URL匹配:基础URL后面跟着topics 。可以在末尾包含斜

杠,也可以省略它,但单词topics 后面不能有任何东西,否则就与该模式不匹配。其URL与该模式匹配的请求都将交给views.py中的函数topics() 进行处理。

视图

函数topics() 需要从数据库中获取一些数据,并将其发送给模板。我们需要在views.py中添加的代码如下:

复制代码
from django.shortcuts import render
❶ from .models import Topic
def index(request):
--snip--
❷ def topics(request):
"""显示所有的主题"""
❸ topics = Topic.objects.order_by('date_added')
❹ context = {'topics': topics}
❺ return render(request, 'learning_logs/topics.html', context)

我们首先导入了与所需数据相关联的模型(见❶)。函数topics() 包含一个形参:Django从服务器那里收到的request 对象(见❷)。在❸处,我们查询数据库------请求提

供Topic 对象,并按属性date_added 对它们进行排序。我们将返回的查询集存储在topics 中。

在❹处,我们定义了一个将要发送给模板的上下文。上下文是一个字典,其中的键是我们将在模板中用来访问数据的名称,而值是我们要发送给模板的数据。在这里,只有一个

键---值对,它包含我们将在网页中显示的一组主题。创建使用数据的网页时,除对象request 和模板的路径外,我们还将变量context 传递给render() (见❺)。

模板

显示所有主题的页面的模板接受字典context ,以便能够使用topics() 提供的数据。请创建一个文件,将其命名为topics.html,并存储到index.html所在的目录中。下面演示了

如何在这个模板中显示主题:

复制代码
{% extends "learning_logs/base.html" %}
{% block content %}
<p>Topics</p>
❶ <ul>
❷ {% for topic in topics %}
❸ <li>{{ topic }}</li>
❹ {% empty %}
<li>No topics have been added yet.</li>
❺ {% endfor %}
❻ </ul>
{% endblock content %}

就像模板index.html一样,我们首先使用标签{% extends %} 来继承base.html,再开始定义content 块。这个网页的主体是一个项目列表,其中列出了用户输入的主题。在标准

HTML中,项目列表被称为无序列表,用标签

表示。包含所有主题的项目列表始于❶处。

在❷处,我们使用了一个相当于for 循环的模板标签,它遍历字典context 中的列表topics 。模板中使用的代码与Python代码存在一些重要差别:Python使用缩进来指出哪些

代码行是for 循环的组成部分,而在模板中,每个for 循环都必须使用{% endfor %} 标签来显式地指出其结束位置。因此在模板中,循环类似于下面这样:

复制代码
{% for item in list %}
do something with each item
{% endfor %}

在循环中,我们要将每个主题转换为一个项目列表项。要在模板中打印变量,需要将变量名用双花括号括起来。每次循环时,❸处的代码{{ topic }} 都被替换为topic 的当

前值。这些花括号不会出现在网页中,它们只是用于告诉Django我们使用了一个模板变量。HTML标签

  • 表示一个项目列表项,在标签对

    内部,位于标

  • 之间的内容都是一个项目列表项。

    在❹处,我们使用了模板标签{% empty %} ,它告诉Django在列表topics 为空时该怎么办:这里是打印一条消息,告诉用户还没有添加任何主题。最后两行分别结束for 循

    环(见❺)和项目列表(见❻)。

    现在需要修改父模板,使其包含到显示所有主题的页面的链接:

    复制代码
    <p>
    ❶ <a href="{% url 'learning_logs:index' %}">Learning Log</a> -
    ❷ <a href="{% url 'learning_logs:topics' %}">Topics</a>
    </p>
    {% block content %}{% endblock content %}

    我们在到主页的链接后面添加了一个连字符(见❶),然后添加了一个到显示所有主题的页面的链接------使用的也是模板标签url (见❷)。这一行让Django生成一个链接,它

    与learning_logs/ urls.py中名为topics 的URL模式匹配。

    现在如果你刷新浏览器中的主页,将看到链接Topics。单击这个链接,将看到类似于图18-4所示的网页。

    显示特定主题的页面

    接下来,我们需要创建一个专注于特定主题的页面------显示该主题的名称及该主题的所有条目。同样,我们将定义一个新的URL模式,编写一个视图并创建一个模板。我们还将

    修改显示所有主题的网页,让每个项目列表项都是一个链接,单击它将显示相应主题的所有条目。

    URL模式

    显示特定主题的页面的URL模式与前面的所有URL模式都稍有不同,因为它将使用主题的id 属性来指出请求的是哪个主题。例如,如果用户要查看主题Chess(其id 为1)的详细

    页面,URL将为http://localhost:8000/topics/1/。下面是与这个URL匹配的模式,它包含在learning_logs/urls.py中:

    复制代码
    --snip--
    urlpatterns = [
    --snip--
    # 特定主题的详细页面
    url(r'^topics/(?P<topic_id>\d+)/$', views.topic, name='topic'),
    ]

    我们来详细研究这个URL模式中的正则表达式------r'^topics/(?P<topic_id>\d+)/$' 。r 让Django将这个字符串视为原始字符串,并指出正则表达式包含在引号内。这个

    表达式的第二部分(/(?P<topic_id>\d+)/ )与包含在两个斜杠内的整数匹配,并将这个整数存储在一个名为topic_id 的实参中。这部分表达式两边的括号捕获URL中的

    值;?P<topic_id> 将匹配的值存储到topic_id 中;而表达式\d+ 与包含在两个斜杆内的任何数字都匹配,不管这个数字为多少位。

    发现URL与这个模式匹配时,Django将调用视图函数topic() ,并将存储在topic_id 中的值作为实参传递给它。在这个函数中,我们将使用topic_id 的值来获取相应的主

    题。

    视图

    函数topic() 需要从数据库中获取指定的主题以及与之相关联的所有条目,如下所示:

    复制代码
    --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)

    这是第一个除request 对象外还包含另一个形参的视图函数。这个函数接受正则表达式(?P<topic_id>\d+) 捕获的值,并将其存储到topic_id 中(见❶)。在❷处,我

    们使用get() 来获取指定的主题,就像前面在Django shell中所做的那样。在❸处,我们获取与该主题相关联的条目,并将它们按date_added 排序:date_added 前面的减号

    指定按降序排列,即先显示最近的条目。我们将主题和条目都存储在字典context 中(见❹),再将这个字典发送给模板topic.html(见❺)。

    注意  ❷处和❸处的代码被称为查询,因为它们向数据库查询特定的信息。在自己的项目中编写这样的查询时,先在Django shell中进行尝试大有裨益。相比于编写视

    图和模板,再在浏览器中检查结果,在shell中执行代码可更快地获得反馈。

    模板

    这个模板需要显示主题的名称和条目的内容;如果当前主题不包含任何条目,我们还需向用户指出这一点:

    复制代码
    {% 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, 2015 23:00。接下来的一行显示text 的完整值,而不仅仅是entry 的前

    50个字符。过滤器linebreaks (见❺)将包含换行符的长条目转换为浏览器能够理解的格式,以免显示为一个不间断的文本块。在❻处,我们使用模板标签{% empty %} 打

    印一条消息,告诉用户当前主题还没有条目。

    将显示所有主题的页面中的每个主题都设置为链接

    在浏览器中查看显示特定主题的页面前,我们需要修改模板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/。

    如果你刷新显示所有主题的页面,再单击其中的一个主题,将看到类似于下图所示的页面。

相关推荐
数据智能老司机6 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机7 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机7 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机7 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
c8i8 小时前
drf初步梳理
python·django
每日AI新事件8 小时前
python的异步函数
python
这里有鱼汤9 小时前
miniQMT下载历史行情数据太慢怎么办?一招提速10倍!
前端·python
databook18 小时前
Manim实现脉冲闪烁特效
后端·python·动效
程序设计实验室18 小时前
2025年了,在 Django 之外,Python Web 框架还能怎么选?
python
倔强青铜三20 小时前
苦练Python第46天:文件写入与上下文管理器
人工智能·python·面试