Web应用程序

发布于 2022-04-08  2.5k 次阅读


Django入手

当今网站实际上都是富应用程序(rich application),就像成熟的桌面应用程序一样。
Python提供了一组开发Web应用程序的卓越工具。
Django是一个Web框架,即以套旨在帮助开发交互式网站的工具。
本项目将通过使用Django来开发一个名为“学习笔记”的项目。
该项目将指定规范,然后为应用程序使用的数据定义模型,使用Django的管理系统来输入一些初始数据,再学习编写视图和模板,让Django能够为网站创建页面。
Django能够响应页面请求,话能够轻松地读写数据库、管理用户,等等。

建立项目

建立项目时,首先要以规范的方式对项目进行描述,再建立虚拟环境,以便在其中创建项目。

制定规范

完整的规范详细说明了项目的目标,阐述了项目的功能,并讨论了项目的外观和用户界面。与任何良好的项目规划和商业计划书一样,规范突出重点,帮助避免项目偏离轨道。
列出明确目标,突出开发重点:

编写一个名为‘学习笔记’的Web应用程序,让用户记录感兴趣的主题界面,并在学习每个主题的过程中添加日志条目。‘学习笔记’的主页对这个网站进行描述,并邀请用户注册或登录。
用户登录后,可创建新主题、添加新条目以及阅读既有的条目。

建立虚拟环境

激活虚拟环境

安装Django

在Django中创建项目

执行如下命令新建一个项目:

(webapp) F:\Pycharm-project\webapp>django-admin startproject learning_log .

file
该命令让Django新建一个名为learning_log的项目。这个命令末尾的句点让新项目使用何使的目录结构,这样开发完成后可轻松地将应用程序部署到服务器。

注意:这个句点不能省略,否则部署应用程序时候将遭遇一些配置问题。如果忘记了,要删除已创建的文件和文件夹,再重新运行这个命令。

settings.py指定Django如何与系统交互以及如何管理项目。在开发项目的过程中,将修改其中一些设置,并添加一些设中。文件url.py告诉Django,应创建哪些页面来响应浏览器请求,文件wsgi.py帮助Django提供他的创建文件,这个文件名是Web服务器网关接口的首字母缩写。
asgi:https://www.django.cn/article/show-28.html

创建数据库

Django将大部分与项目相关的信息存储在数据库中,因此需要创建一个供Django使用的数据库。
file
file
将修改数据库称为迁移数据库,首次执行命令migrate时,将让Django确保数据库与项目的当前状态匹配。在使用SQLite的项目中首次执行这个命令时,Django将创建一个数据库。①处,Django指出它将准备好数据库,用于存储执行管理和身份验证任务所需的信息。
看到Django又创建了一个文件db.sqlite3。SQLite是一种使用单个文件的数据库,是编写简单应用程序的理想选项。因为它可以不用太关注数据库管理的问题。

查看项目

下面来核实Django正确地创建了项目,使用命令runserver查看项目的状态,如下所示:
file

file
Django启动了一个名为development serve的服务器,能够查看系统中的项目。在浏览器输入URL请求页面,该Django服务器将进行响应,生成核实的页面,并将其发送给浏览器。

System check identified no issues (0 silenced).
April 08, 2022 - 17:32:48

处通过检查确认正确地创建了项目。在

Django version 4.0.3, using settings 'learning_log.settings'

指出使用的Django版本以及当前使用的设置文件的名称。并在后面指出项目在计算机8000前端口侦听请求。

如果出现错误消息:That port is already in use。执行命令
python manage.py runserver 8001。

创建应用程序

Dijango项目由一系列应用程序组成,它们协同工作让项目称为一个整体。
当前,在前面打开的终端窗口中运行着runserve,再打开一个终端窗口。切换到manage.py所在的目录,激活虚拟环境,再执行命令startapp:
file

file
命令startapp appname让Django搭建创建应用程序所需的基础设施。新创建的文件夹中包含重要的文件是models.py、admin.py和views.py使用models.py来定义要再程序中管理的数据。

定义模型

每位用户都需要再学习笔记中创建很多主题,用户输入的每个条目都与特定主题相关联,这些条目将以文本的方式显示。需要存储每个条目的时间戳,以告诉用户是什么时候创建的。
打开models.py

from django.db import models

# Create your models here.

这里导入了模块models,并让创建自己的模型。模型告诉Django如何处理应用程序中存储的数据。在代码层面,模型就是一个类,包含属性和方法。
下面是表示用户将存储的主题的模型:

from django.db import models

# Create your models here.
class Topic(models.Model):
    """用户学习的主题"""
    text = models.CharField(max_length=200)
    date_added = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        """返回模型的字符串表示。"""
        return self.text

创建了一个名为Topic的类,它继承Model,即Django中定义了模型的基本功能的类。
属性text是一个CharField————由字符组成的数据,即文本。需要存储少量文本,如名称、标题或城市时。可以使用CharField。定义CharField属性时,必须告诉Django该在数据库中预留多少空间。这里将max_length设置为了200字符。这对存储大多数主题名来说够了。
属性date_added是一个DateTimeField————记录日期和时间的数据。传递了实参auto_now_add=True,每当用户创建新主题时,Django都会将这个属性自动设置为当前日期和时间。

注意:要获悉可在模型中使用的各种字段,参阅Django Model Field Reference。

需要告诉Django,默认使用哪个属性来显示有关主题的信息。Django调用方法str()来显示模型的简单表示,这里编写方法str()它返回存储在属性text中的字符串。

激活模型

要使用这些模型,必须让Django将前述应用程序包含在项目中。为此,打开settings.py,其中有个片段告诉Django哪些应用程序被安装到了项目中并将协同工作:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

修改后:

INSTALLED_APPS = [
    # 我的应用程序
    'learning_logs',
    # 默认添加的应用程序
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

通过将应用程序编组,在项目不断增大,包含更多的应用程序时,有助于对应用程序进行跟踪。这里新建了一个名为“我的应用程序”的片段,当前它只包含应用程序learn_logs。
务必将自己创建的应用程序放在默认应用程序前面,这样能够覆盖默认应用程序的行为。
接下来,需要让Django修改数据库,示器能够存储与模型Topic相关的信息,为此:
file

file
命令makemigrations让Django确定如何修改数据库,使其能够存储与前面定义的新模型相关联的数据。
输出表明Django创建了一个名为0001_inital.py的迁移文件,这个文件将在数据库中为模型Topic创建一个表。
下面这种迁移,让Django替我们修改数据库:
file
这个命令的大部分输出与首次执行命令migrate的输出相同,需要检查的是红色标记。Django指出为learning_logs应用迁移时一切正常。
每当需要修改“学习笔记”管理的数据时,都采取如下三个步骤:
修改models.py
对learn_logs调用makemigrations
以及让Django迁移项目

Django管理网站

Django提供的管理网站(admin site)可以轻松地处理模型。网站管理员可以使用管理网站,但普通用户不能使用。

1.创建超级用户

Django允许创建具备所有权限的用户,即超级用户,权限决定了用户可执行的操作。最严格的权限设置只允许用户阅读网站的公开信息,注册用户通常可阅读自己的私有数据,还可以查看一些只有会员才能查看的信息。为有效地管理Web应用程序,网站所有者通常要访问网站存储的所有信息。优秀的管理员会小心对待用户的敏感信息,因为用户极其新人自己访问的应用程序。
为Django中创建超级用户,执行命令:
file

注意:一些敏感信息可能会向网站管理远隐藏,例如,Django并不存储你输入的密码,而是存储从该密码派生出来的一个字符串,称为散列值。每当输入密码,Django都计算器散列值,并将结果与存储的散列值进行比较。
如果两个值相同通过验证。由于存储的是散列值,即便黑客获取了网站数据库的访问权,也只能获取其中存储的散列值无法获得密码。
在网站配置正确的情况下,几乎无法根据散列值推导出原始密码。

2.向管理网站注册模型

Django自动在管理网站中添加了一些模型,如User和Group。但对于我们创建的模型,必须手工进行注册。
我们创建应用程序learning_logs时,Django在models.py所在的目录中创建了一个名为admin.py的文件:

from django.contrib import admin

# Register your models here.

为向管理网站注册Topic,输入:

from django.contrib import admin

from .models import Topic

admin.site.register(Topic)

这些代码首先导入要注册的模型Topic。models前面的句点让Django在admin.py所在的目录中查找models.py.admin.site.register()让Django通过管理网站管理模型。
使用超级用户账号管理访问网站:访问http://localhost:8000/admin/
输入刚创建的超级用户。
这个页面能够让你添加和修改用户和用户组。还可以管理刚才定义的模型Topic相关的数据。
file

注意:在编写代码途中遇到网页打不开问题。
关闭所有终端,重新打开环境,允许runserver

3.添加主题

单机Topics进入主题页面,单击Topics进入主题页面,单击Add
file

定义模型Entry

要记录学到的编程语言与数学知识,用户必须能够在学习笔记中添加条目,为此,需要定义相关的模型。每个条目都与特定主题相关联,这种关系称为多对一关系,即多个条目可以关联到同一个主题。
下面是Entry代码。

from django.db import models

# Create your models here.
class Topic(models.Model):
    """用户学习的主题"""
    text = models.CharField(max_length=200)
    date_added = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        """返回模型的字符串表示。"""
        return self.text

class Entry(models.Model):
    """学到的有关某个主题的具体知识。"""
    topic = models.ForeignKey(Topic,on_delete=models.CASCADE)
    text = models.TextField()
    date_add = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name_plural = 'entries'

    def __str__(self):
        """返回模型的字符串表示。"""
        return f"{self.text[:50]}..."

和Topic一样,Entry也继承了Django基类Model。第一个属性topic是个ForeignKey实例。外键(foreign key)是一个数据库术语,它指向数据库中的另一条记录,这里是将每个条目关联到特定主题。创建每个主题时,都分配了一个键(ID),需要在两项数据之间建立联系。Django使用与每项信息相关联的键。下面将根据这些联系获取与特定主题相关联的所有条目。实参on_delete=models.CASCADE让Django在删除主题的同时删除所有与之关联的条目,这称为级联删除(cascading delete)。
属性text,它是一个TextField实例。这种字段的长度不受限制,因为我们不想显示条目的长度。属性date_added让我们能够按创建顺序呈现条目,并在每个条目旁边放置时间戳。
在Entry类中嵌套了Meta类,Meta存储于管理模型的额外信息。在这里,它让我们能够设置一个特殊属性,让Django在需要时使用Entries来表示多少个条目。如果没有这个类,Django将使用Entrys来表示多少个条目。
方法str()告诉Django,呈现条目时应显示哪些信息。条目包含的文本可能很长,因此让Django只显示text的前50字符,还添加了一个省略号,指出显示并非整个条目。

迁移模型Entry

添加新模型后,需要再此迁移数据库。
file
生成一个新的迁移文件0002_entry.py,它告诉Django如何修改数据库,使其能够存储与模型Entry相关的信息。

向管理网站注册Entry

还需要注册模型Entry。为此们需要将admin.py修改成类似:

from django.contrib import admin

from .models import Topic,Entry

admin.site.register(Topic)
admin.site.register(Entry)
# Register your models here.

返回到站点,将看到Learning_Logs下列出了Entries。
file

Django shell

输入一些数据后,就可以通过交互式终端会话以编程方式查看这些数据了。这种交互环境称为Django shell,是测试项目和排除故障的理想之地。
file
在活动状态的虚拟环境状态中执行,命令python manage.py shell启动Python解释器。能够探索存储在项目数据库中的数据,这里导入了模块learning_logs.models中的模型Topic。再使用方法Topic.object.all()获取模型Topic的所有实例。这将返回一个称为查询集(queryset)的列表。
可以像遍历列表一样遍历查询集,下面演示了如何查看分配给每个主题对象的ID:
file
知道主题对象的ID后,就可以使用方法Topic.objects.get()获取该对象并查看其属性。
file
还可以查看与主题相关联的条目.前面给模型Entry定义了属性topic。这是一个Foreignkey,将与主题关联起来。利用这种关联,Django能够获取与特定主题相关联的所有条目。
file
要通过外键关系获取数据,可使用相关模型的小写名称,下划线和单词set。例如,假设有模型Pizza和Topping,而Topping通过一个外键关联到Pizza。如果有一个名为my_pizza的Pizza对象,就可使用my_pizza.topping_set.all()来获取这张比萨的所有配料。
编写用户请求的页面时,使用这种语法。确认代码能获取所需的数据时,shell很有帮助。如果代码再shell中符合预期没那么在简单的shell环境中排除问题要比在生成页面文件中排除问题容易的多,不会太多的使用shell,但应继续使用它来熟悉对存储在项目中的数据进行访问的Django语法。

每次修改模型后,都需要重启shell,这样才能看到修改的效果,要退出shell会话,可以按Ctr+D,如果使用的是Windows系统,应按Ctr+Z,再按回车。

Django API编写访问项目中数据的代码时,实际上编写的是查询。浏览Django网站中有关如何查询数据的文档Making queries。

创建页面:学习笔记主页

使用Django创建页面的过程分为三个阶段:定义URL,编写试图和编写模板。按什么顺序编写这三个阶段无关紧要,但在本项目中,总先定义URL模式。URL模式描述了URL是如何设计的,让Django知道如何将浏览器请求与网站URL匹配,以确定返回哪个页面。
每个URL都映射到特定的视图——视图函数获取并处理页面所需的数据。视图函数通常使用模板来渲染页面,而模板定义页面的总体结构,为明白其中的工作原理。创建学习笔记的主页,这包括定义该主页的URL。编写其视图函数并创建一个简单的模板。

映射URL

用户通过在浏览器中输入URL以及单击链接来请求页面,因此需要确定项目需要哪些URL。主页的URL最重要,它是用户来访问项目的基础URL。当前,基础URL(http://127.0.0.1:8000/)返回默认的Django网站。修改这一点,将这个基础URL映射到“学习笔记”的主页
项目主文件夹learning_log中的文件urls.py:

from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
]

前两行导入了一个模块和一个函数,以便对管理网站的URL进行管理。
这个文件的主体定义了遍历urlpatterns。在这个针对整个项目的urls.py文件中。变量urlpatterns包含项目中应用程序URL.
path处代码包含模块admin.site.urls该模块定义了可在管理网站中请求的所有URL.
需要包含learning_logs的URL


from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('learning_logs.urls')),
]

添加一行代码来包含模块learning_logs.urls
默认的urls.py包含在文件夹learning_log中,现在需要在文件夹中learning_logs中再创建一个urls.py文件。

"""定义learning_logs的URL模式"""

from django.urls import path

from . import views

app_name = 'learning_logs'
urlpatterns = [
   # 主页
    path('',views.index,name='index'),
]

为指出当前位于哪个urls.py文件中,在该文件开头添加一个文档字符串。
接下来导入函数path,因为需要使用它将URL映射到视图,还导入了模块views。
变量app_name让Dijango能够将这个urls.py文件同项目内其他应用程序中的同名文件区分开来。在这个模块中,变量urlpatterns是一个列表,包含可在应用程序learning_logs中请求的页面。
实际上URL模式是对函数path()的调用,这个函数接受三个实参。
第一个是一个字符串,帮助Django正确的路由(route)请求,收到请求的URL之后,Django力图将请求路由给一个视图。为此它搜索所有的URL模式,找到与当前请求匹配的哪个,Django忽略项目的基础URL(http://127.0.0.1:8000/),因此空字符串('')与基础URL匹配,其他URL都与这个模式不匹配。如果请求的URL与任何既有的URL模式都不匹配,Django将返回一个错误页面
path()第二个参数制定了要调用views.py中的哪个函数,请求的URL与前述正则表达匹配时,Django将调用views.py中的函数index()。第三个实参将这个URL模式的名称指定为index,让我们能够在代码的其他地方引用它,每当需要提供这个这个主页的链接时,都将使用这个名称,而不编写URL。

编写视图

视图函数将接受请求中的信息,准备好生成页面所需的数据,再将这些数据发送给浏览器————这通常是使用定义页面外观的模板实现的。
learning_logs中的文件views.py是执行python manage.py startapp时自动生成的。

from django.shortcuts import render

# Create your views here.

当前这个文件只导入了函数render(),它根据视图提供的数据渲染响应。

from django.shortcuts import render

# Create your views here.
def index(request):
    """学习笔记的主页。"""
    return render(request,'learning_logs/index.html')

URL请求与刚才定义的模式匹配时,Django将在文件views.py中查找函数index(),再将对像request传递给这个视图函数。
这里不需要处理任何数据,因此这个函数只包含调用rander()的代码,这里向函数rander()提供了两个实参:对象request以及一个可用于创建页面的模板。

编写模板

模板定义页面的外观,而每当页面被请求时,Django将填入相关的数据。模板能够让访问视图提供的任何数据,该主页视图没有提供任何数据,因此相应的模板非常简单。
再文件夹learning_logs中新建一个文件夹,并将其命名为templates,在该文件夹中再新建一个文件夹,命名为learing_logs.
file
这好像有点多余,但是建立了Django能够明确解读的结构,即便项目很大,包含很多应用程序亦如此,在最里面的文件夹learning_logs中,新建一个文件,命名为index.html

<p>Learing Log</p>

<p>Learning Log helps you keep track of your learning....</p>

创建其他页面

制定创建页面的流程,可以扩充“学习笔记项目”。
创建两个显示数据的页面,其中一个列出所有的主题,另一个显示特定主题的所有条目。对于每个页面,都将指定URL模式、编写一个视图函数并编写一个模板。但这样做之前,先创建一个父模板,项目中的其他模板都将继承它。

模板继承

创建网站时,一些通用元素几乎会在所有页面中出现,在这种情况下,编写一个包含通用元素的父模板,并让每个页面都继承这个模板,而不必在每个页面中重复定义这些通用的元素。
这种方法能让开发者专注开发每个页面的独特方面,还能让整体外观容易的多。

1. 父模板。

下面创建一个名为base.html的模板,并将其存储在index.html所在的目录中。这个模板包含所有页面都有的元素,而其他模板都继承他。当前,所有页面都包含的元素只有顶端的标题。因此为每个页面都包含这个模板,所以就iang这个标题设置为到主页的链接:

<p>
    <a href="{% url 'learning_logs:index' %}">Home</a>
</p>

{% block content %}{% endblock content %}

这个文件的第一部分呢创建一个包含项目名的段落,改段落也是到主页的链接。为创建链接,使用了一个模板标签,它是使用花括号和百分号({%%})表示的。模板标签是一小段代码,生成要在页面中显示的信息,这里的模板标签{%url 'learning:index' %}生成一个URL,该URL与在learning_logs/urls.py中定义的名为'index'的URL模式匹配。。
在本例中,learning_logs是一个命名空间,而index是该名称空间中一个独特的URL模式。这个命名空间来自在文件learning_logs/urls.py中赋给app_name的值。
通过使用模板标签来生成URL,能很容易的确保链接是最新的:只需要修改urls.py中的URL模式,Django就会在页面下次被请求时自动插入修改后的URL。从现在开始,每个页面都包含到主页的链接。
在代码的后面,插入了一堆块标签,这个块名为content,是一个占位符,其中包含的信息由子模版指定。
子模板并非必须定义父模板中的每个块,因此在父模板中,可使用任意多个块来预留空间,而子模版可根据需要定义相应数量的块。

2.子模板

重写index.html,使其继承base.html。

{% extends "learning_logs/base.html" %}

{% block content %}
<p>Learning Log helps you keep track of your learning....</p>
{% endblock content %}

file
模块继承的优点开始显现出来了:在子模块中,只需包含当前页面特有的内容。这不仅简化了每个模板,还使得网站修改起来容易得多。要修改很多页面都包含的元素,只需要修改父模板即可。所做的修改将传导到继承该父模板的每个页面。在包含数十个乃至数百个页面的项目中,这种结构使得网站改进起来更容易、更快捷。

在大型项目中,通常有一个用于整个网站的父模板base.html,且网站的每个主要部分都有一个父模板。
每个部分的父模板都继承base.html。而网站的每个页面都继承相应的父模板,这让我们能够轻松地修改整个网站的外观,网站任何一部分的外观以及任何一个页面的外观,这张配置提供了一种效率极高的工作方式。

显示所有主题的页面

有了高效的页面创建方式,就能专注于另外两个页面了:显示所有主题的页面和现实特定主题中条目的页面。前者现实用户创建的所有主题,它是第一个需要使用数的页面。

1.URL模式

定义显式所有主题的页面的URL。
通常使用一个简单的URL片段来指出页面显示的信息。
这里使用单词topics。
因此URL http://localhost:8000/topic/
将返回显示所有主题的页面。
修改learning_logs/urls.py

"""定义learning_logs的URL模式"""

from django.urls import path

from . import views

app_name = 'learning_logs'
urlpatterns = [
    # 主页
    path('',views.index,name='index'),
    # 显示所有的主题
    path('topics/',views.topics,name='topics')
]

这里用于主页URL的字符串参数中添加了topic/。Django检查请求的URL时,这个模式与如下URL匹配:基础URL后跟topics。可在末尾包含斜杠,也可以省略。但单词topics后面不能有任何东西,否则就与该模式不匹配。URL与该模式匹配请求都将交给views.py中的函数topics()处理。

2.视图

函数topics()需要从数据库中获取一些数据,并将其交给模板,需要在views.py中添加代码:

from django.shortcuts import render
from .models import Topic

# Create your views here.
def index(request):
    """学习笔记的主页。"""
    return render(request,'learning_logs/index.html')

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中。
context处定义一个将发送给模板的上下文,。上下文是一个字典,其中的键是将在页面中用来访问数据的名称,而值是要发送给模板的数据。这里只有一个键值对,包含一组在页面中显示的主题,常见使用数据的页面时,除了对象request和模板的路径外,还将变量context传递给rander().

3.模板

显示所有主题的页面的模板接受字典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中,项目列表称为无序列表,用标签<ul></ul>表示。
在后面使用一个相当于for循环的模板标签,它遍历context中的列表topics。模板中使用的代码与Python代码存在一些重要的差别:
Python使用缩进来指出哪些代码是for循环的组成部分,而在模板中,每个for循环都必须使用{%endfor%}标签来显式地指出其结束位置。
{% for item in items %}
    do something with each item
{% endfor %}

在循环中,要将每个主题转换为项目列表中的一项。要在模板中打印变量,需要将变量名用双花括号括起来。

HTML标签<li></li>表示一个项目列表。
在标签对<ul></ul>内部。位于标签<li></li>之间的内容都是一个项目列表项。
在后面有一个模板标签{% empty %}它告诉Django在列表topic为空时该如何办。这里都是打印一条消息。

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

<p>
    <a href="{% url 'learning_logs:index' %}">Home</a>
    <a href="{% url 'learning_logs:topics' %}">Topics</a>
</p>

{% block content %}{% endblock content %}

file

显示特定主题的页面

需要创建一个专注于特定主题的页面。它显示该主题的名称以及所有条目。我们同样将定义一个新的URL模式,编写一个视图并创建一个模板。此外,还将修改显示所有主题的页面,让每个页面列表都变为到相应主题页面的链接。

1.URL模式

显示特定主题的页面的URL模式与前面的所有URL模式都稍有不同。因为它使用主题的id熟悉来指出请求的是哪个主题。例如,如果用户查看Guoan(id =1)的详细页面。
URL将为:http://localhost:8000/topics/1/
下面是与这个URL匹配的模式。应将其放在learning_logs/urls.py中:

"""定义learning_logs的URL模式"""

from django.urls import path

from . import views

app_name = 'learning_logs'
urlpatterns = [
    # 主页
    path('',views.index,name='index'),
    # 显示所有的主题
    path('topics/',views.topics,name='topics'),
    # 指定主题的详细页面
    path('topics/<int:topic_id>/',views.topics,name='topics'),
]
这个URL模式中的字符串'topics/<int:topic_id>/'第一部分让Django查找在基础URL后包含单词topics的URL,第二部分(/<int:topic_id>/)与包含在两个斜杠内的整数匹配,并将这个整数存储在一个名为topic_id的实参中。

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

2. 视图

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

from django.shortcuts import render
from .models import Topic

# Create your views here.
def index(request):
    """学习笔记的主页。"""
    return render(request,'learning_logs/index.html')

def topics(request):
    """显示所有的主题"""
    topics = Topic.objects.order_by('date_added')
    context = {'topics':topics}
    return render(request,'learning_logs/topics.html',context)

def topic(request,topic_id):
    """显示单个主题及其所有的条目"""
    topic = Topic.object.get(id=topic_id)
    entries = topic.entry_set.order_by('-date_add')
    context = {'topic':topic,'entries':entries}
    return render(request,'learning_logs/topic.html',context)
这是除request对象外,第一个还包含另一个形参的视图函数,这个函数接受表达式/<int:topic_id>/捕获的值,并将其存储到topic_id中,在之后使用get()来获取指定的主题,就像前面在DjangoShell中所做的那样。在entries中,获取与该主题相关联的条目,并根据date_added进行排序:减指的是降序排序。即先显示最近的条目。将主题和条目都存储在字典context中,再将这个字典发送给模板topic.html。
topic(request,topic_id):
    """显示单个主题及其所有的条目"""
    topic = Topic.object.get(id=topic_id)
    entries = topic.entry_set.order_by('-date_added')
这两处代码称为查询,因为它们向数据库查询了特定的信息。在自己的项目中编写这样的查询时,先在Django shell中进行尝试。

3.模板

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

{% extends "learning_logs/base.html" %}

{% block content %}

<p>Topic:{{topic}}</p>

<p>Entries:</p>
<ul>
    {% for entry in entries %}
    <p>{{entry.date_add|date:'M d,Y H:i'}}</p>
    <p>{{entry.text|linebreaks}}</p>

{% empty %}
<li>There are no entries for this topic yet.</li>
{% endfor %}
</ul>
{% endblock content %}

显示当前主题。它存储在模板变量{{topic}}中。
为什么可以使用变量topic?
因为它包含在字典context中。
下面,定义一个显示每个条目的项目列表。并像前面显示所有主题一样遍历条目。
每个项目列表都将列出两项信息:条目的时间戳和完整的文本。为列出时间戳,显示熟悉date_added的值。在Django模板中,竖线(|)表示模板过滤器。即对模板变量的值进行修改的函数。过滤器data:'M d,Y H:i'以类似于这样的格式显示时间戳:January 1,2018 23:00,接下来一行显示text的完整值,而不仅仅是前50个字符,过滤器linebreaks将包含换行符的长条目转换为浏览器能够理解的格式,以免显示为不间断的文本块。

4.将显示所有主题的页面中的主题设置为链接

在浏览器中查看显示特定主题的页面前,需要修改模板topics.html,让每个主题都链接到相应的页面,如下所示:

{% extends "learning_logs/base.html" %}

{% block content %}
<p>Topics</p>

<ul>
    {% for topic in topics %}
        <li>
            <a href="{% url 'learning_logs:topic' topic.id %}">{{topic}}</a>
        </li>
    {% empty %}
        <li>No topics have been added yet.</li>
    {% endfor %}
</ul>
{% endblock content %}

file

file

注意:topic.id和topic_id之间存在细微而重要的差别,表达式topic.id检查主题并获取其ID值。而在代码中,变量topic_id是指向该ID的引用,使用ID时如果出现错误,确保正确地使用了这两个表达式。

小结

如何使用Django创建Web应用程序。
指定简要的项目规范,在虚拟环境中安装Django
如何创建应用程序
以及如何定义表示应用程序的模型。
修改模型后,Django可以为迁移数据库提供什么帮助。
创建了可访问的超级用户。


擦肩而过的概率