不耐烦程序员的-Django5-指南-全-
不耐烦程序员的 Django5 指南(全)
原文:
zh.annas-archive.org/md5/120ade4a86489235e5e3f7a026be0fb8译者:飞龙
第一章:前言
Django 是一个高级 Python Web 框架,鼓励快速开发和清晰、实用的设计。Django 用于构建现代 Python Web 应用,它是免费且开源的。
学习 Django 可能是一项棘手且耗时的工作。有成百上千的教程、大量的文档和许多难以消化的解释。然而,这本书能让你在短短几天内学会并使用 Django。
在本书中,你将踏上愉快、动手实践且实用的旅程,学习 Django 全栈开发。你将在几分钟内开始构建你的第一个 Django 应用。你将获得简短的解释和实用的方法,涵盖一些最重要的 Django 特性,例如 Django 的结构、URL、视图、模板、模型、CSS 包含、图像存储、表单、会话、认证和授权,以及 Django 管理面板。你还将学习如何设计 Django 的
在本书结束时,你将能够构建和部署自己的 Django Web 应用。
本书面向对象
本书适用于任何水平的 Python 开发者,他们希望使用 Django 构建全栈 Python Web 应用。本书适用于完全的 Django 初学者。
本书涵盖内容
为了充分利用这本书
下载示例代码文件
《代码实战》
使用的约定
<st c="4849">代码文本</st>
from django.contrib import admin
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
]
from django.shortcuts import render <st c="5439">def index(request):</st>
<st c="5458">return render(request, 'home/index.html')</st>
python3 --version
联系我们
分享您的想法
下载此书的免费 PDF 副本
扫描二维码或访问以下 链接

-
提交您的购买 证明 -
这就完了! 我们将直接将您的免费 PDF 和其他福利发送到您的 邮箱
第二章:1
安装 Python 和 Django,并介绍电影商店应用程序
www.amazon.com/dp/B07TWDNMHJ/)。
-
介绍和 安装 Python -
介绍和 安装 Django -
创建和运行一个 Django 项目 -
理解电影 存储应用程序 -
介绍 Django MVT 架构
技术要求
介绍和安装 Python
-
对于 macOS, 运行以下命令: <st c="3745">python3 --version</st> -
对于 Windows, 运行以下命令: <st c="3786">python --version</st>


介绍和安装 Django
<st c="5527">pip</st> <st c="5682">pip</st>
<st c="5798">pip</st>
-
对于 macOS, 运行以下命令: <st c="5893">pip3</st> -
对于 Windows, 运行以下命令: <st c="5921">pip</st>
<st c="5938">pip</st> <st c="5989">pip</st>

接下来,要安装 Django,运行以下命令:
-
对于 macOS,
For macOS, run this: <st c="6295">pip3 install django==5.0</st> -
对于 Windows,
For Windows, run this: <st c="6343">pip install django==5.0</st>
上述命令
要检查您是否已安装 Django,运行以下命令。
-
对于 macOS,
For macOS, run this: <st c="6800">python3 -m django</st> -
对于 Windows,
For Windows, run this: <st c="6841">python -m django</st>
现在,输出将显示你可以使用的所有 Django 命令,如图

图 1.4 – macOS 上的 Django 模块命令
在本书的过程中,你将逐步被介绍一些先前的命令。
注意
通常也使用
我们已经有了创建 Django 项目所需的所有工具。
创建和运行 Django 项目 Creating and running a Django project
现在我们已经安装了 Django,我们准备好创建我们的 Django 项目。
有几种方法可以创建 Django 项目。<st c="8217">django-admin</st>
在终端中,导航到您想要创建项目的文件夹,并运行以下命令:
django-admin startproject moviesstore
<st c="8605">moviesstore</st>
<st c="8831">cd</st>
cd moviesstore
-
对于 macOS, 运行以下命令: <st c="8943">python3 manage.py runserver</st> -
对于 Windows, 运行以下命令: <st c="8994">python manage.py runserver</st>
<st c="9172">http://127.0.0.1:8000/</st> <st c="9210">http://localhost:8000</st>

理解电影商店应用程序
-
主页将展示一个欢迎信息。
-
关于页面将提供关于电影商店的详细信息。
-
电影页面将展示可用的电影信息,并包括一个按名称搜索电影的过滤器。此外,用户可以点击特定的电影来查看其详细信息并发表评论。
-
购物车页面将展示添加到购物车中的电影以及需要支付的总价。
用户 还可以从购物车中删除电影并继续购买。 -
注册页面将提供一个允许用户注册账户的表单。
-
登录页面将提供一个允许用户登录应用程序的表单。
-
订单页面将显示已登录用户下过的订单。
-
管理员面板将包括管理商店信息的部分,包括创建、更新、删除和列出信息。
电影商店将使用 Django(Python)、SQLite 数据库和 Bootstrap(一个 CSS 框架)开发。关于这些组件的更多细节将在接下来的章节中介绍。
在图 1.6中,你可以找到一个概述应用程序范围和设计的类图。





介绍 Django MVT 架构
-
模型 :模型表示 数据结构。 在 Django 中,模型是定义数据结构和它与数据库交互方式的 Python 类。 模型处理 查询数据库、执行 CRUD ( 创建、读取、更新、删除 )操作以及执行数据验证等任务。 在 电影商店 *应用程序的情况下, 电影 **、 评论 、* 订单 以及我们类图中的其他类将被编码为 Django 模型 。 -
视图 :Django 中的视图负责处理用户请求并返回适当的 响应。 视图通常接收来自客户端的 HTTP 请求,使用模型从数据库中获取数据,并渲染模板以生成 HTML 响应。 在 Django 中,视图是接受 HTTP 请求并返回 HTTP 响应的 Python 函数或类。 在 电影商店 *应用程序的情况下,我们将创建视图和函数来处理电影、账户和购物车等。 。 -
模板 :模板用于动态生成 HTML。 它们包含应用程序的用户界面,并定义了视图中的数据应该如何显示给用户。 在 电影商店 *应用程序的情况下,我们将创建一个允许用户登录的模板,一个列出电影的模板,以及一个显示购物车的模板等。 。
MVT 模式提供了诸如代码分离增强、促进多个团队成员之间的协作、简化错误识别、提高代码重用性和改进可维护性等好处。 *

图 1.11 – 电影商店软件架构图
-
位于左侧的是客户端,它们是我们应用程序的用户,使用移动或桌面设备上的浏览器。
这些客户端通过 超文本传输协议 ( HTTP )与应用程序建立连接,为用户提供与我们的 Web 应用程序交互的手段。 -
在右侧,我们有服务器,它托管我们的 应用程序代码。 -
所有客户端交互首先传递给一个名为 <st c="19468">urls.py</st>的项目级别 URL 文件。 该文件位于名为<st c="19532">moviesstore/</st>的主项目文件夹中。 URLs 将在第二章 中探讨。 此项目文件夹还包含一个<st c="19618">templates/</st>文件夹,我们将在此设计一个可重用的基础模板。 基础模板将在 第三章 中探讨。 -
项目级别的 URL 文件将交互传递给应用级别的 URL 文件。 对于这个项目,我们将设计和实现四个 Django 应用—— 主页 , 电影 , 购物车 ,以及 账户 。Django 应用将在 第二章 中探讨。 -
每个应用级别的 URL 文件将交互传递给一个 <st c="20003">views.py</st>文件。 视图将在 第二章 中探讨。 -
视图如果需要,会与模型通信,并将信息传递给模板,最终将 HTML、CSS 和 JS 代码作为 HTML、CSS 和 JS 代码发送到客户端。 模板将在 第二章 中探讨,而模型将在 第五章 中探讨。
总结
<st c="21054">pip</st>
第三章:理解项目结构和创建我们的第一个应用
Django 项目包含一个预定义的结构,其中包含一些关键文件夹和文件。在本章中,我们将讨论 Django 项目结构以及如何使用这些文件夹和文件来配置我们的 Web 应用。此外,Django 项目由一个或多个应用组成。我们将学习如何创建一个由“主页”和“关于”部分组成的“主页”应用,以及如何在我们的 Django 项目中注册它。
在本章中,我们将涵盖以下主题:
-
理解项目结构
-
创建我们的第一个应用
-
创建一个主页
-
创建一个关于页面
完成所有这些主题后,您将了解如何创建 Django 应用和 Web 页面。
技术要求
在本章中,我们将使用 Python 3.10+。此外,我们将在本书中使用《Visual Studio (VS) Code》编辑器,您可以从code.visualstudio.com/下载。
本章的代码位于github.com/PacktPublishing/Django-5-for-the-Impatient-Second-Edition/tree/main/Chapter02/moviesstore。
本章的 CiA 视频可以在packt.link/rzU25找到。
理解项目结构
让我们看看为我们创建的第一章项目文件,在《创建和运行 Django 项目》部分。图 2.1.1 中展示了这些元素:

图 2.1 – MOVIESSTORE 目录结构
让我们了解这些元素中的每一个。
MOVIESSTORE 文件夹
<st c="1684">moviesstore</st><st c="1701">moviesstore</st> <st c="1813">moviesstore</st>

-
<st c="2074">__pycache__</st>: 这个文件夹 在生成我们的项目时存储编译后的字节码。 您可以基本上忽略这个文件夹。 它的目的是通过缓存编译代码来使您的项目启动稍微快一点,这些代码可以 被轻松执行。 -
<st c="2318">__init__.py</st>: 这个文件指示 Python 将这个目录视为一个 Python 包。 我们可以忽略 这个文件。 -
<st c="2446">asgi.py</st>: Django 作为一个 Web 框架,需要 Web 服务器来运行。 由于大多数 Web 服务器本身不支持 Python,我们需要一个接口来实现这种通信。 Django 目前支持两个接口 – <st c="2766">asgi.py</st>文件包含一个入口点,用于 ASGI 兼容的 Web 服务器异步地为您 项目提供服务。 -
<st c="2872">settings.py</st>: The <st c="2891">settings.py</st>文件是一个重要的文件,它控制着我们的项目设置。 它包含几个属性;让我们分析一些 它们: -
<st c="3026">BASE_DIR</st>: 确定项目 在您的机器上的位置。 -
<st c="3095">SECRET_KEY</st>: 这是一个特定 Django 项目的密钥。 它用于提供加密签名,应设置为唯一、不可预测的值。 在生产环境中,它应该被一个安全 生成的密钥所替换。 -
<st c="3340">DEBUG</st>: 我们网站可以运行在调试模式或非调试模式。 在调试模式下,我们可以获得有关错误的详细信息,这在开发我们的应用程序时非常有用。 例如,如果我们尝试在浏览器中运行 <st c="3528">http://localhost:8000/123</st>,我们将看到一个 页面未找到 (404) 错误(见 图 2.3 ):
-

-
<st c="4142">INSTALLED_APPS</st>: 这个 设置指定了 所有启用在此项目中的 Django 应用程序的列表。 列表中的每个字符串都代表一个 Django 应用的 Python 路径。 默认情况下,Django 包含了几个内置应用程序,例如 admin、auth、contenttypes 和 sessions。 在本章的后面,我们将看到如何创建我们自己的应用程序以及如何将它们包含在这个配置中。 -
<st c="4553">MIDDLEWARE</st>: Django 中的中间件拦截并管理请求和响应处理流程。 列出的中间件由 Django 提供,并处理请求/响应处理的各个方面,包括安全、会话管理、身份验证等。 -
<st c="4822">ROOT_URLCONF</st>: 指定 Django 项目的根 URL 配置的 Python 路径。 -
<st c="4917">TEMPLATES</st>: 定义 Django 模板系统的配置。 它包括有关系统应查找模板源文件和其他特定 模板设置的信息。 -
在 <st c="5174">settings.py</st>》中还有一些其他属性,例如 <st c="5195">DATABASES</st>》、《st c="5206">LANGUAGE_CODE <st c="5219">》和TIME_ZONE `》,但我们专注于前面列表中更重要的属性。 我们将在稍后重新访问此文件,并查看它在开发我们的网站时有多相关。 -
<st c="5391">urls.py</st>: 此文件包含此 Django 项目的 URL 声明。 它可以链接特定的 URL 路径到函数、类或其他 URL 文件以生成响应,或者根据浏览器或 URL 请求渲染页面。 我们将在稍后添加路径到该文件,并更好地理解它是如何工作的。 -
<st c="5699">wsgi.py</st>:此文件包含一个 WSGI 兼容的 Web 服务器用于服务项目的入口点。 默认情况下,当我们使用 python manage.py runserver命令运行服务器时,它使用 WSGI 配置。
manage.py
<st c="5925">manage.py</st>
python manage.py runserver
<st c="6474">python</st> <st c="6481">manage.py startapp</st>
db.sqlite3
<st c="6516">db.sqlite3</st> <st c="6840">db.sqlite3</st>
创建我们的第一个应用

<st c="8157">moviesstore</st> <st c="8203">manage.py</st>
python3 manage.py startapp home
python manage.py startapp home
<st c="8413">home</st>

<st c="8771">主页</st> <st c="8900">settings.py</st><st c="8923">/moviesstore/settings.py</st><st c="8955">INSTALLED_APPS</st>
…
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles', <st c="9306">'home',</st> ]
…
创建主页
-
配置 一个 URL。 -
定义一个视图函数 或类。 -
创建 一个模板。
配置 URL
<st c="10111">/moviesstore/urls.py</st>
…
from django.contrib import admin
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
]
<st c="10396">/moviesstore/urls.py</st> <st c="10450">path</st> <st c="10465">urlpatterns</st> <st c="10509">http://localhost:8000/admin</st> <st c="10578">admin/</st>

<st c="10875">localhost:8000/hello</st><st c="10918">404 not found</st>
创建主页的路径
-
在项目级别的 URL 文件中创建路径( 在 <st c="11148">/moviesstore/urls.py</st>) -
在应用级别的 <st c="11191">urls.py</st>文件中创建路径,该文件定义在应用级别( 在 <st c="11235">/home/urls.py</st>)
<st c="11359">/home/</st><st c="11392">urls.py</st>
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='home.index'),
]
-
我们导入 <st c="11667">path</st>函数,该函数用于在 Django 中定义 URL 模式 。 -
我们导入 <st c="11744">views</st>文件。 在下一节中,我们将在 <st c="11798">views</st>文件中实现一个 <st c="11824">index</st>函数。 该函数将渲染一个包含“ 欢迎”消息的模板。 -
我们定义了 <st c="11923">urlpatterns</st>的 home 应用。 在这种情况下,在 <st c="11978">urlpatterns</st>列表中,我们添加了一个带有 三个参数 的新路径对象:-
第一个参数, <st c="12063">''</st>, 代表 URL 模式本身。 在这种情况下,它是一个空字符串,表示根 URL。 这意味着当访问应用程序的根 URL( <st c="12229">localhost:8000/</st>)时,它将匹配 此路径。 -
第二个参数, <st c="12295">views.index</st>, 指的是将处理 HTTP 请求的视图函数。 在这里, <st c="12377">views.index</st>表示 <st c="12408">index</st>函数位于 <st c="12430">views</st>文件中,负责处理 请求。 -
第三个参数, <st c="12504">name='home.index'</st>, 是 URL 模式的名称。 此名称用于唯一标识 URL 模式,并可以在 Django 项目的其他部分中引用,例如模板或其他 URL 模式。
-
<st c="12744">views.index</st>
定义视图函数
<st c="13094">views.py</st> <st c="13171">/home/views.py</st>
from django.shortcuts import render <st c="13251">def index(request):</st>
<st c="13270">return render(request, 'home/index.html')</st>
-
默认情况下, <st c="13362">views</st>文件导入 <st c="13385">render</st>函数,该函数用于渲染模板并返回包含 <st c="13473">渲染内容</st>的 HTTP 响应。 -
我们定义一个
<index>函数。该函数接受一个参数,<request>,它代表服务器接收到的 HTTP 请求。 -
最后,
<index>函数返回一个渲染的模板。<render>函数将<request>作为第一个参数,第二个参数(<'home/index.html'>)表示要渲染的模板文件的路径。在下一节中,我们将创建该模板。
我们现在已经将''路径与适当的<views.index>函数连接起来,但我们缺少<views.index>函数和<home/index.html>模板之间的连接。所以,让我们实现模板。
创建一个模板
Django 模板是包含 HTML 和Django 模板语言(DTL)语法的文本文件,它描述了网页的结构。Django 模板允许你通过在 HTML 标记中插入变量、循环、条件和其他逻辑来动态生成 HTML 内容。
我们的“主页”应用不包括存储模板的位置,所以让我们创建它。在<home>中,创建一个<templates>文件夹。然后,在<home/templates>中,创建一个<home>文件夹。
现在,在<home/templates/home>中,创建一个新文件,<index.html>。这将是我们“主页”的完整 HTML 页面。目前,用以下内容填充它:
<!DOCTYPE html>
<html>
<head>
<title>Home page</title>
</head>
<body>
<h1>Welcome to the Home Page</h1>
</body>
</html>
该文件包含一个简单的 HTML 代码,带有“欢迎”消息。
注意
<st c="15004">app_name/templates/app_name/my_template.html</st>
<st c="15448">/home/urls.py</st>
将项目级 URL 文件与应用程序级 URL 文件连接
<st c="15642">/moviesstore/urls.py</st>
…
from django.contrib import admin
from django.urls import path<st c="15754">, include</st> urlpatterns = [
path('admin/', admin.site.urls), <st c="15813">path('', include('home.urls')),</st> ]
-
我们修改代码以导入 <st c="15917">include</st>函数,该函数用于包含来自其他 URL 配置文件的 URL。 -
我们将一个新的路径对象添加到 <st c="16033">urlpatterns</st>列表中。 空字符串 <st c="16069">''</st>表示包含来自 <st c="16126">home.urls</st>文件》的 URL 的基本 URL。
<st c="16196">http://localhost:8000</st>

创建关于页面
-
配置 关于 URL。 -
定义 关于函数。 -
创建关于模板。
配置关于页面的 URL
<st c="16938">/home/urls.py</st>
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='home.index'),
<st c="17093">path('about', views.about, name='home.about'),</st> ]
<st c="17166">/about</st> <st c="17199">about</st> <st c="17229">views</st>
定义关于函数
<st c="17268">/home/views.py</st>
from django.shortcuts import render
def index(request):
return render(request, 'home/index.html') <st c="17410">def about(request):</st>
<st c="17476">about</st> function is similar to the <st c="17509">index</st> function. This function renders the <st c="17551">'home/about.html'</st> template, which will be implemented next.
<st c="17610">Creating about template</st>
<st c="17634">Now, in</st> `<st c="17643">/home/templates/home/</st>`<st c="17664">, create a</st> <st c="17675">new file,</st> `<st c="17685">about.html</st>`<st c="17695">. This file contains the HTML for the about page.</st> <st c="17745">For now, fill it in with</st> <st c="17770">the following:</st>
<head>
<title>关于页面</title>
</head>
<body>
欢迎使用关于页面
</body>
<st c="17906">Save the files, and when you navigate to</st> `<st c="17948">localhost:8000/about</st>`<st c="17968">, it will show the about page (</st>*<st c="17999">Figure 2</st>**<st c="18008">.8</st>*<st c="18010">):</st>

<st c="18090">Figure 2.8 – The about page</st>
<st c="18117">Note</st>
<st c="18122">When we executed the command to create the home app, some folders and files were automatically created for us.</st> <st c="18234">For the home app, we won’t use many of them.</st> <st c="18279">So, you can optionally delete the following folders and files to keep your application clean and simple –</st> `<st c="18385">migrations/</st>`<st c="18396">,</st> `<st c="18398">admin.py</st>`<st c="18406">,</st> `<st c="18408">models.py</st>`<st c="18417">,</st> <st c="18419">and</st> `<st c="18423">tests.py</st>`<st c="18431">.</st>
<st c="18432">We quickly created our second page, “about.” Now, we hope you have a better understanding of how URLs, views, and</st> <st c="18547">templates connect.</st>
<st c="18565">Summary</st>
<st c="18573">In this chapter, we discussed the Django project structure.</st> <st c="18634">We analyzed some of the most important project folders, files, and their functionalities.</st> <st c="18724">We saw how a web project can be composed of several applications, and we learned how to create a Django app.</st> <st c="18833">We also learned how URLs, views, and templates connect to create web pages.</st> <st c="18909">We created a couple of pages and loaded them into our local web server.</st> <st c="18981">In the next chapter, we will see how to improve the look and feel of our Django applications by using base templates and a</st> <st c="19104">CSS framework.</st>
第四章:3
设计基本模板
-
使用 Bootstrap 创建 基本模板 -
更新 主页 以使用 基本模板 -
更新 关于 页面以使用 基本模板 -
添加一个 页眉部分 -
添加一个 页脚部分
技术要求
使用 Bootstrap 创建基本模板
<st c="1390">index.html</st> <st c="1406">about.html</st>
介绍 Bootstrap

图 3.1 – Bootstrap 网站
介绍 Django 模板语言(DTL)
我们将构建基础模板,作为 Bootstrap、HTML、CSS、JavaScript 和
DTL 是 Django Web 框架中用于构建动态网页的模板语言(
Django 模板语言的关键特性包括以下内容:
-
<st c="3417">{{</st><st c="3420">变量</st>. -
<st c="3538">{% %}</st>. 模板标签允许循环、条件和其他控制流语句。 例如, <st c="3638">{% if condition %} ...</st>{% endif %} `. -
<st c="3721">{# #}</st>在最终的输出 HTML 中不会被渲染。 -
模板继承 :Django 模板支持继承,允许创建定义页面整体结构和布局的基础模板,子模板继承并覆盖特定的块或部分。
创建基础模板
<st c="4190">moviesstore/</st> <st c="4243">settings.py</st> <st c="4277">templates</st><st c="4333">base.html</st>
<!DOCTYPE html>
<html>
<head>
<title>{{ template_data.title }}</title>
<link href=
"https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/
dist/css/bootstrap.min.css" rel="stylesheet"
crossorigin="anonymous">
<link rel=
"stylesheet" href="https://cdnjs.cloudflare.com/
ajax/libs/font-awesome/6.1.1/css/all.min.css">
<link href=
"https://fonts.googleapis.com/
css2?family=Poppins:wght@300;400;500;600;700&display=
swap" rel="stylesheet">
<script src=
"https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/
js/bootstrap.bundle.min.js"crossorigin="anonymous">
</script>
<meta name="viewport" content="width=device-width,
initial-scale=1" />
</head>
<body>
<!-- Header -->
<!-- Header -->
<div>
{% block content %}
{% endblock content %}
</div>
<!-- Footer -->
<!-- Footer -->
</body>
</html>
-
<st c="5286">head</st>标签包含 <st c="5308">title</st>标签,该标签使用 DTL 双大括号来显示变量的信息( <st c="5396">{{ template_data.title }}</st>)。 稍后,我们将看到如何 从视图传递该变量到这个模板。 它还包含一些链接和脚本,用于包含 Bootstrap 以及我们网站的字体。 我们从以下 网站 https://getbootstrap.com/docs/5.3/getting-started/introduction/#cdn-links 中获取了一些链接。 -
<st c="5714">body</st>标签包含一个 HTML 注释,指示头部(我们将在该位置包含头部)的位置和 <st c="5842">div</st>,其中包含一些 DTL 模板标签。 <st c="5893">{% block %}</st>和 <st c="5909">{% endblock %}</st>是用于模板继承的模板标签。 这是一个定义名为 <st c="6023">content</st>的块的模板标签。块是模板中的占位符,可以被子模板覆盖。 此块内的内容将被扩展此模板的子模板中定义的内容所替换(我们将在后面看到它的实际应用)。 它还包含一个 HTML 注释,指示脚部的位置(我们将在该位置包含脚部)。
注册基本模板
<st c="6453">moviesstore/templates</st> <st c="6533">os</st> <st c="6584">/moviesstore/settings.py</st> <st c="6618">/moviesstore/settings.py</st>
… <st c="6672">import os</st> from pathlib import Path
…
ROOT_URLCONF = 'moviesstore.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django. DjangoTemplates',
'DIRS': [<st c="6831">os.path.join(BASE_DIR,</st>
<st c="6854">'moviesstore/templates')</st>],
'APP_DIRS': True,
…
更新主页以使用基本模板
创建新的索引模板
<st c="7259">/home/templates/home/index.html</st>
{% extends 'base.html' %}
{% block content %}
<header class="masthead bg-index text-white text-center
py-4">
<div class="container d-flex align-items-center flex-
column pt-2">
<h2>Movies Store</h2>
<p>Your Ticket to Unlimited Entertainment!</p>
</div>
</header>
<div class="p-3">
<div class="container">
<div class="row mt-3">
<div class="col mx-auto text-center mb-3">
<h4>Welcome to the best movie store!!</h4>
</div>
</div>
</div>
</div>
{% endblock content %}
-
新的 <st c="7851">index.html</st>文件现在扩展了 <st c="7883">base.html</st>模板。 -
内部的代码 <st c="7927">{% block content %}</st><st c="7946">{% endblock content %}</st>将被注入到 <st c="7994">div</st>的 <st c="8005">base.html</st>模板文件中。 此代码定义了一些消息并使用了一些将在下面定义的自定义 CSS 类。
创建自定义 CSS 文件
<st c="8163">moviesstore/</st> <st c="8216">settings.py</st> <st c="8258">static</st><st c="8291">css</st><st c="8322">moviesstore/static/css/</st> <st c="8367">style.css</st>
.bg-index{
background: url("/static/img/background.jpg") no-repeat
fixed;
background-size: 100% auto;
}
<st c="8566">bg-index</st>
存储图像
<st c="8689">background.jpg</st> <st c="8729">moviesstore/static</st><st c="8775">的文件夹。然后,在</st> <st c="8809">中,从以下链接下载并存储</st>

服务静态文件
<st c="9538">/</st>``<st c="9539">moviesstore/settings.py</st>
…
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' <st c="9623">STATICFILES_DIRS = [</st>
<st c="9643">BASE_DIR / 'moviesstore/static/',</st>
<st c="9677">]</st>
更新基础模板以使用定制 CSS 和加载静态文件
<st c="9902">/moviesstore/templates/base.html</st>
<!DOCTYPE html>
<html> <st c="9986">{% load static %}</st> <head>
<title>{{ template_data.title }}</title>
… <st c="10054"><link rel="stylesheet" type="text/css"</st>
<st c="10092">href="{% static 'css/style.css' %}"></st> <meta name="viewport"
content="width=device-width, initial-scale=1" />
</head>
…
<st c="10236">load static</st> <st c="10301">base.html</st> <st c="10364">static</st> <st c="10481">STATICFILES_DIRS</st>
<st c="10569">http://localhost:8000</st><st c="10716">template_data.title</st>

更新视图索引函数
<st c="11394">/home/views.py</st>
from django.shortcuts import render
def index(request): <st c="11493">template_data = {}</st><st c="11511">template_data['title'] = 'Movies Store'</st> return render(request, 'home/index.html'<st c="11592">, {</st>
<st c="11595">'template_data': template_data})</st> def about(request):
return render(request, 'home/about.html')
-
我们创建了一个名为 <st c="11761">template_data</st>的 Python 字典。 当我们需要从视图函数 传递信息到模板时 ,我们总是使用这个字典。 -
我们在 <st c="11911">template_data</st>字典中添加了一个名为 <st c="11898">title</st>的键。 <st c="11937">title</st>将用于定义浏览器标签页标题。 请记住 <st c="12003">template_data.title</st>在 <st c="12038">base.html</st>模板中 被使用。 -
我们修改了 <st c="12072">render</st>函数以传递第三个参数。 这次我们传递了 <st c="12136">template_data</st>变量,它将在 <st c="12195">home/index.html</st>模板 或它扩展的模板中 可用。

更新关于页面以使用基本模板
创建新的关于模板
<st c="12742">/home/templates/home/about.html</st>
{% extends 'base.html' %}
{% block content %}
{% load static %}
<div class="p-3">
<div class="container">
<div class="row mt-3">
<div class="col-md-6 mx-auto mb-3">
<h2>About</h2>
<hr />
<p>
At Movies Store, we offer a vast digital library
that spans across genres, ensuring there's
something for every movie lover. Browse our
extensive collection of films, including the
latest releases, timeless classics, and hidden
gems. With just a few clicks, you can rent or
purchase your favorite titles and instantly
stream them in high-definition quality. </p>
<p>
Discover the convenience of our digital platform,
where you have the flexibility to watch movies
on your preferred device, whether it's a smart
TV, tablet, or smartphone. With our intuitive
search and recommendation features, finding your
next movie night pick has never been easier. </p>
</div>
<div class="col-md-6 mx-auto mb-3 text-center">
<img src="img/about.jpg' %}"
class="max-width-100"
alt="about" />
</div>
</div>
</div>
</div>
{% endblock content %}
-
新的 <st c="13899">about.html</st>文件现在扩展了 <st c="13931">base.html</st>模板。 -
我们使用 <st c="13958">{% block content %}</st><st c="13977">{% endblock content %}</st>来在 <st c="14039">div</st>的 <st c="14050">base.html</st>模板文件中注入适当的 HTML 代码。 此代码定义了一个关于页面的段落,并显示一个将要 存储的图片。 -
我们同样使用 <st c="14184">{% load static %}</st>标签,因为这个模板通过使用 <st c="14260">static</st>模板标签来加载自定义图片。
存储 about.jpg 图片
<st c="14332">about.jpg</st> <st c="14367">moviesstore/static/img/</st><st c="14424">about.jpg</st>
更新关于函数的视图
<st c="14704">/home/views.py</st>
from django.shortcuts import render
…
def about(request): <st c="14805">template_data = {}</st><st c="14823">template_data['title'] = 'About'</st> return render(request,
'home/about.html'<st c="14897">,</st>
<st c="14948">index</st> function, we define the <st c="14978">template_data</st> dictionary and create the proper <st c="15025">title</st> key with its value. Then, we pass the <st c="15069">template_data</st> variable to the templates.
<st c="15109">Now, save those files, run the server, and go to</st> [<st c="15159">http://localhost:8000/about</st>](http://localhost:8000/about)<st c="15186">; you should see the new</st> **<st c="15212">About</st>** <st c="15217">page displayed (</st>*<st c="15234">Figure 3</st>**<st c="15243">.5</st>*<st c="15245">).</st>

<st c="15937">Figure 3.5 – New About page</st>
<st c="15964">Now that we have updated the home and</st> **<st c="16003">About</st>** <st c="16008">pages, let’s improve the base template by adding a header</st> <st c="16067">section that includes the website</st> <st c="16101">menu options.</st>
<st c="16114">Adding a header section</st>
<st c="16138">To complete the</st> `<st c="16155">base.html</st>` <st c="16164">template, we need to include a header section and a footer section.</st> <st c="16233">Let’s start</st> <st c="16245">with</st> <st c="16250">the header.</st>
<st c="16261">Updating the base template</st>
<st c="16288">In</st> `<st c="16292">/moviesstore/templates/base.html</st>`<st c="16324">, add the</st> <st c="16333">following</st> <st c="16344">in bold:</st>
…
<body><!-- Header --> <st c="16377"><nav class="p-3 navbar navbar-dark bg-dark</st>
…</st>**
**<st c="17047">We included a responsive</st> `<st c="17073">navbar</st>` <st c="17079">between the</st> `<st c="17092">Header</st>` <st c="17098">HTML comments.</st> <st c="17114">This responsive navbar includes a</st> `<st c="17148">logo.png</st>` <st c="17156">file that links to the</st> `<st c="17180">home.index</st>` <st c="17190">URL, and includes an</st> `<st c="17212">About</st>` <st c="17217">text that links to the</st> `<st c="17241">home.about</st>` <st c="17251">URL.</st> <st c="17257">Check that we used the</st> `<st c="17280">url</st>` <st c="17283">template tag, as this tag</st> <st c="17309">links to the specified URL</st> <st c="17337">pattern name.</st>
<st c="17350">Note</st>
<st c="17355">The construction of the previous header section is inspired by the Bootstrap</st> `<st c="17433">navbar</st>` <st c="17439">component.</st> <st c="17451">You can take a look at this component and its available options at this</st> <st c="17523">link:</st> [<st c="17529">https://getbootstrap.com/docs/5.3/components/navbar/</st>](https://getbootstrap.com/docs/5.3/components/navbar/)<st c="17581">.</st>
## <st c="17582">Storing the logo image</st>
<st c="17605">Let’s include the</st> `<st c="17624">logo.png</st>` <st c="17632">image in</st> <st c="17641">our project.</st> <st c="17655">In</st> `<st c="17658">moviesstore/static/img/</st>`<st c="17681">, download and store the</st> `<st c="17706">logo.png</st>` <st c="17714">image from this</st> <st c="17731">link:</st> [<st c="17737">https://github.com/PacktPublishing/Django-5-for-the-Impatient-Second-Edition/blob/main/Chapter03/moviesstore/moviesstore/static/img/logo.png</st>](https://github.com/PacktPublishing/Django-5-for-the-Impatient-Second-Edition/blob/main/Chapter03/moviesstore/moviesstore/static/img/logo.png)<st c="17877">.</st>
## <st c="17878">Updating the style.css</st>
<st c="17901">Finally, let’s include a couple of CSS</st> <st c="17940">classes in our custom CSS file.</st> <st c="17973">In</st> `<st c="17976">/moviesstore/static/css/style.css</st>`<st c="18009">, add the following in bold at the end of</st> <st c="18051">the file:</st>
…
<st c="18161">Now, save those files, run the server, and go to</st> [<st c="18210">http://localhost:8000/</st>](http://localhost:8000/)<st c="18232">; you should see the home page</st> <st c="18264">with the new header section (</st>*<st c="18293">Figure 3</st>**<st c="18302">.6</st>*<st c="18304">).</st>

<st c="18415">Figure 3.6 – The home page with the header section</st>
<st c="18465">This new header section is responsive.</st> <st c="18505">If you reduce the browser window width, you will see a responsive navbar, thanks to the use of different Bootstrap classes (see</st> *<st c="18633">Figure 3</st>**<st c="18641">.7</st>*<st c="18643">).</st>

<st c="18737">Figure 3.7 – Home page with a responsive navbar</st>
<st c="18784">The base template now includes a proper header section.</st> <st c="18841">Let’s finalize this template by adding a</st> <st c="18882">footer section.</st>
# <st c="18897">Adding a footer section</st>
<st c="18921">Let’s complete the website</st> <st c="18949">structure with the inclusion of</st> <st c="18981">a footer.</st>
## <st c="18990">Updating the base template</st>
<st c="19017">In</st> `<st c="19021">/moviesstore/templates/base.html</st>`<st c="19053">, add the</st> <st c="19062">following</st> <st c="19073">in bold:</st>
…
<!-- Footer --> <st c="19099"><section class="p-3 ms-footer d-none d-md-block"></st>
<a class="nav-link"
**
**
…</st>******
****<st c="20379">We included a footer section with</st> <st c="20413">information on the website, some links, and the book’s author names and links to their</st> <st c="20501">X accounts.</st>
## <st c="20512">Updating the style.css</st>
<st c="20535">Finally, let’s include some custom</st> <st c="20571">CSS classes.</st> <st c="20584">In</st> `<st c="20587">/moviesstore/static/css/style.css</st>`<st c="20620">, add the following in bold at the end of</st> <st c="20662">the file:</st>
…
**<st c="20989">Now, save those files, run the server, and go to</st> [<st c="21038">http://localhost:8000/</st>](http://localhost:8000/)<st c="21060">; you should see the home page</st> <st c="21092">with the new footer section (</st>*<st c="21121">Figure 3</st>**<st c="21130">.8</st>*<st c="21132">).</st>

<st c="21315">Figure 3.8 – The home page with the footer section</st>
<st c="21365">You can also click the</st> **<st c="21389">About</st>** <st c="21394">link, and you will see the</st> **<st c="21422">About</st>** <st c="21427">page with the same</st> <st c="21447">website structure.</st>
# <st c="21465">Summary</st>
<st c="21473">In this chapter, we learned how to create base templates that reduce duplicated code.</st> <st c="21560">We improved our application interface with the inclusion of a header and footer, and we learned how to manage static files.</st> <st c="21684">We redesigned the home and</st> **<st c="21711">About</st>** <st c="21716">pages to extend the base template and created proper links to those pages.</st> <st c="21792">In the next chapter, we’ll learn how to start</st> <st c="21838">managing movies.</st>********
第五章:使用虚拟数据创建电影应用
目前,我们的项目包含一个包含几个显示静态信息的部分的应用程序。Web 应用程序更复杂。在本章中,我们将学习如何开发更复杂的应用程序,例如电影应用。电影应用将用于列出电影,并允许用户点击它们在单独的页面上显示其数据。目前,我们将使用虚拟数据来模拟电影数据。
在本章中,我们将介绍以下主题:
-
创建电影应用
-
使用虚拟数据列出电影
-
列出单个电影
-
在基础模板中添加链接
最后,我们将了解如何创建更复杂的多吉安应用以及如何在那些应用中管理信息。
技术要求
在本章中,我们将使用 Python 3.10+。此外,我们将在本书中使用code.visualstudio.com/下载。
本章的代码位于github.com/PacktPublishing/Django-5-for-the-Impatient-Second-Edition/tree/main/Chapter04/moviesstore。
本章的 CiA 视频可以在packt.link/WmJR1找到
创建电影应用
目前,我们有一个包含在
创建电影应用
导航到顶级<st c="1664">moviesstore</st>文件夹(包含<st c="1710">manage.py</st>文件的文件夹)并在终端中运行以下命令:
对于 macOS,运行以下命令:
python3 manage.py startapp movies
对于 Windows,运行以下命令:
python manage.py startapp movies

将电影应用添加到设置中
<st c="2326">settings.py</st> <st c="2347">/moviesstore/settings.py</st><st c="2379">INSTALLED_APPS</st>
…
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'home', <st c="2612">'movies',</st> ]
…
在项目级别的 URL 文件中包含电影 URL 文件
<st c="2689">/moviesstore/urls.py</st>
…
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('home.urls')), <st c="2891">path('movies/', include('movies.urls')),</st> ]
<st c="2965">home.urls</st> <st c="2996">movies.urls</st> <st c="3102">movies.urls</st> <st c="3134">movies/</st> <st c="3203">movies.urls</st>
使用虚拟数据列出电影
<st c="3592">views</st> <st c="3597">index</st> <st c="3642">movies</st>
配置电影 URL
<st c="3688">/movies/</st><st c="3707">urls.py</st>
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='movies.index'),
]
<st c="3966">''</st> <st c="4031">/movies</st> <st c="4086">/movies</st> <st c="4120">views</st> <st c="4150">index</st> <st c="4184">index</st>
定义视图索引函数
在 <st c="4242">/movies/views.py</st>
from django.shortcuts import render <st c="4323">movies = [</st>
<st c="4333">{</st>
<st c="4335">'id': 1, 'name': 'Inception', 'price': 12,</st>
<st c="4378">'description': 'A mind-bending heist thriller.'</st>
<st c="4426">},</st>
<st c="4429">{</st>
<st c="4431">'id': 2, 'name': 'Avatar', 'price': 13,</st>
<st c="4471">'description': 'A journey to a distant world and</st>
<st c="4520">the battle for resources.'</st>
<st c="4547">},</st>
<st c="4550">{</st>
<st c="4552">'id': 3, 'name': 'The Dark Knight', 'price': 14,</st>
<st c="4601">'description': 'Gothams vigilante faces the Joker.'</st>
<st c="4653">},</st>
<st c="4656">{</st>
<st c="4658">'id': 4, 'name': 'Titanic', 'price': 11,</st>
<st c="4699">'description': 'A love story set against the</st>
<st c="4744">backdrop of the sinking Titanic.',</st>
<st c="4779">},</st>
<st c="4782">]</st>
<st c="4784">def index(request):</st>
<st c="4803">template_data = {}</st>
<st c="4822">template_data['title'] = 'Movies'</st>
<st c="4856">template_data['movies'] = movies</st>
<st c="4889">return render(request, 'movies/index.html',</st>
<st c="4933">{'template_data': template_data})</st>
让我们解释一下之前的代码:
-
我们定义了一个名为
<st c="5030">movies</st>的变量。这个变量是一个字典列表,其中每个字典代表一部特定电影的信息。 例如,在索引 <st c="5172">0</st>处,我们有 id=1 的电影(即 <st c="5208">Inception</st>电影)。 我们有四部虚拟电影。我们将在后续章节中从 SQLite 数据库中检索电影数据。 -
我们还有一个
<st c="5342">index</st>函数。 此函数将渲染 <st c="5388">movies/index.html</st>模板,但首先,它将页面标题和电影完整列表传递给该模板。
创建电影索引模板
在 <st c="5532">/movies/</st><st c="5555">templates</st> <st c="5582">/movies/templates/</st><st c="5611">movies</st>
现在,在 <st c="5634">/movies/templates/movies/</st><st c="5680">index.html</st>
{% extends 'base.html' %}
{% block content %}
{% load static %}
<div class="p-3">
<div class="container">
<div class="row mt-3">
<div class="col mx-auto mb-3">
<h2>List of Movies</h2>
<hr />
</div>
</div>
<div class="row">
{% for movie in template_data.movies %}
<div class="col-md-4 col-lg-3 mb-2">
<div class="p-2 card align-items-center pt-4">
<img src="img/about.jpg' %}"
class="card-img-top rounded">
<div class="card-body text-center">
{{ movie.name }}
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endblock content %}
让我们解释一下之前的代码:
-
我们扩展了
<st c="6327">base.html</st>模板。 -
我们定义了一个带有文本
<st c="6389">List</st><st c="6394">of Movies</st>的标题元素。 -
我们使用 DTL
<st c="6420">for</st>模板 标签遍历每部电影,并显示电影名称。 目前,我们为所有电影显示默认图片;我们将在后续章节中上传并显示每部电影的正确图片。
注意
我们以 Bootstrap 卡片组件为基础来设计电影显示的方式。

列出单个电影
<st c="7314">views</st> <st c="7319">show</st> <st c="7359">show</st>
配置单个电影 URL
<st c="7471">/movies/urls.py</st>
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='movies.index'), <st c="7624">path('<int:id>/', views.show, name='movies.show'),</st> ]
<st c="7747"><int:id></st> <st c="7905">id</st><st c="7992">movies/1</st><st c="8058">id=1</st><st c="8129">views</st> <st c="8100">show</st>
定义视图 show 函数
<st c="8277">/movies/views.py</st>
… <st c="8346">def show(request, id):</st>
<st c="8368">movie = movies[id - 1]</st>
<st c="8391">template_data = {}</st>
<st c="8410">template_data['title'] = movie['name']</st>
<st c="8449">template_data['movie'] = movie</st>
<st c="8480">return render(request, 'movies/show.html',</st>
<st c="8523">{'template_data': template_data})</st>
-
我们定义了 <st c="8605">show</st>函数。 此函数接受两个参数: <st c="8656">request</st>和 <st c="8668">id</st>( <st c="8672">id</st>是从 URL 中收集的。 -
然后,我们使用该 ID 提取电影数据。 我们减去一个单位,因为我们用 <st c="8801">id=1</st>存储电影,在电影列表索引 <st c="8831">0</st>中,电影 <st c="8849">id=2</st>在电影列表索引 <st c="8879">1</st>中,以此类推。 -
最后,我们将电影名称和单个电影传递给 movies/show.html模板。
创建电影展示模板
<st c="9019">/movies/templates/movies/</st><st c="9065">show.html</st>
{% extends 'base.html' %}
{% block content %}
{% load static %}
<div class="p-3">
<div class="container">
<div class="row mt-3">
<div class="col-md-6 mx-auto mb-3">
<h2>{{ template_data.movie.name }}</h2>
<hr />
<p><b>Description:</b> {{
template_data.movie.description }}</p>
<p><b>Price:</b> ${{
template_data.movie.price }}</p>
</div>
<div class="col-md-6 mx-auto mb-3 text-center">
<img src="img/about.jpg' %}"
class="rounded" />
</div>
</div>
</div>
</div>
{% endblock content %}
在电影页面上添加单个电影链接
<st c="9725">/movies/templates/movies/index.html</st>
…
{% for movie in template_data.movies %}
<div class="col-md-4 col-lg-3 mb-2">
<div class="p-2 card align-items-center pt-4">
<img src="img/about.jpg' %}"
class="card-img-top rounded">
<div class="card-body text-center"> <st c="10020"><a href="{% url 'movies.show' id=movie.id %}"</st>
<st c="10065">class="btn bg-dark text-white"></st> {{ movie.name }} <st c="10115"></a></st> </div>
</div>
</div>
{% endfor %}
…
<st c="10236">url</st> <st c="10296">movie.show</st><st c="10370">id=movie.id</st><st c="10417">id</st> <st c="10437">id</st> <st c="10457">movie</st>
<st c="10610">http://localhost:8000/movies</st>

在基本模板中添加链接
<st c="11237">/moviesstore/templates/base.html</st>
…
<div class="collapse navbar-collapse"
id="navbarNavAltMarkup">
<div class="navbar-nav ms-auto navbar-ml">
<a class="nav-link" href=
"{% url 'home.about' %}">About</a> <st c="11489"><a class="nav-link" href=</st>
<st c="11514">"{% url 'movies.index' %}">Movies</a></st> </div>
</div>
…
<st c="11617">http://localhost:8000/movies</st>

摘要
<st c="12098">for</st>
第六章:5
与模型一起工作
-
创建我们的 第一个模型 -
安装 Pillow -
管理迁移 -
访问 Django 管理界面 -
配置 图像上传 -
服务 存储的图像 -
将电影模型 添加到管理界面
技术要求
创建我们的第一个模型
-
每个模型都是一个类,它 扩展了 <st c="1745">django.db.models.Model</st> -
每个模型属性代表一个 数据库列 -
有了这些,Django 为我们提供了一组有用的方法来 创建、更新、读取和删除 ( CRUD **)数据库中的模型信息
创建一个 Movie 模型
<st c="2106">movies</st> <st c="2140">Movie</st> <st c="2162">/movies</st>,我们有</st> <st c="2192">文件,我们在其中为</st>
from django.db import models
class Movie(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
price = models.IntegerField()
description = models.TextField()
image = models.ImageField(upload_to='movie_images/')
def __str__(self):
return str(self.id) + ' - ' + self.name
-
首先,我们导入 的 <st c="2665">models</st>模块,它提供了定义数据库模型所需的各类类和实用工具。 -
接下来,我们定义一个名为 <st c="2792">Movie</st>的 Python 类,它继承自 <st c="2819">models.Model</st>。这意味着 <st c="2849">Movie</st>是一个 Django 模型类。 -
在 <st c="2891">Movie</st>类内部,我们定义了 几个字段: -
<st c="2929">id</st>: 这是一个 <st c="2946">AutoField</st>值,它会自动为数据库中添加的每个新记录增加其值。 <st c="3056">primary_key=True</st>`参数指定该字段是表的唯一主键,唯一标识 每条记录。 -
<st c="3176">name</st>: 这是一个 <st c="3194">CharField</st>值,表示一个最大长度为 255 个字符的字符串字段。 它存储电影的名称。 -
<st c="3314">price</st>: 这是一个 <st c="3334">IntegerField</st>值,用于存储整数值。 它代表电影的票价。 -
<st c="3418">description</st>: 这是一个 <st c="3443">TextField</st>值,表示一个没有指定最大长度的文本字段。 它存储电影的文本描述。 -
<st c="3567">图像</st>:这是一个 <st c="3587">ImageField</st>值,用于存储图像文件。 <st c="3633">upload_to</st>参数指定了上传的图像将被存储的目录。 在这种情况下,上传的图像将被存储在 Django 项目媒体目录下的 <st c="3767">movie_images/</st>目录中。 媒体目录用于存储用户上传的文件,如图像、文档或其他媒体文件。 此目录在您的 Django 项目设置中指定(我们将在本章后面配置它)。
-
-
<st c="4055">__str__</st>:这是 Python 类中的一个特殊方法,它返回对象的字符串表示形式。 它将电影的 <st c="4188">id</st>值(转换为字符串)与一个连字符和电影名称连接起来。 此方法将在我们稍后在 Django 管理面板中显示电影时非常有用。
<st c="4536">模型</st>
安装 Pillow
-
对于 macOS,运行以下命令: <st c="4918">pip3 install pillow</st> -
对于 Windows,运行以下命令: <st c="4978">pip install pillow</st>
管理迁移
应用默认迁移
You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions. Run 'python manage.py migrate' to apply them.
<st c="6266">moviesstore</st> <st c="6303">manage.py</st>
-
对于 macOS,运行以下命令: <st c="6357">python3 manage.py migrate</st> -
对于 Windows,运行以下命令: <st c="6423">python manage.py migrate</st>
<st c="6453">migrate</st> <st c="6554">db.sqlite3</st> <st c="6675">migrate</st> <st c="6686">runserver</st>
<st c="6723">migrate</st> <st c="6856">admin</st><st c="6863">auth</st><st c="6869">contenttypes</st><st c="6887">sessions</st><st c="6926">INSTALLED_APPS</st> <st c="6957">moviesstore/settings.py</st>
<st c="6995">migrate</st> <st c="7068">INSTALLED_APPS</st> <st c="7098">movies</st> <st c="7154">movies</st> <st c="7226">movies</st>

创建自定义迁移
<st c="7949">movies</st> <st c="7952">电影</st> <st c="8075">movies</st> <st c="8106">makemigrations</st> <st c="8132">终端</st> 中运行:
-
对于 macOS,运行以下命令: <st c="8183">python3 manage.py makemigrations</st> -
对于 Windows,运行以下命令: <st c="8256">python manage.py makemigrations</st>

<st c="8569">迁移</st> <st c="8622">电影</st> <st c="8645">movies</st> <st c="8711">电影</st> <st c="8734">movies/migrations/</st>

<st c="8953">电影</st> <st c="9010">makemigrations</st>
应用自定义迁移
<st c="9271">makemigrations</st>
-
对于 macOS,运行以下命令: <st c="9480">python3 manage.py migrate</st> -
对于 Windows,运行以下命令: <st c="9546">python manage.py migrate</st>
<st c="9611">movies</st>

-
对于 macOS,运行以下命令: 以下命令: <st c="9949">python3 manage.py makemigrations</st> <st c="9982">python3 manage.py migrate</st> -
对于 Windows,运行以下命令: 以下命令: <st c="10048">python manage.py makemigrations</st> <st c="10080">python manage.py migrate</st>
访问 Django 管理界面
<st c="10409">admin</st> <st c="10423">/moviesstore/urls.py</st>
…
urlpatterns = [ <st c="10461">path('admin/', admin.site.urls),</st> path('', include('home.urls')),
path('movies/', include('movies.urls')),
]
<st c="10582">localhost:8000/admin</st>

创建超级用户
-
对于 macOS,运行以下命令: 以下命令: <st c="11241">python3 manage.py createsuperuser</st> -
对于 Windows,运行以下命令: 以下命令: <st c="11315">python manage.py createsuperuser</st>
Superuser created successfully.
恢复超级用户密码
-
以下是 macOS 的命令 以下命令: <st c="11784">python3 manage.py changepassword <username></st> -
这是 Windows 的命令 以下命令: <st c="11860">python manage.py changepassword <username></st>
访问管理面板


<st c="12787">电影</st> <st c="12900">电影</st>
配置图片上传
<st c="13090">/moviesstore/settings.py</st>
… <st c="13167">MEDIA_ROOT = os.path.join(BASE_DIR, 'media')</st>
<st c="13211">MEDIA_URL = '/media/'</st>
-
<st c="13266">MEDIA_ROOT</st>: 这个变量指定了上传媒体文件将存储的文件系统路径。 在此处, <st c="13390">BASE_DIR</st>是一个变量,代表 Django 项目的基目录,而 <st c="13475">'media'</st>是位于 <st c="13510">BASE_DIR</st>中的子目录,其中将存储媒体文件。 因此, <st c="13557">MEDIA_ROOT</st>将被设置为类似 <st c="13595">/your_project_folder/media</st>的路径。 -
<st c="13622">MEDIA_URL</st>: 这个变量指定了将用于从网络服务器提供媒体文件的 URL 前缀。 在此代码中,它被设置为 <st c="13760">'/media/'</st>,这意味着上传到 Django 应用程序的媒体文件将通过以 <st c="13873">/media/</st>开头的 URL 进行访问。 例如,如果你上传一个名为<st c="13924">example.jpg</st>的图片,它可能可以通过以下 URL 访问 http://localhost:8000/media/example.jpg 。
服务存储的图像
<st c="14221">/moviesstore/urls.py</st>
… <st c="14279">from django.conf.urls.static import static</st>
<st c="14321">from django.conf import settings</st> urlpatterns = [
path('admin/', admin.site.urls),
path('', include('home.urls')),
path('movies/', include('movies.urls')),
] <st c="14479">urlpatterns += static(settings.MEDIA_URL,</st>
<st c="14611">MEDIA_ROOT</st> directory when the <st c="14641">MEDIA_URL</st> URL prefix is accessed.
<st c="14674">Note</st>
<st c="14679">It’s important to stop the server and run the server again to apply the</st> <st c="14752">previous changes.</st>
<st c="14769">Now that the image configuration is done, let’s add movies to the</st> <st c="14836">admin panel.</st>
<st c="14848">Adding a movie model to the admin panel</st>
<st c="14888">We are now ready to create movies from the admin panel and store the images in our Django project.</st> <st c="14988">We will add the</st> `<st c="15004">Movie</st>` <st c="15009">model to the admin panel, and we will</st> <st c="15048">create movies.</st>
<st c="15062">Adding the Movie model to the admin panel</st>
<st c="15104">To add the</st> `<st c="15116">Movie</st>` <st c="15121">model to the admin panel, go back to</st> `<st c="15159">/movies/admin.py</st>` <st c="15175">and</st> <st c="15180">register our model</st> <st c="15198">by adding the following</st> <st c="15223">in</st> **<st c="15226">bold</st>**<st c="15230">:</st>
from django.contrib import admin

<st c="15629">图 5.8 – 可用电影的管理页面</st>
<st c="15674">通过点击</st> **<st c="15713">+添加</st>**<st c="15717">来尝试添加一个</st> `<st c="15688">电影</st>` <st c="15693">对象。您将被带到</st> **<st c="15746">添加电影</st>** <st c="15755">表单,如图</st> *<st c="15774">图 5</st>**<st c="15782">.9</st>*<st c="15784">所示:</st>

<st c="16083">图 5.9 – 添加电影表单</st>
<st c="16114">尝试添加一个电影并点击</st> **<st c="16142">保存</st>**<st c="16146">。您的电影对象将被保存到数据库中,并在管理页面中显示,如图</st> *<st c="16241">图 5</st>**<st c="16249">.10</st>*<st c="16252">所示:</st>

<st c="16399">图 5.10 – 电影管理页面</st>
<st c="16430">请注意,管理面板将电影信息显示为电影 ID 与短横线结合以及电影名称的组合。</st> <st c="16558">这是因为我们定义了</st> `<st c="16588">Movie</st>` <st c="16593">模型</st> 的 `<st c="16602">__str__</st>` <st c="16609">方法以这种方式工作。</st>
<st c="16635">您还可以在</st> `<st c="16672">/moviesstore/media/movie_images/<image file>.jpg</st>`<st c="16720">中看到电影</st> <st c="16722">图像。</st> *<st c="16722">图 5</st>**<st c="16730">.11</st> <st c="16733">显示了存储在</st> <st c="16756">inception.jpg</st> <st c="16769">之前的文件夹中的图像:</st>

<st c="16917">图 5.11 – 存储的电影图像位置</st>
<st c="16962">每次您上传电影图像时,它将被存储在之前的文件夹中。</st> <st c="17040">通过这样,我们已经配置了我们的项目,使其能够存储和</st> <st c="17067">服务图像。</st>
<st c="17118">总结</st>
<st c="17126">在 Django 中处理数据库时,模型是必不可少的。</st> <st c="17186">在本章中,我们学习了 Django 模型的基础知识,并创建了一个</st> `<st c="17268">电影</st>` <st c="17273">模型。</st> <st c="17281">我们还学习了如何使用 Django 管理界面以及如何创建电影。</st> <st c="17361">在下一章中,我们将学习如何从我们的数据库中提取并显示存储在我们的网站上的电影。</st>
第七章:6
从数据库收集和展示数据
-
删除电影的 虚拟数据 -
更新电影 列表页面 -
更新单个 电影页面的列表 -
实现搜索 电影功能
技术要求
code.visualstudio.com/
github.com/PacktPublishing/Django-5-for-the-Impatient-Second-Edition/tree/main/Chapter06/moviesstore
packt.link/mZUvA
删除电影的虚拟数据
<st c="1567">/movies/views.py</st><st c="1596">movies</st>
from django.shortcuts import render <st c="1685">movies = [</st>
<st c="1695">{</st>
<st c="1697">'id': 1, 'name': 'Inception', 'price': 12,</st>
<st c="1740">'description': 'A mind-bending heist thriller.'</st>
<st c="1788">},</st>
<st c="1791">{</st>
<st c="1793">'id': 2, 'name': 'Avatar', 'price': 13,</st>
<st c="1833">'description': 'A journey to a distant world and the battle for resources.'</st>
**<st c="1909">},</st>**
**<st c="1912">{</st>**
**<st c="1914">'id': 3, 'name': 'The Dark Knight', 'price': 14,</st>**
**<st c="1963">'description': 'Gothams vigilante faces the Joker.'</st>**
**<st c="2015">},</st>**
**<st c="2018">{</st>**
**<st c="2020">'id': 4, 'name': 'Titanic', 'price': 11,</st>**
**<st c="2061">'description': 'A love story set against the backdrop of the sinking Titanic.',</st>**
**<st c="2141">},</st>**
**<st c="2144">]</st>** <st c="2146">…</st>
**
更新电影列表页面
<st c="2538">index</st> <st c="2573">movies.index</st>
更新索引函数
<st c="2658">/movies/views.py</st>
from django.shortcuts import render <st c="2739">from .models import Movie</st> def index(request):
template_data = {}
template_data['title'] = 'Movies'
template_data['movies'] = <st c="2864">Movie.objects.all()</st> return render(request, 'movies/index.html',
{'template_data': template_data})
…
-
我们从 <st c="3011">Movie</st>模型中导入 <st c="3032">models</st>文件。 我们将使用此模型来访问 数据库信息。 -
我们通过使用 <st c="3153">Movie.objects.all()</st>方法从数据库中收集所有电影。 <st c="3181">Movie.objects</st>是 Django 中的一个管理器,作为查询与模型关联的数据库表的默认接口。 它提供了执行数据库操作(如创建、更新、删除和检索对象)的各种方法。 <st c="3435">all()</st>方法从模型表示的数据库表获取所有对象。 记住,我们之前通过使用 <st c="3591">movies</st>变量收集了电影信息;现在,我们使用 <st c="3624">Movie</st>Django 模型。
更新 movies.index 模板
<st c="3863">/movies/templates/movies/index.html</st>
…
{% for movie in template_data.movies %}
<div class="col-md-4 col-lg-3 mb-2">
<div class="p-2 card align-items-center pt-4">
<img src="img/st>**<st c="4062">{{ movie.image.url }}</st>**<st c="4084">"
class="card-img-top rounded</st> **<st c="4115">img-card-200</st>**<st c="4127">">
<div class="card-body text-center">
<a href="{% url 'movies.show' id=movie.id %}"
class="btn bg-dark text-white">
{{ movie.name }}
</a>
</div>
</div>
</div>
{% endfor %}
…</st>
添加自定义 CSS 类
<st c="4550">/moviesstore/static/css/style.css</st>
… <st c="4636">.img-card-200 {</st>
<st c="4651">width: fit-content;</st>
<st c="4671">max-height: 200px;</st>
<st c="4690">}</st>

更新单个电影页面的列表
<st c="5333">movies.show</st>
更新显示函数
在 <st c="5416">/movies/views.py</st>
…
def show(request, id):
movie = <st c="5493">Movie.objects.get(id=id)</st> template_data = {}
template_data['title'] = <st c="5562">movie.name</st> template_data['movie'] = movie
return render(request, 'movies/show.html',
{'template_data': template_data})
-
我们使用 <st c="5725">Movie.objects.get(id=id)</st>方法根据其 <st c="5799">id</st>检索一个特定的电影。记住 <st c="5817">id</st>是通过 URL 传递的,并在 <st c="5876">show</st>函数中作为参数接收的。 -
我们现在将 <st c="5905">movie.name</st>作为对象属性访问。 之前,我们通过键( <st c="5983">movie['name']</st>)访问名称,因为虚拟数据变量 存储字典。
更新 movies.show 模板
在 <st c="6085">/movies/templates/movies/show.html</st>
…
<div class="col-md-6 mx-auto mb-3">
<h2>{{ template_data.movie.name }}</h2>
<hr />
<p><b>Description:</b> {{
template_data.movie.description }}</p>
<p><b>Price:</b> ${{
template_data.movie.price }}</p>
</div>
<div class="col-md-6 mx-auto mb-3 text-center">
<img src="img/st>**<st c="6420">{{ template_data.movie.image.url }}</st>**<st c="6456">"
class="rounded</st> **<st c="6474">img-card-400</st>**<st c="6486">" />
</div>
…</st>
添加自定义 CSS 类
<st c="6674">/moviesstore/static/css/style.css</st>
… <st c="6760">.img-card-400 {</st>
<st c="6775">width: fit-content;</st>
<st c="6795">max-height: 400px;</st>
<st c="6814">}</st>
现在保存这些文件,运行服务器,并访问特定电影在

实现搜索电影功能
<st c="7527">movies.index</st> <st c="7573">index</st>
更新 movies.index 模板
<st c="7627">/movies/templates/movies/index.html</st>
…
<div class="col mx-auto mb-3">
<h2>List of Movies</h2>
<hr /> <st c="7754"><p class="card-text"></st>
<st c="7775"><form method="GET"></st>
**<st c="7795"><div class="row"></st>**
**<st c="7813"><div class="col-auto"></st>**
**<st c="7836"><div class="input-group col-auto"></st>**
**<st c="7871"><div class="input-group-text"></st>**
**<st c="7902">Search</div></st>**
**<st c="7915"><input type="text" class="form-control"</st>**
**<st c="7955">name="search"></st>**
**<st c="7970"></div></st>**
**<st c="7977"></div></st>**
**<st c="7984"><div class="col-auto"></st>**
****<st c="8007"><button class="btn bg-dark text-white"</st>**
**<st c="8046">type="submit">Search</button></st>**
**<st c="8076"></div></st>**
**<st c="8083"></div></st>**
**<st c="8090"></form></st>**
**<st c="8098"></p></st>** <st c="8103"></div>
</div>
…</st>**
****<st c="8257">search</st> <st c="8318">Avatar</st><st c="8347">http://localhost:8000/movies/?search=Avatar</st>
更新 index 函数
<st c="8419">/movies/views.py</st>
…
def index(request): <st c="8485">search_term = request.GET.get('search')</st>
<st c="8524">if search_term:</st>
<st c="8540">movies =</st>
<st c="8549">Movie.objects.filter(name__icontains=search_term)</st>
<st c="8599">else:</st>
<st c="8605">movies = Movie.objects.all()</st> template_data = {}
template_data['title'] = 'Movies'
template_data['movies'] = <st c="8714">movies</st> return render(request, 'movies/index.html',
{'template_data': template_data})
<st c="8803">index</st> <st c="8871">search</st> <st c="8973">search</st>
-
我们通过使用 <st c="9053">search</st>参数的 <st c="9083">request.GET.get('search')</st>方法来检索该值,并将其分配给 <st c="9145">search_term</st>变量。 在这里,我们捕获了 <st c="9188">search</st>输入值,该值是通过上一节中定义的表单提交的。 -
如果 <st c="9270">search_term</st>不为空,我们将过滤包含 <st c="9337">search_term</st>名称的电影。我们使用 <st c="9354">__icontains</st>查找进行不区分大小写的 包含搜索。 -
如果 <st c="9427">search_term</st>为空,我们将从数据库中检索所有电影,而不应用 `任何过滤器。 -
最后,我们将提取的 <st c="9551">movies</st>传递给 <st c="9565">template_data</st>字典。
<st c="9636">http://localhost:8000/movies</st>

我们已经重构了我们的
摘要
<st c="10409">all</st><st c="10414">get</st><st c="10423">filter</st>
第八章:7
理解数据库
-
理解数据库查看器 -
自定义 Django 管理面板 -
切换到 MySQL 数据库
技术要求
code.visualstudio.com/
github.com/PacktPublishing/Django-5-for-the-Impatient-Second-Edition/tree/main/Chapter07/moviesstore
packt.link/wD2bK
理解数据库查看器
<st c="1242">db.sqlite3</st> <st c="1378">SQLite 查看器</st>
<st c="1487">db.sqlite3</st>

<st c="2123">电影</st><st c="2278">movies</st> <st c="2309">Movie</st><st c="2354">movies_movie</st>
<st c="2528">django_session</st>
<st c="2674">movies_movie</st>

<st c="3999">/moviereviews/settings.py</st>
…
DATABASES = {
'default': { <st c="4093">'ENGINE': 'django.db.backends.sqlite3',</st>
<st c="4132">'NAME': BASE_DIR / 'db.sqlite3',</st> }
}
…
自定义 Django 管理面板

按名称排序电影
<st c="5623">/movies/admin.py</st>
from django.contrib import admin
from .models import Movie <st c="5727">class MovieAdmin(admin.ModelAdmin):</st>
<st c="5762">ordering = ['name']</st> admin.site.register(Movie<st c="5808">, MovieAdmin</st>)
-
我们创建了一个 <st c="5868">MovieAdmin</st>类,它继承自 <st c="5904">admin.ModelAdmin</st>。这定义了一个自定义管理类,允许您自定义电影 <st c="6029">模型</st>的管理界面。 -
我们设置了一个 <st c="6052">排序</st>属性。 此属性设置管理界面中电影对象的默认排序。 在我们的例子中,它指定电影应按其 <st c="6227">名称</st>字段排序。 -
最后,我们将 <st c="6266">Movie</st>模型与自定义管理类 <st c="6307">MovieAdmin</st>注册。 这告诉 Django 使用 <st c="6348">MovieAdmin</st>类来自定义电影 <st c="6406">模型</st>的管理界面。
<st c="6451">/admin</st>

允许按名称搜索
<st c="6903">/movies/admin.py</st>
from django.contrib import admin
from .models import Movie
class MovieAdmin(admin.ModelAdmin):
ordering = ['name'] <st c="7063">search_fields = ['name']</st> admin.site.register(Movie, MovieAdmin)
<st c="7138">search_fields</st> <st c="7191">name</st> <st c="7209">Movie</st>
<st c="7505">/admin</st>

切换到 MySQL 数据库
-
配置 MySQL 数据库。 -
配置我们的项目以使用 MySQL 数据库。 -
运行 迁移。
配置 MySQL 数据库

<st c="10018">root</st>

<st c="10393">moviesstore</st>

配置我们的项目以使用 MySQL 数据库
-
对于 macOS,运行以下命令: <st c="10898">pip3 install pymysql</st> -
对于 Windows 系统, 请运行以下命令: <st c="10942">pip install pymysql</st>
<st c="11016">moviesstore/__init__.py</st>
<st c="11045">import pymysql</st>
<st c="11095">__init__.py</st> file will be executed when we run the Django project, and the previous two lines import the PyMySQL package into the project.
<st c="11232">Finally, we need to modify the database settings to switch to MySQL.</st> <st c="11302">In</st> `<st c="11305">/moviesstore/settings.py</st>`<st c="11329">, modify the</st> `<st c="11342">DATABASES</st>` <st c="11351">variable to the following</st> <st c="11378">in bold:</st>
…
DATABASES = {
'default': { <st c="11415">'ENGINE': 'django.db.backends.mysql',</st>
}
…
<st c="11550">Running the migrations</st>
<st c="11573">Since we have switched</st> <st c="11596">the database, the new database is empty.</st> <st c="11638">So, we need to run</st> <st c="11657">the migrations:</st>
* <st c="11672">For macOS,</st> <st c="11684">run this:</st>
```
<st c="11693">python3 manage.py migrate</st>
```py
* <st c="11719">For Windows,</st> <st c="11733">run this:</st>
```
<st c="11742">python manage.py migrate</st>
```py
<st c="11767">Then, we should</st> <st c="11784">see the tables in our</st> *<st c="11806">phpMyAdmin</st>* <st c="11816">application (as shown in</st> *<st c="11842">Figure 7</st>**<st c="11850">.9</st>*<st c="11852">).</st>

<st c="12825">Figure 7.9 – The MySQL database</st>
<st c="12856">Finally, we</st> <st c="12869">repeat the process of creating a superuser and accessing the admin panel to create</st> <st c="12952">some movies.</st>
<st c="12964">Summary</st>
<st c="12972">We hope that you now better understand how SQLite databases work, how Django supports database management, and how you can customize the Django admin panel.</st> <st c="13130">In the next chapter, we will learn how to allow a user to sign up and</st> <st c="13200">log in.</st>
第九章:8
实现用户注册和登录
-
创建一个 账户应用 -
创建一个基本的 注册页面 -
改进注册页面以处理 <st c="528">POST</st>操作 -
自定义 <st c="553">UserCreationForm</st> -
创建一个 登录页面 -
实现登出功能
技术要求
创建一个账户应用
<st c="1466">accounts</st>
-
创建一个 账户应用。 -
将账户应用添加到 设置文件中。 -
在项目级别的 URL 文件中包含一个账户 URL 文件。
创建一个账户应用
<st c="1785">moviesstore</st> <st c="1831">manage.py</st>
-
对于 macOS,运行以下命令: <st c="1923">python3 manage.py startapp accounts</st> -
对于 Windows,运行以下命令: <st c="1999">python manage.py startapp accounts</st>

将 accounts 应用添加到设置文件中
<st c="2528">settings.py</st>
<st c="2549">/moviesstore/settings.py</st><st c="2581">INSTALLED_APPS</st>
…
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'home',
'movies', <st c="2830">'accounts',</st> ]
…
将 accounts URL 文件包含到项目级别的 URL 文件中
<st c="2968">/moviesstore/urls.py</st>
…
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('home.urls')),
path('movies/', include('movies.urls')), <st c="3153">path('accounts/', include('accounts.urls')),</st> ]
…
<st c="3230">accounts.urls</st> <st c="3265">accounts/</st> <st c="3336">accounts.urls</st>
创建基本的注册页面
-
配置一个 注册 URL。 -
定义一个 <st c="3807">注册</st>函数。 -
创建一个 accounts 注册模板。 -
将注册链接添加到 基本模板中。
配置注册 URL
<st c="3970">/accounts/</st><st c="4007">urls.py</st>
from django.urls import path
from . import views
urlpatterns = [
path('signup', views.signup, name='accounts.signup'),
]
<st c="4269">/signup</st> <st c="4364">urls.py</st> <st c="4338">/accounts</st> <st c="4403">/accounts/signup</st> <st c="4477">views</st> <st c="4446">signup</st> <st c="4517">signup</st>
定义注册函数
<st c="4566">/accounts/views.py</st>
from django.shortcuts import render <st c="4664">from django.contrib.auth.forms import UserCreationForm</st>
<st c="4718">def signup(request):</st>
<st c="4739">template_data = {}</st>
<st c="4758">template_data['title'] = 'Sign Up'</st>
<st c="4793">if request.method == 'GET':</st>
<st c="4821">template_data['form'] = UserCreationForm()</st>
<st c="4864">return render(request, 'accounts/signup.html',</st>
<st c="4911">{'template_data': template_data})</st>
-
我们导入了 <st c="4982">UserCreationForm</st>,这是 Django 提供的一个内置表单类。 它旨在简化用户注册表单的创建,特别是用于创建新用户账户。 在 Django 中,我们可以创建自己的 HTML 表单,使用这些 Django 表单的一些,或者甚至自定义 Django 表单。 在这本书中,我们将学习和使用这三种方法。 -
我们创建了我们的 <st c="5354">template_data</st>变量,并给它 分配了一个标题。 -
然后,我们检查当前的 HTTP 请求方法是否是 <st c="5462">GET</st>。如果它是一个 <st c="5478">GET</st>请求,这意味着用户通过 <st c="5555">localhost:8000/accounts/signup</st>URL 导航到注册表单,在这种情况下,我们只需将 <st c="5635">UserCreationForm</st>的一个实例发送到模板。 最后,我们渲染了 <st c="5694">accounts/signup.html</st>模板。
创建账户注册模板
<st c="5815">/accounts/</st><st c="5836">templates</st> <st c="5863">/accounts/templates/</st><st c="5895">accounts</st>
<st c="5911">现在,在</st> <st c="5920">/accounts/templates/accounts/</st> <st c="5949">,创建一个新文件,</st> <st c="5963">文件名为</st> <st c="5970">signup.html</st> <st c="5981">。目前,请用以下内容填充它:</st>
{% extends 'base.html' %}
{% block content %}
<div class="p-3 mt-4">
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card shadow p-3 mb-4 rounded">
<div class="card-body">
<h2>Sign Up</h2>
<hr />
<form method="POST">
{% csrf_token %}
{{ template_data.form.as_p }}
<button type="submit"
class="btn bg-dark text-white">Sign Up
</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock content %}
<st c="6482">让我们</st> <st c="6488">解释</st> <st c="6497">这段代码:</st>
-
<st c="6507">我们扩展了</st><st c="6522">base.html</st><st c="6531">模板。</st> -
<st c="6541">我们定义了一个文本为</st><st c="6584">注册</st><st c="6591">的标题元素。</st> -
<st c="6592">我们定义</st><st c="6603">表单</st><st c="6607">的方法为</st><st c="6627">POST</st><st c="6631">。这意味着当表单提交时,数据将通过 HTTP</st><st c="6740">POST</st><st c="6745">方法发送到当前服务器 URL。</st> -
在表单内部,我们使用 DTL
<st c="6785">csrf_token</st><st c="6795">模板标签。</st><st c="6810">它生成一个</st> **<st c="6825">跨站请求伪造</st>**( CSRF ) 令牌,这 有助于防止 CSRF 攻击。 它确保表单提交来自渲染表单的同一站点。 你应该为你的所有 Django 表单 使用此标签。 ` -
<st c="7045">在表单内部,我们渲染</st><st c="7073">template_data.form</st><st c="7091">,它代表从视图函数传递过来的</st><st c="7114">UserCreationForm</st><st c="7130">实例。</st><st c="7171">.as_p</st><st c="7176">将表单字段渲染为 HTML 段落(</st><st c="7221"><p></st><st c="7225">),每个表单字段都包裹在其自己的段落中。</st><st c="7280">默认情况下,</st><st c="7292">UserCreationForm</st><st c="7308">包含三个表单字段——</st><st c="7338">用户名</st><st c="7346">、</st><st c="7348">密码</st><st c="7356">和</st><st c="7362">密码确认</st><st c="7383">。</st> -
在表单内部,我们包含一个
<st c="7415">submit</st><st c="7421">按钮。</st><st c="7430">此按钮将使用 HTTP</st><st c="7488">POST</st><st c="7492">方法将用户重定向到当前 URL。</st><st c="7501">目前,我们的注册视图函数仅指定了</st><st c="7568">GET</st><st c="7571">方法的逻辑。</st><st c="7580">稍后,我们将实现</st><st c="7623">POST</st><st c="7627">方法的逻辑。</st>
<st c="7635">注意</st>
<st c="7640">除了</st> <st c="7656">form.as_p</st> <st c="7665">之外,还有其他选项可以用来使用不同的 HTML 标签渲染表单元素。</st> <st c="7742">你可以在以下位置找到更多信息:</st> <st c="7778">https://docs.djangoproject.com/en/5.0/ref/forms/api/#output-styles</st> [<st c="7778">https://docs.djangoproject.com/en/5.0/ref/forms/api/#output-styles</st>](https://docs.djangoproject.com/en/5.0/ref/forms/api/#output-styles)<st c="7844">。</st>
现在,让我们通过添加注册链接到基础模板来完成。
添加注册链接到基础模板
在 <st c="7961">/moviesstore/templates/base.html</st> 中,在标题部分,添加以下粗体行:
…
<div class="collapse navbar-collapse"
id="navbarNavAltMarkup">
<div class="navbar-nav ms-auto navbar-ml">
<a class="nav-link"
href="{% url 'home.about' %}">About</a>
<a class="nav-link" href=
"{% url 'movies.index' %}">Movies</a> <st c="8291"><div class="vr bg-white mx-2 d-none</st>
<st c="8326">d-lg-block"></div></st>
<st c="8345"><a class="nav-link"</st>
<st c="8365">href="{% url 'accounts.signup' %}"></st>
<st c="8401">Sign Up</st>
<st c="8409"></a></st> </div>
</div>
…
现在,保存这些文件,运行服务器,并访问

图 8.2 – 注册页面
注意,如果你尝试完成并提交表单,它将显示错误。这是因为我们还没有完成 <st c="9195">注册</st> 函数。
改进注册页面以处理 POST 操作
当用户提交注册表单时,我们必须处理请求并在管理员中创建一个用户。为了实现这一点,我们将修改 <st c="9399">注册</st> 函数。
在 <st c="9415">/accounts/views.py</st> 中,添加以下粗体行:
from django.shortcuts import render
from django.contrib.auth.forms import UserCreationForm <st c="9572">from django.shortcuts import redirect</st> def signup(request):
template_data = {}
template_data['title'] = 'Sign Up'
if request.method == 'GET':
template_data['form'] = UserCreationForm()
return render(request, 'accounts/signup.html',
{'template_data': template_data}) **<st c="9837">elif request.method == 'POST':</st>**
**<st c="9867">form = UserCreationForm(request.POST)</st>**
**<st c="9905">if form.is_valid():</st>**
**<st c="9925">form.save()</st>**
**<st c="9937">return redirect('home.index')</st>**
**<st c="9967">else:</st>**
**<st c="9973">template_data['form'] = form</st>**
**<st c="10002">return render(request, 'accounts/signup.html',</st>**
**<st c="10049">{'template_data': template_data})</st>**
**
-
我们导入
<st c="10123">redirect</st>函数,该函数用于在应用程序内部重定向用户到不同的 URL。 -
我们添加一个
<st c="10230">elif</st>部分。该部分检查 HTTP 请求方法是否为<st c="10299">POST</st>,表示表单已被提交。 -
在
<st c="10361">elif</st>部分内部,我们创建一个<st c="10404">UserCreationForm</st>类的实例,将请求的<st c="10464">POST</st>参数(request.POST `)传递给表单字段以填充数据。这使用提交的数据初始化表单。这将使用提交的数据初始化表单。 -
<st c="10581">if form.is_valid()</st>检查提交的表单数据是否有效,根据<st c="10698">UserCreationForm</st>类中定义的验证规则。这些验证包括两个密码字段匹配,密码不是常见的,用户名是唯一的,以及其他一些验证。-
如果表单数据有效, <st c="10881">form.save()</st>将用户数据保存到数据库。 这意味着使用提供的用户名和密码创建一个新的用户账户。 此外,我们根据 URL <st c="11044">模式名称</st>将用户重定向到 <st c="11044">主页</st>。 -
如果表单数据无效,则执行 <st c="11120">else</st>部分,并将表单(包括错误)传递到模板中,再次渲染 <st c="11221">accounts/signup.html</st>模板。
-


<st c="12437">UserCreationForm</st>
自定义 UserCreationForm
<st c="12483">UserCreationForm</st> <st c="12637">UserCreationForm</st>
-
创建 <st c="12853">CustomUserCreationForm</st>。 -
更新 <st c="12888">注册</st>函数以 使用 <st c="12911">CustomUserCreationForm</st>。 -
自定义错误 显示方式。
创建 CustomUserCreationForm
<st c="13087">/accounts/</st><st c="13124">forms.py</st>
from django.contrib.auth.forms import UserCreationForm
class CustomUserCreationForm(UserCreationForm):
def __init__(self, *args, **kwargs):
super(CustomUserCreationForm, self).__init__
(*args, **kwargs)
for fieldname in ['username', 'password1',
'password2']:
self.fields[fieldname].help_text = None
self.fields[fieldname].widget.attrs.update(
{'class': 'form-control'}
)
-
我们从 Django 的 <st c="13650">UserCreationForm</st>类中导入。 -
我们创建一个名为 <st c="13737">CustomUserCreationForm</st>的新类,它继承自 <st c="13781">UserCreationForm</st>,使其成为 Django 内置用户 创建表单的子类。 -
我们定义类构造函数(即 <st c="13898">__init__</st>方法)。 构造函数通过 <st c="14007">super</st>方法调用父类的构造函数( <st c="13975">UserCreationForm</st>)。 -
然后,我们 遍历由 <st c="14069">UserCreationForm</st>提供的字段。这些是 <st c="14097">'username'</st>, <st c="14109">'password1'</st>和 <st c="14126">'password2'</st>。对于循环中指定的每个字段,我们将 <st c="14188">help_text</st>属性设置为 <st c="14211">None</st>,这将移除与这些字段关联的任何帮助文本。 最后,对于循环中指定的每个字段,我们添加 CSS <st c="14337">form-control</st>类到字段的控件中。 这是一个 Bootstrap 类,它改善了字段的视觉效果。
<st c="14498">signup</st> <st c="14468">CustomUserCreationForm</st>
将注册功能更新为使用 CustomUserCreationForm
在 <st c="14645">/accounts/views.py</st>
from django.shortcuts import render <st c="14747">from .forms import CustomUserCreationForm</st> from django.shortcuts import redirect
def signup(request):
template_data = {}
template_data['title'] = 'Sign Up'
if request.method == 'GET':
template_data['form'] = <st c="14954">CustomUserCreationForm</st>()
return render(request, 'accounts/signup.html',
{'template_data': template_data})
elif request.method == 'POST':
form = <st c="15099">CustomUserCreationForm</st>(request.POST)
if form.is_valid():
form.save()
return redirect('home.index')
else:
template_data['form'] = form
return render(request, 'accounts/signup.html',
{'template_data': template_data})
<st c="15361">UserCreationForm</st> <st c="15402">CustomUserCreationForm</st><st c="15457">UserCreationForm()</st> <st c="15494">CustomUserCreationForm()</st>

自定义错误显示方式
<st c="16036">/accounts/forms.py</st>
from django.contrib.auth.forms import UserCreationForm <st c="16153">from django.forms.utils import ErrorList</st>
<st c="16193">from django.utils.safestring import mark_safe</st>
<st c="16239">class CustomErrorList(ErrorList):</st>
<st c="16273">def __str__(self):</st>
<st c="16292">if not self:</st>
<st c="16305">return ''</st>
<st c="16315">return mark_safe(''.join([</st>
<st c="16342">f'<div class="alert alert-danger" role="alert"></st>
<st c="16390">{e}</div>' for e in self]))</st> class CustomUserCreationForm(UserCreationForm):
…
-
我们导入 <st c="16507">ErrorList</st>类,这是一个默认类,用于存储和显示与 表单字段相关的验证错误消息。 -
我们导入 <st c="16644">mark_safe</st>函数,该函数用于将字符串标记为适合 HTML 渲染,表示它不包含任何有害内容,应该原样渲染,无需转义。 (无需转义直接渲染)。 -
我们定义了一个名为 <st c="16853">CustomErrorList</st>的新类,它扩展了 Django 的 <st c="16893">ErrorList</st>类。 这将是我们定义自定义错误外观和感觉的类。 -
我们重写了 <st c="16991">__str__()</st>方法,这是基类 <st c="17020">ErrorList</st>的方法。 如果错误列表为空(即没有错误),它返回一个空字符串,表示不应生成任何 HTML。 否则,它定义了一个自定义 HTML 代码,该代码使用 <st c="17217"><div></st>元素和 Bootstrap CSS 类来改进错误显示的方式。 它还使用了 <st c="17320">mark_safe</st>函数来渲染代码,保持原样。
<st c="17395">CustomErrorList</st>
<st c="17479">/accounts/views.py</st>
from django.shortcuts import render
from .forms import CustomUserCreationForm<st c="17603">, CustomErrorList</st> from django.shortcuts import redirect
def signup(request):
template_data = {}
template_data['title'] = 'Sign Up'
if request.method == 'GET':
template_data['form'] = CustomUserCreationForm()
return render(request, 'accounts/signup.html',
{'template_data': template_data})
elif request.method == 'POST':
form = CustomUserCreationForm(request.POST<st c="17965">,</st>
<st c="17966">error_class=CustomErrorList</st>)
...
<st c="18017">CustomErrorList</st> <st c="18083">CustomUserCreationForm</st><st c="18193">CustomErrorList</st>

创建登录页面
-
配置登录 URL。 -
定义 <st c="18945">登录</st>函数。 -
创建一个账户 登录模板。 -
在基础模板中添加一个链接。 -
将已注册用户重定向到 登录页面。
配置登录 URL
<st c="19187">/accounts/urls.py</st>
from django.urls import path
from . import views
urlpatterns = [
path('signup', views.signup, name='accounts.signup'), <st c="19357">path('login/', views.login, name='accounts.login'),</st> ]
<st c="19435">/accounts/login</st> <st c="19477">views</st> <st c="19482">登录</st>
<st c="19567">登录</st>
定义登录函数
<st c="19610">/accounts/views.py</st>
from django.shortcuts import render <st c="19708">from django.contrib.auth import login as auth_login, authenticate</st> from .forms import CustomUserCreationForm, CustomErrorList
from django.shortcuts import redirect <st c="19871">def login(request):</st>
<st c="19890">template_data = {}</st>
<st c="19909">template_data['title'] = 'Login'</st>
<st c="19942">if request.method == 'GET':</st>
<st c="19970">return render(request, 'accounts/login.html',</st>
<st c="20016">{'template_data': template_data})</st>
<st c="20050">elif request.method == 'POST':</st>
<st c="20081">user = authenticate(</st>
<st c="20102">request,</st>
<st c="20111">username = request.POST['username'],</st>
<st c="20148">password = request.POST['password']</st>
<st c="20184">)</st>
<st c="20186">if user is None:</st>
<st c="20203">template_data['error'] =</st>
<st c="20228">'The username or password is incorrect.'</st>
<st c="20269">return render(request, 'accounts/login.html',</st>
<st c="20315">{'template_data': template_data})</st>
<st c="20349">else:</st>
<st c="20355">auth_login(request, user)</st>
<st c="20381">return redirect('home.index')</st> def signup(request):
…
-
我们导入 <st c="20469">login</st>和 <st c="20479">authenticate</st>。这些用于用户认证。 我们导入 <st c="20543">login</st>并使用别名( <st c="20564">auth_login</st>)以避免与 <st c="20606">login</st>函数名混淆。 -
我们创建 <st c="20641">login</st>函数。 此函数定义 <st c="20679">template_data</st>并检查 <st c="20697">request.method</st>。 -
对于 <st c="20724">GET</st>请求,函数渲染 <st c="20763">accounts/login.html</st>模板。 -
对于 <st c="20797">POST</st>请求,函数尝试使用提供的 <st c="20878">username</st>和 <st c="20891">password</st>进行用户认证。如果认证失败,它将再次渲染带有错误信息的登录模板。 如果认证成功,它将登录用户并将 他们重定向到 主页 。
<st c="21119">login</st>
创建账户登录模板
<st c="21171">/accounts/templates/accounts/</st><st c="21221">login.html</st>
{% extends 'base.html' %}
{% block content %}
<div class="p-3 mt-4">
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card shadow p-3 mb-4 rounded">
<div class="card-body">
<h2>Login</h2>
<hr />
{% if template_data.error %}
<div class="alert alert-danger" role="alert">
{{ template_data.error }}
</div>
{% endif %}
<form method="POST">
{% csrf_token %}
<p>
<label for="username">Username</label>
<input id="username" type="text"
name="username" required
autocomplete="username"
class="form-control">
</p>
<p>
<label for="password">Password</label>
<input id="password" type="password"
name="password" required
autocomplete="current-password"
class="form-control">
</p>
<div class="text-center">
<button type="submit"
class="btn bg-dark text-white">Login
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock content %}
-
我们扩展 <st c="22254">base.html</st>模板并定义一个包含文本 <st c="22311">Login</st>的标题元素。 -
我们检查是否有错误,如果有, 则显示它。 -
我们创建一个具有 <st c="22412">POST</st>方法 和<st c="22432">csrf_token</st>令牌 的 HTML 表单。此表单包含两个输入,一个用于用户名,另一个用于密码。 它还包含一个 提交按钮。
将链接添加到基本模板
<st c="22715">/moviesstore/templates/base.html</st>
…
<div class="navbar-nav ms-auto navbar-ml">
<a class="nav-link"
href="{% url 'home.about' %}">About</a>
<a class="nav-link"
href="{% url 'movies.index' %}">Movies</a>
<div class=
"vr bg-white mx-2 d-none d-lg-block"></div> <st c="23035"><a class="nav-link"</st>
<st c="23054">href="{% url 'accounts.login' %}">Login</a></st> <a class="nav-link"
href="{% url 'accounts.signup' %}">Sign Up
</a>
</div>
…

将注册用户重定向到登录页面
<st c="23625">/accounts/views.py</st>
…
def signup(request):
template_data = {}
template_data['title'] = 'Sign Up'
if request.method == 'GET':
template_data['form'] = CustomUserCreationForm()
return render(request, 'accounts/signup.html',
{'template_data': template_data})
elif request.method == 'POST':
form = CustomUserCreationForm(request.POST,
error_class=CustomErrorList)
if form.is_valid():
form.save()
return redirect(<st c="24058">'accounts.login</st>')
…
实现登出功能
-
配置一个 登出 URL。 -
定义 <st c="24380">登出</st>函数。 -
在 基本模板中 添加一个 链接。
配置登出 URL
<st c="24512">/accounts/urls.py</st>
from django.urls import path
from . import views
urlpatterns = [
path('signup', views.signup, name='accounts.signup'),
path('login/', views.login, name='accounts.login'), <st c="24732">path('logout/', views.logout, name='accounts.logout'),</st> ]
<st c="24814">/accounts/logout</st> <st c="24857">views</st> <st c="24888">登出</st>
定义登出函数
<st c="24932">/accounts/views.py</st>
from django.shortcuts import render
from django.contrib.auth import login as auth_login, authenticate<st c="25095">, logout as auth_logout</st> from .forms import CustomUserCreationForm, CustomErrorList
from django.shortcuts import redirect <st c="25216">from django.contrib.auth.decorators import login_required</st>
<st c="25273">@login_required</st>
<st c="25289">def logout(request):</st>
<st c="25310">auth_logout(request)</st>
<st c="25331">return redirect('home.index')</st> def login(request):
…
-
我们导入 <st c="25422">登出</st>函数作为 <st c="25441">auth_logout</st>。这用于登出用户。 -
我们导入了 <st c="25496">login_required</st>,这是一个装饰器,用于确保只有经过身份验证的用户才能访问特定的视图函数。 Django 装饰器 是一个函数,它 包装另一个函数或方法以修改其行为。 装饰器通常用于诸如认证、权限 和日志记录等。 -
我们创建了一个 <st c="25808">注销</st>函数,该函数使用了 <st c="25840">login_required</st>装饰器。 这意味着只有经过身份验证的用户才能访问 此函数。 -
<st c="25937">注销</st>函数调用 <st c="25959">auth_logout</st>,该函数用于注销当前用户。 然后,该函数将用户重定向到 主页 。
将链接添加到基本模板
<st c="26165">/moviesstore/templates/base.html</st>
…
<a class="nav-link"
href="{% url 'home.about' %}">About</a>
<a class="nav-link"
href="{% url 'movies.index' %}">Movies</a>
<div class=
"vr bg-white mx-2 d-none d-lg-block"></div> <st c="26444">{% if user.is_authenticated %}</st>
<st c="26474"><a class="nav-link"</st>
<st c="26494">href="{% url 'accounts.logout' %}">Logout ({{</st>
<st c="26540">user.username }})</a></st>
<st c="26562">{% else %}</st> <a class="nav-link"
href="{% url 'accounts.login' %}">Login</a>
<a class="nav-link"
href="{% url 'accounts.signup' %}">Sign Up
</a> <st c="26706">{% endif %}</st> …

总结
第十章:9
让用户创建、阅读、更新和删除电影评论
-
创建评论模型 -
创建评论 -
阅读评论 -
更新 评论 -
删除 评论
技术要求
创建评论模型
-
创建 评论模型。 -
应用迁移。 -
将评论模型添加到 管理面板。
创建评论模型
<st c="1421">movies</st> <st c="1440">/movies/models.py</st>
from django.db import models <st c="1547">from django.contrib.auth.models import User</st> class Movie(models.Model):
… <st c="1620">class Review(models.Model):</st>
<st c="1647">id = models.AutoField(primary_key=True)</st>
<st c="1687">comment = models.CharField(max_length=255)</st>
<st c="1730">date = models.DateTimeField(auto_now_add=True)</st>
<st c="1777">movie = models.ForeignKey(Movie,</st>
<st c="1810">on_delete=models.CASCADE)</st>
<st c="1836">user = models.ForeignKey(User,</st>
**<st c="1867">on_delete=models.CASCADE)</st>**
**<st c="1893">def __str__(self):</st>**
**<st c="1912">return str(self.id) + ' - ' + self.movie.name</st>**
**
-
我们从 Django 的 <st c="2007">User</st>模型中导入 <st c="2032">django.contrib.auth.models</st>模块。 -
我们定义了一个名为 <st c="2098">Review</st>的 Python 类,它继承自 <st c="2126">models.Model</st>。这意味着 <st c="2156">Review</st>是一个 Django 模型类。 -
在 <st c="2199">Review</st>类中,我们定义了 几个字段: -
<st c="2238">id</st>是一个 <st c="2248">AutoField</st>,它自动为数据库中添加的每个新记录增加其值。 <st c="2347">primary_key=True</st>参数指定该字段是表的 主键,唯一标识 每条记录。 -
<st c="2467">评论</st>是一个 <st c="2481">CharField</st>,它代表一个最大长度为 255 个字符的字符串字段。 它存储电影评论文本。 -
<st c="2597">日期</st>是一个 <st c="2608">DateTimeField</st>,用于日期和时间数据。 <st c="2665">auto_now_add=True</st>确保在创建评论时,日期和时间会自动设置为当前日期和时间。 -
<st c="2792">电影</st>是与 <st c="2836">Movie</st>模型的外键关系。 评论与电影相关联。 <st c="2890">on_delete</st>参数指定如何处理与评论相关联的电影的删除。 在这种情况下, <st c="3006">on_delete=models.CASCADE</st>意味着如果相关电影被删除,相关的评论也将被删除。 -
<st c="3118">用户</st>是另一个外键关系,但指向 <st c="3171">User</st>模型。 评论与用户(撰写评论的人)相关联。 类似于 <st c="3268">电影</st>属性, <st c="3285">on_delete=models.CASCADE</st>指定如果相关用户被删除,相关的评论也将被删除。
-
-
<st c="3400">__str__</st>是一个返回评论字符串表示的方法。 在这种情况下,它返回一个由评论 ID 和与评论相关的电影名称组成的字符串。 的 评论。
应用迁移
<st c="3641">Review</st>
For macOS, run this:
python3 manage.py makemigrations
python3 manage.py migrate
For Windows, run this:
python manage.py makemigrations
python manage.py migrate

Add the review model to the admin panel
<st c="4146">Review</st> <st c="4162">admin</st><st c="4180">/movies/admin.py</st>
from django.contrib import admin
from .models import Movie<st c="4331">, Review</st> class MovieAdmin(admin.ModelAdmin):
ordering = ['name']
search_fields = ['name']
admin.site.register(Movie, MovieAdmin) <st c="4561">/admin</st>. The review model will now show up (as shown in *<st c="4616">Figure 9</st>**<st c="4624">.2</st>*):

<st c="4812">Figure 9.2 – The admin page with reviews available</st>
<st c="4862">Now that we have created and applied our</st> `<st c="4904">Review</st>` <st c="4910">model, let’s create the functionality to</st> <st c="4952">create reviews.</st>
<st c="4967">Creating reviews</st>
<st c="4984">To allow users to</st> <st c="5002">create reviews, we need to follow the</st> <st c="5041">next steps:</st>
1. <st c="5052">Update the</st> `<st c="5064">movies.show</st>` <st c="5075">template.</st>
2. <st c="5085">Define the</st> `<st c="5097">create_review</st>` <st c="5110">function.</st>
3. <st c="5120">Configure</st> <st c="5130">the</st> `<st c="5135">create</st>` `<st c="5142">review</st>` <st c="5148">URL.</st>
<st c="5153">Updating the movies.show template</st>
<st c="5187">We will include a form to</st> <st c="5213">allow authenticated users to create reviews.</st> <st c="5259">This form will be included in the</st> `<st c="5293">movies.show</st>` <st c="5304">template.</st> <st c="5315">In the</st> `<st c="5322">/movies/templates/movies/show.html</st>` <st c="5356">file, add the following, as presented</st> <st c="5395">in bold:</st>
…
<p><b>Price:</b> ${{ template_data.movie.price }}</p> <st c="5459">{% if user.is_authenticated %}</st>
**
<div class="col-md-6 mx-auto mb-3 text-center">
<img src="img/{{ template_data.movie.image.url }}"
class="rounded img-card-400" />
</div>
…
{% endblock content %}**
**<st c="6264">Let’s explain the</st> <st c="6282">preceding code:</st>
* <st c="6298">We use the</st> `<st c="6310">{% if user.is_authenticated %}</st>` <st c="6340">DTL conditional statement that checks whether the user is authenticated (logged in).</st> <st c="6426">If the user is authenticated, the block of HTML code within the</st> `<st c="6490">if</st>` <st c="6492">statement will be rendered</st> <st c="6520">and displayed.</st>
* <st c="6534">We create an HTML form with the</st> `<st c="6567">POST</st>` <st c="6571">method and the</st> `<st c="6587">csrf_token</st>` <st c="6597">token.</st> <st c="6605">This form contains a single input named</st> `<st c="6645">comment</st>`<st c="6652">. This input stores the review text.</st> <st c="6689">The form also contains a</st> <st c="6714">submit button.</st>
* <st c="6728">The form is linked</st> <st c="6747">to the</st> `<st c="6755">movies.create_review</st>` <st c="6775">URL, and it also passes the movie ID to that URL.</st> <st c="6826">The movie ID will be used to link the current comment with the movie that</st> <st c="6900">it represents.</st>
## <st c="6914">Defining the create_review function</st>
<st c="6950">In</st> `<st c="6954">/movies/views.py</st>`<st c="6970">, add the following, as</st> <st c="6994">presented</st> <st c="7004">in bold:</st>
from django.shortcuts import render
…
def show(request):
… <st c="7194">@login_required</st>
**<st c="7539">Let’s explain the</st> <st c="7558">preceding code:</st>
* <st c="7573">We import the</st> `<st c="7588">redirect</st>` <st c="7596">function, which is</st> <st c="7616">used to redirect the user to a</st> <st c="7647">different URL.</st>
* <st c="7661">We import the</st> `<st c="7676">Review</st>` <st c="7682">model, which will be used to create</st> <st c="7719">new reviews.</st>
* <st c="7731">We import</st> `<st c="7742">login_required</st>`<st c="7756">, which is used to verify that only logged users can access the</st> `<st c="7820">create_review</st>` <st c="7833">function.</st> <st c="7844">If a guest user attempts to access this function via the corresponding URL, they will be redirected to the</st> <st c="7951">login page.</st>
* <st c="7962">We create the</st> `<st c="7977">create_review</st>` <st c="7990">function that handles creating</st> <st c="8022">a review.</st>
* <st c="8031">The</st> `<st c="8036">create_review</st>` <st c="8049">takes two arguments: the</st> `<st c="8075">request</st>` <st c="8082">that contains information about the HTTP request, and the</st> `<st c="8141">id</st>`<st c="8143">, which represents the ID of the movie for which a review is</st> <st c="8204">being created.</st>
* <st c="8218">Then, we check whether the request method is</st> `<st c="8264">POST</st>` <st c="8268">and the</st> `<st c="8277">comment</st>` <st c="8284">field in the request’s</st> `<st c="8308">POST</st>` <st c="8312">data is not empty.</st> <st c="8332">If that is</st> `<st c="8343">TRUE</st>`<st c="8347">, the</st> <st c="8353">following happens:</st>
* <st c="8371">We retrieve the movie using</st> `<st c="8400">Movie.objects.get(id=id)</st>` <st c="8424">based on the</st> <st c="8438">provided</st> `<st c="8447">id</st>`<st c="8449">.</st>
* <st c="8450">We create a new</st> `<st c="8467">Review</st>` <st c="8473">object.</st>
* <st c="8481">We set the review properties</st> <st c="8511">as follows:</st>
* <st c="8522">We set the</st> `<st c="8534">comment</st>` <st c="8541">based on the comments collected in</st> <st c="8577">the form</st>
* <st c="8585">We set the</st> `<st c="8597">movie</st>`<st c="8602">, based on the retrieved movie from</st> <st c="8638">the database</st>
* <st c="8650">We set the</st> `<st c="8662">user</st>`<st c="8666">, based on the</st> <st c="8681">authenticated user who submitted</st> <st c="8714">the form.</st>
* <st c="8723">Finally, we save the review to the database and redirect the user to the movie</st> <st c="8803">show page.</st>
* <st c="8813">In the</st> `<st c="8821">else</st>` <st c="8825">case, we redirect the user to the movie show page using the</st> `<st c="8886">redirect('movies.show',</st>` `<st c="8910">id=id)</st>` <st c="8916">code.</st>
## <st c="8922">Configuring the create review URL</st>
<st c="8956">In</st> `<st c="8960">/movies/urls.py</st>`<st c="8975">, add the next path as highlighted</st> <st c="9010">in</st> <st c="9012">bold:</st>
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='movies.index'),
path('<int:id>/', views.show, name='movies.show'), <st c="9179">path('<int:id>/review/create/', views.create_review,</st>
<st c="9263">Let’s analyze the new path.</st> <st c="9291">The</st> `<st c="9295"><int:id></st>` <st c="9303">part indicates that this path expects an integer value to be passed from the URL and that the integer value will be associated with a variable named</st> `<st c="9453">id</st>`<st c="9455">. The</st> `<st c="9461">id</st>` <st c="9463">variable will be used to identify to which movie the review that we want to create is linked.</st> <st c="9558">For example, if the form is submitted to</st> `<st c="9599">movies/1/review/create</st>`<st c="9621">, it indicates that the new review will be associated with the movie</st> <st c="9690">with</st> `<st c="9695">id</st>`<st c="9697">=</st>`<st c="9699">1</st>`<st c="9700">.</st>
<st c="9701">Now save those files, run the</st> <st c="9732">server, and go to</st> `<st c="9750">http://localhost:8000/movies</st>`<st c="9778">. Click on a specific movie and you will see the form to create reviews (</st>*<st c="9851">Figure 9</st>**<st c="9860">.3</st>*<st c="9862">).</st>

<st c="10073">Figure 9.3 – A movie page with the review form</st>
<st c="10119">Then, enter a comment and click</st> **<st c="10152">Add Review</st>**<st c="10162">. A new review should be created, and you should be redirected to the movie show page.</st> <st c="10249">Go to the admin panel, click</st> **<st c="10278">Reviews</st>**<st c="10285">, and you will see the new review there (</st>*<st c="10326">Figure 9</st>**<st c="10335">.4</st>*<st c="10337">).</st>

<st c="10478">Figure 9.4 – The reviews admin page</st>
<st c="10513">Let’s now include a</st> <st c="10533">functionality to read and list reviews from our</st> <st c="10582">web application.</st>
# <st c="10598">Reading reviews</st>
<st c="10614">To be able to read and list reviews, we</st> <st c="10655">need to follow the steps</st> <st c="10680">that follow:</st>
1. <st c="10692">Update the</st> `<st c="10704">movies.show</st>` <st c="10715">template.</st>
2. <st c="10725">Update the</st> `<st c="10737">show</st>` <st c="10741">function.</st>
## <st c="10751">Updating the movies.show template</st>
<st c="10785">We will list the</st> <st c="10802">reviews in the</st> `<st c="10818">movies.show</st>` <st c="10829">template.</st> <st c="10840">In the</st> `<st c="10847">/movies/templates/movies/show.html</st>` <st c="10881">file, add the following, as highlighted</st> <st c="10922">in bold:</st>
…
<p><b>价格:</b> ${{ template_data.movie.price }}
</p> <st c="10987"><h2>评论</h2></st>
-
评论者:{{ review.user.username }} {{ review.date }} {{ review.comment }}
…
<st c="11353">We have added a new section inside the template.</st> <st c="11403">This section iterates through the</st> `<st c="11437">reviews</st>` <st c="11444">and displays</st> <st c="11457">the review</st> `<st c="11469">date</st>` <st c="11473">and</st> `<st c="11478">comment</st>`<st c="11485">, as well as the username of the user who created</st> <st c="11535">the review.</st>
## <st c="11546">Updating the show function</st>
<st c="11573">In</st> `<st c="11577">/movies/views.py</st>`<st c="11593">, add the following, as</st> <st c="11616">highlighted</st> <st c="11629">in bold:</st>
…
def show(request, id):
movie = Movie.objects.get(id=id) <st c="11695">reviews = Review.objects.filter(movie=movie)</st> template_data = {}
template_data['title'] = movie.name
template_data['movie'] = movie <st c="11826">template_data['reviews'] = reviews</st> return render(request, 'movies/show.html',
{'template_data': template_data})
…
<st c="11939">Let’s explain the</st> <st c="11958">preceding code.</st>
* <st c="11973">We retrieve all review objects that are associated with the movie that we are showing.</st> <st c="12061">To do this, we use the</st> `<st c="12084">filter</st>` <st c="12090">method to limit the query to reviews related to the</st> <st c="12143">specific movie.</st>
* <st c="12158">We add those reviews to the</st> `<st c="12187">template_data</st>` <st c="12200">dictionary, which is passed to the</st> `<st c="12236">movies/show.html</st>` <st c="12252">template.</st>
<st c="12262">Now, save those files, run the server, and go to</st> `<st c="12312">http://localhost:8000/movies</st>`<st c="12340">. Click on a specific movie that contains reviews and you will see the movie information, including its corresponding reviews (</st>*<st c="12467">Figure 9</st>**<st c="12476">.5</st>*<st c="12478">).</st>

<st c="12700">Figure 9.5 – A movie page with reviews</st>
<st c="12738">Now, let’s move on</st> <st c="12757">to</st> <st c="12761">updating reviews.</st>
# <st c="12778">Updating a review</st>
<st c="12796">To be able to update reviews, we need to</st> <st c="12838">follow</st> <st c="12845">these steps:</st>
1. <st c="12857">Update the</st> `<st c="12869">movies.show</st>` <st c="12880">template.</st>
2. <st c="12890">Create the</st> `<st c="12902">movies</st>` `<st c="12909">edit_review</st>` <st c="12920">template.</st>
3. <st c="12930">Define the</st> `<st c="12942">edit_review</st>` <st c="12953">function.</st>
4. <st c="12963">Configure the</st> `<st c="12978">edit</st>` `<st c="12983">review</st>` <st c="12989">URL.</st>
## <st c="12994">Updating movies.show template</st>
<st c="13024">In</st> `<st c="13028">/movies/templates/movies/show.html</st>` <st c="13062">file, add the following</st> <st c="13087">bold text:</st>
…
{% for review in template_data.reviews %}
<li class="list-group-item pb-3 pt-3">
<h5 class="card-title">
评论者:{{ review.user.username }}
</h5>
<h6 class="card-subtitle mb-2 text-muted">
{{ review.date }}
</h6>
<p class="card-text">{{ review.comment }}</p> <st c="13360">{% if user.is_authenticated and user ==</st>
{% endfor %}
…
<st c="13568">We added a code snippet for</st> <st c="13597">each review that is displayed.</st> <st c="13628">That code checks whether a user is authenticated and whether the user is the one who wrote a specific review.</st> <st c="13738">If both of these conditions are true, it will render the</st> `<st c="13827">movies.edit_review</st>` <st c="13845">URL.</st>
## <st c="13850">Creating the movies edit_review template</st>
<st c="13891">Now, in</st> `<st c="13900">/movies/templates/movies/</st>`<st c="13925">, create a new file,</st> `<st c="13946">edit_review.html</st>`<st c="13962">. For now, fill it in with</st> <st c="13989">the</st> <st c="13993">following:</st>
{% extends 'base.html' %}
{% block content %}
<div class="row mt-3">
<div class="col mx-auto mb-3">
<h2>编辑审查</h2>
<hr />
<form method="POST">
{% csrf_token %}
<p>
<label for="comment">评论:</label>
<textarea name="comment" required
class="form-control" id="comment">{{
template_data.review.comment }}</textarea>
</p>
<div class="text-start">
<button type="submit"
class="btn bg-dark text-white">编辑评论
</button>
</div>
</form>
</div>
</div>
{% endblock content %}
<st c="14538">We have created a form to edit the review.</st> <st c="14582">This form is very similar to the review creation form.</st> <st c="14637">The differences</st> <st c="14653">are</st> <st c="14657">as follows:</st>
* <st c="14668">We removed the form action, which means that the form will be submitted to the</st> <st c="14748">current URL</st>
* <st c="14759">We displayed the current review comment value inside the</st> <st c="14817">text area</st>
* <st c="14826">We modified the</st> <st c="14843">button text</st>
## <st c="14854">Defining the edit_review function</st>
<st c="14888">In</st> `<st c="14892">/movies/views.py</st>`<st c="14908">, add the</st> <st c="14917">following, as highlighted</st> <st c="14944">in bold:</st>
from django.shortcuts import render, redirect
**<st c="15641">Let’s explain the</st> <st c="15660">preceding code:</st>
* <st c="15675">We import the</st> `<st c="15690">get_object_or_404</st>` <st c="15707">function, which retrieves an object from the database or</st> <st c="15764">raises an HTTP 404 (Not Found) error (if the object is</st> <st c="15820">not found).</st>
* <st c="15831">We use the</st> `<st c="15843">@login_required</st>` <st c="15858">decorator to ensure that the</st> `<st c="15888">edit_review</st>` <st c="15899">function can only be accessed by authenticated users.</st> <st c="15954">If an unauthenticated user tries to access this function, they will be redirected to the</st> <st c="16043">login page.</st>
* <st c="16054">We define the</st> `<st c="16069">edit_review</st>` <st c="16080">function, which takes three parameters: the request, the movie ID, and the</st> <st c="16156">review ID.</st>
* <st c="16166">We retrieve the</st> `<st c="16183">Review</st>` <st c="16189">object with the given</st> `<st c="16212">review_id</st>`<st c="16221">. If the review does not exist, a 404 error will</st> <st c="16270">be raised.</st>
* <st c="16280">We check whether the current user (</st>`<st c="16316">request.user</st>`<st c="16329">) is the owner of the review to be edited (</st>`<st c="16373">review.user</st>`<st c="16385">).</st> <st c="16389">If the user does not own the review, the function redirects them to the</st> `<st c="16461">movie.show</st>` <st c="16471">page.</st>
* <st c="16477">Then, we check whether the request method is</st> `<st c="16523">GET</st>`<st c="16526">. In that case, the function prepares data for the template and renders the</st> `<st c="16602">edit_review.html</st>` <st c="16618">template.</st>
* <st c="16628">If the request method is</st> `<st c="16654">POST</st>` <st c="16658">and the</st> `<st c="16667">comment</st>` <st c="16674">field in the request’s</st> `<st c="16698">POST</st>` <st c="16702">data is not empty, the</st> <st c="16725">function proceeds to update the review and redirects the user to the movie</st> <st c="16801">show page.</st>
* <st c="16811">In any other case, the function redirects the user to the movie</st> <st c="16876">show page.</st>
<st c="16886">Note</st>
<st c="16891">You can improve the look and feel of these functionalities by including your own error messages.</st> <st c="16989">You can use the</st> `<st c="17005">login</st>` <st c="17010">template and the</st> `<st c="17028">login</st>` <st c="17033">function, which uses and passes a</st> `<st c="17068">template_data.error</st>`<st c="17087">, as</st> <st c="17092">a base.</st>
## <st c="17099">Configuring the edit_review URL</st>
<st c="17131">In</st> `<st c="17135">/movies/urls.py</st>`<st c="17150">, add the next path, as shown</st> <st c="17180">in</st> <st c="17182">bold:</st>
从 django.urls 导入 path
从 . 导入 views
urlpatterns = [
路径('', views.index, name='movies.index'),
路径('<int:id>/', views.show, name='movies.show'),
路径('<int:id>/review/create/', views.create_review,
name='movies.create_review'), <st c="17432">路径('<int:id>/review/<int:review_id>/edit/',</st><st c="17477">views.edit_review, name='movies.edit_review'),</st> ]
<st c="17526">This path captures two integer values (the movie ID and review ID) from the URL and passes them to the</st> `<st c="17629">edit_review</st>` <st c="17640">function</st> <st c="17649">as arguments.</st>
<st c="17663">Now, save those files, run the server, and go to</st> `<st c="17713">http://localhost:8000/movies</st>`<st c="17741">. Click on a specific movie that contains a review you created, then click the</st> **<st c="17820">Edit</st>** <st c="17824">button (</st>*<st c="17833">Figure 9</st>**<st c="17842">.6</st>*<st c="17844">).</st>

<st c="18050">Figure 9.6 – A movie page with reviews and an edit button</st>
<st c="18107">An edit form will be shown.</st> <st c="18136">Modify the review and click the</st> **<st c="18168">Edit Review</st>** <st c="18179">button (</st>*<st c="18188">Figure 9</st>**<st c="18197">.7</st>*<st c="18199">).</st>

<st c="18270">Figure 9.7 – The Edit Review page</st>
<st c="18303">You will be redirected to the movie show page.</st> <st c="18351">The new review comment</st> <st c="18374">should appear.</st>
<st c="18388">We just learned how to update</st> <st c="18418">reviews and models in general, so let’s move to the final functionality and learn how to</st> <st c="18508">delete information.</st>
# <st c="18527">Deleting a review</st>
<st c="18545">To be able to delete reviews, we</st> <st c="18579">need to follow the</st> <st c="18598">ensuing steps:</st>
1. <st c="18612">Update the</st> `<st c="18624">movies.show</st>` <st c="18635">template.</st>
2. <st c="18645">Define the</st> `<st c="18657">delete_review</st>` <st c="18670">function.</st>
3. <st c="18680">Configure the</st> `<st c="18695">delete</st>` `<st c="18702">review</st>` <st c="18708">URL.</st>
## <st c="18713">Updating the movies.show template</st>
<st c="18747">In the</st> `<st c="18755">/movies/templates/movies/show.html</st>` <st c="18789">file, add the</st> <st c="18803">following</st> <st c="18814">bolded code:</st>
…
<h5 class="card-title">
评论者:{{ review.user.username }}
</h5>
<h6 class="card-subtitle mb-2 text-muted">
{{ review.date }}
</h6>
<p class="card-text">{{ review.comment }}</p>
{% if user.is_authenticated and user ==
review.user %}
<a class="btn btn-primary"
href="{% url 'movies.edit_review' %}
id=template_data.movie.id
review_id=review.id %}">编辑
</a> <st c="19184"><a class="btn btn-danger"</st>
…
<st c="19321">We have added a new delete</st> <st c="19348">button.</st> <st c="19357">This button links to the</st> `<st c="19382">movies.delete_review</st>` <st c="19402">URL, and much like the</st> **<st c="19426">Edit</st>** <st c="19430">button, it passes the movie ID and the</st> <st c="19470">review ID.</st>
## <st c="19480">Defining the delete_review function</st>
<st c="19516">In</st> `<st c="19520">/movies/views.py</st>`<st c="19536">, add the following</st> <st c="19555">bold code at the end of</st> <st c="19580">the file:</st>
…
**
**<st c="19771">Let’s explain the</st> <st c="19790">preceding code:</st>
* <st c="19805">We use the</st> `<st c="19817">@login_required</st>` <st c="19832">decorator to ensure that the</st> `<st c="19862">delete_review</st>` <st c="19875">function can be only accessed by authenticated users.</st> <st c="19930">If an unauthenticated user tries to access this function, they will be redirected to the</st> <st c="20019">login page.</st>
* <st c="20030">We retrieve the</st> `<st c="20047">Review</st>` <st c="20053">object with the given</st> `<st c="20076">review_id</st>` <st c="20085">that belongs to the current user (</st>`<st c="20120">request.user</st>`<st c="20133">).</st> <st c="20137">If the review does not exist, or if the user does not own the review, an HTTP 404 error will</st> <st c="20230">be raised.</st>
* <st c="20240">We delete the review from the database using the Django model’s</st> `<st c="20305">delete()</st>` <st c="20313">method.</st>
* <st c="20321">We redirect to the previous movie</st> <st c="20356">show page.</st>
## <st c="20366">Configuring the delete_review URL</st>
<st c="20400">In</st> `<st c="20404">/movies/urls.py</st>`<st c="20419">, add the following path, as highlighted</st> <st c="20459">in bold:</st>
…
urlpatterns = [
…
路径('<int:id>/review/<int:review_id>/edit/',
views.edit_review, name='movies.edit_review'), <st c="20581">路径('<int:id>/review/<int:review_id>/delete/',</st>
<st c="20681">This path captures two integer values (the movie ID and the review ID) from the URL and passes them as arguments to the</st> `<st c="20801">delete_review</st>` <st c="20814">function.</st>
<st c="20824">Now, save those files, run</st> <st c="20851">the server, and go to</st> `<st c="20874">http://localhost:8000/movies</st>`<st c="20902">. Click on a specific movie that contains a review that you created, then click the</st> **<st c="20986">Delete</st>** <st c="20992">button (</st>*<st c="21001">Figure 9</st>**<st c="21010">.8</st>*<st c="21012">).</st>

<st c="21226">Figure 9.8 – A movie page with reviews and a Delete button</st>
<st c="21284">The review should be deleted, and</st> <st c="21318">you should be redirected to the movie</st> <st c="21357">show page.</st>
# <st c="21367">Summary</st>
<st c="21375">In this chapter, we implemented a complete CRUD for movie reviews.</st> <st c="21443">With the tools we’ve developed, we can now create various CRUD systems by applying the knowledge gained in this chapter to other projects and models.</st> <st c="21593">As for the</st> *<st c="21604">Movies Store</st>* <st c="21616">project, users can now create, read, update, and delete reviews.</st> <st c="21682">Additionally, we have acquired the skills to manage application authorization, restricting access to certain routes and functions for</st> <st c="21816">non-logged-in users.</st>
<st c="21836">In the next chapter, we will learn how to create a</st> <st c="21888">shopping cart.</st>********
```**
# 第十一章:<st c="0">10</st>
# <st c="3">实现购物车系统</st>
<st c="39">在本章中,我们将学习如何为网站创建购物车。</st> <st c="121">为了实现这一功能,我们需要了解</st> <st c="169">如何</st> **<st c="174">使用网络会话</st>** <st c="186">以及如何使用</st> **<st c="207">Django 会话</st>**<st c="222">。Django 会话</st> <st c="239">将用于在用户浏览网站时存储特定于用户的信息。</st>
<st c="322">在本章中,我们将涵盖以下主题:</st> <st c="364">包括:</st>
+ <st c="381">介绍</st> <st c="394">网络会话</st>
+ <st c="406">创建一个</st> <st c="418">购物车应用程序</st>
+ <st c="426">将电影添加到购物车</st> <st c="444">。</st>
+ <st c="452">列出添加到购物车的电影</st> <st c="477">。</st>
+ <st c="485">从购物车中删除电影</st> <st c="507">。</st>
<st c="515">到本章结束时,你将具备使用网络会话、实现购物车系统以及跟踪和维护同一用户之间请求的用户信息所需的知识。</st> <st c="697">。</st>
# <st c="707">技术要求</st>
<st c="730">在本章中,我们将使用</st> **<st c="765">Python 3.10+</st>**<st c="777">。此外,我们将在本书中使用</st> **<st c="814">Visual Studio Code</st>** <st c="821">编辑器,您可以从</st> <st c="866">以下位置</st> [<st c="871">https://code.visualstudio.com/</st>](https://code.visualstudio.com/)<st c="901">下载。</st>
<st c="902">本章的代码位于</st> <st c="940">以下位置</st> [<st c="943">https://github.com/PacktPublishing/Django-5-for-the-Impatient-Second-Edition/tree/main/Chapter10/moviesstore</st>](https://github.com/PacktPublishing/Django-5-for-the-Impatient-Second-Edition/tree/main/Chapter10/moviesstore)<st c="1051">。</st>
<st c="1052">本章的 CiA 视频可以在</st> <st c="1097">以下位置找到</st> [<st c="1100">https://packt.link/mEcH8</st>](https://packt.link/mEcH8)
# <st c="1124">介绍网络会话</st>
<st c="1149">你理解登录系统是如何工作的吗?</st> <st c="1200">应用程序是如何识别我的连接状态的?</st> <st c="1257">它是如何区分为已登录用户显示注销按钮和为未连接的朋友显示登录按钮的?</st> <st c="1391">应用程序会保留我的</st> <st c="1431">连接状态多长时间?</st>
<st c="1449">在本章中,我们将解答这些问题,并探讨网络会话在 Web 应用程序开发中的重要性。</st> <st c="1582">我们将按照以下顺序探讨这些元素:</st>
1. <st c="1636">HTTP</st> <st c="1642">协议限制</st>
1. <st c="1662">网络会话</st>
1. <st c="1675">Django</st> <st c="1683">登录场景</st>
1. <st c="1697">Django 会话</st>
## <st c="1713">HTTP 协议限制</st>
<st c="1739">目前,我们与电影商店网站的交互依赖于 HTTP 协议。</st> <st c="1826">例如,要访问电影信息,我们使用</st> `<st c="1880">http://localhost:8000/movies</st>`<st c="1908">,而登录时使用</st> `<st c="1937">http://localhost:8000/login</st>`<st c="1964">。我们发出的每个请求都使用 HTTP 协议作为其</st> <st c="2021">通信媒介。</st>
<st c="2042">尽管如此,HTTP 协议有其局限性。</st> <st c="2096">它以</st> **<st c="2113">无状态</st>** <st c="2122">方式运行,这意味着服务器在连续请求之间不保留任何信息(状态)。</st> <st c="2224">每次新的请求都会建立一个新的连接,消除对先前交互的任何了解。</st> <st c="2334">本质上,它缺乏对</st> <st c="2366">过去行为的记忆。</st>
<st c="2379">然而,当登录到应用程序后,后续请求会显示一个注销按钮,这表明应用程序能够识别用户并维护状态数据。</st> <st c="2548">此功能是通过 Django 会话实现的,它增强了 HTTP 协议的功能。</st>
## <st c="2656">Web 会话</st>
<st c="2669">一个</st> **<st c="2672">Web 会话</st>** <st c="2683">由</st> <st c="2693">在指定时间内访问者在网站上执行的一系列连续操作组成。</st> <st c="2790">每个框架都提供自己的方法来实现会话,以监控访问者的活动。</st> *<st c="2886">图 10</st>**<st c="2895">.1</st>* <st c="2897">说明了 Django 会话的工作原理。</st>

<st c="3299">图 10.1 – Django 会话操作的示例</st>
<st c="3353">让我们分析</st> <st c="3372">之前的场景:</st>
1. <st c="3390">用户导航到登录页面</st> <st c="3428">在</st> `<st c="3431">http://localhost:8000/login</st>`<st c="3458">。</st>
1. <st c="3459">然后 Django 向用户发送一个包含</st> <st c="3523">登录表单</st>的 HTTP 响应。</st>
1. <st c="3534">用户填写登录表单并点击</st> **<st c="3585">登录</st>** <st c="3590">按钮。</st> <st c="3599">Django 验证用户数据,如果准确无误,则为用户创建会话数据,并分配一个 ID 或</st> <st c="3713">一个</st> **<st c="3716">会话密钥</st>**<st c="3727">。默认情况下,会话数据存储在数据库中(我们将在下一节中看到其操作)。</st>
1. <st c="3831">Django 向用户的浏览器发送一个 cookie。</st> <st c="3877">这个 cookie 包含一个会话 ID,用于在后续请求中检索用户的会话数据。</st> <st c="3986">登录成功后,Django 将用户重定向到</st> <st c="4043">主页。</st>
1. <st c="4053">每个后续请求都会渲染带有</st> **<st c="4119">注销</st>** <st c="4125">按钮的导航菜单,因为会话 ID 包含在用户的请求中。</st> <st c="4188">这个过程会一直持续到用户点击</st> **<st c="4240">注销</st>** <st c="4246">按钮(这将删除会话数据和 cookie),或者直到会话过期,默认为</st> <st c="4351">两周。</st>
<st c="4361">现在我们已经学习了 Django 会话的工作原理,让我们用我们的电影</st> <st c="4448">商店项目来复制它。</st>
## <st c="4462">Django 登录场景</st>
<st c="4484">让我们按照以下步骤</st> <st c="4513">来查看 Django 会话的实际操作:</st>
1. <st c="4546">在隐身模式下运行应用程序,转到</st> `<st c="4592">http://localhost:8000/login</st>`<st c="4619">,并使用已注册用户的凭据进行</st> <st c="4678">登录。</st>
1. <st c="4685">转到</st> [<st c="4692">https://inloop.github.io/sqlite-viewer/</st>](https://inloop.github.io/sqlite-viewer/)<st c="4731">,将您的</st> `<st c="4752">db.sqlite3</st>` <st c="4762">文件拖放到页面上,然后选择</st> `<st c="4803">django_session</st>` <st c="4817">表。</st> <st c="4825">您将看到与刚刚登录的用户对应的新的</st> `<st c="4844">session_key</st>`<st c="4855">,</st> `<st c="4857">session_data</st>`<st c="4869">,和</st> `<st c="4875">expire_date</st>` <st c="4886">(</st>*<st c="4933">图 10</st>**<st c="4943">.2</st>*<st c="4945">)。</st>

<st c="5833">图 10.2 – django_session 表</st>
1. <st c="5871">之前的</st> <st c="5885">Django 会话数据包含一个 cookie。</st> <st c="5926">打开您的浏览器,确认您位于</st> **<st c="5980">电影商店</st>** <st c="5992">主页上,并打开开发者控制台。</st> <st c="6036">对于 Google Chrome,您可以通过</st> *<st c="6095">Shift</st>* <st c="6100">+</st> *<st c="6103">Ctrl</st>* <st c="6107">+</st> *<st c="6110">J</st>* <st c="6111">(在 Windows/Linux 上),或者</st> *<st c="6135">option</st>* <st c="6141">+</st> *<st c="6144">⌘</st>* <st c="6145">+</st> *<st c="6148">J</st>* <st c="6149">(在 macOS 上)来打开开发者控制台。</st>
1. <st c="6161">然后,导航到</st> `<st c="6221">http://127.0.0.1:8000</st>` <st c="6242">选项,您将看到存储的 cookie 数据,其中包括一个</st> `<st c="6309">sessionid</st>` <st c="6318">与数据库中存储的</st> `<st c="6341">session_key</st>` <st c="6352">相匹配(</st>*<st c="6377">图 10</st>**<st c="6387">.3</st>*<st c="6389">)。</st>

<st c="6786">图 10.3 – 电影商店 cookie 数据</st>
<st c="6828">这就是 Django 跟踪我们的网站交互的方式;如果你点击</st> **<st c="6897">注销</st>** <st c="6903">按钮,所有这些数据</st> <st c="6926">都将消失。</st>
<st c="6941">注意</st>
<st c="6946">会话数据不仅在登录场景中创建,也不只是为登录用户创建。</st> <st c="7042">如果你的应用程序在任何时候利用 Django 的会话功能,它也会生成相应的会话和 cookie 数据。</st> <st c="7183">我们将在实现购物车系统时看到这一点。</st> <st c="7260">有关</st> <st c="7296">Django 会话</st>的更多信息,请参阅:</st> [<st c="7318">https://docs.djangoproject.com/en/5.0/topics/http/sessions/</st>](https://docs.djangoproject.com/en/5.0/topics/http/sessions/)<st c="7377">。</st>
## <st c="7378">Django 会话</st>
**<st c="7394">Django 会话</st>** <st c="7410">是在 Web 应用程序中跨 HTTP 请求持久化数据的一种机制。</st> <st c="7490">它们允许 Django 在多个请求中为特定用户存储和检索任意数据。</st> <st c="7591">以下是关于</st> <st c="7622">Django 会话</st>的一些关键点:
+ **<st c="7638">客户端 cookies</st>**<st c="7658">:默认情况下,Django 会话是通过客户端 cookies 实现的。</st> <st c="7732">Django</st> <st c="7739">通常会在用户的浏览器中设置一个唯一的会话 ID,作为</st> <st c="7798">cookie。</st>
+ **<st c="7807">服务器端存储</st>**<st c="7827">:虽然会话 ID 存储在客户端的浏览器中,但实际的会话数据存储在服务器端。</st> <st c="7933">默认情况下,会话数据存储在</st> <st c="7975">数据库中。</st>
+ **<st c="7988">配置选项</st>**<st c="8010">:Django 为会话提供了各种配置选项,包括</st> <st c="8081">会话引擎(例如,数据库支持、缓存、基于文件),会话过期时间和会话数据的加密。</st>
+ **<st c="8197">与认证集成</st>**<st c="8229">:Django 会话通常与 Django 的认证</st> <st c="8288">系统一起工作。</st> <st c="8296">例如,当用户登录时,他们的认证状态通常会存储在会话中,使得 Django 能够在多个请求之间保持用户登录状态,直到他们明确退出或</st> <st c="8499">会话过期。</st>
+ `<st c="8602">request.session</st>` <st c="8617">属性。</st> <st c="8629">这允许它们在请求处理过程中根据需要读取、修改和删除会话数据。</st>
<st c="8723">现在我们已经学习了 Web 会话和 Django 会话的基础知识,让我们开始创建</st> <st c="8826">购物车应用</st>。
# <st c="8835">创建购物车应用</st>
<st c="8855">所有购物车</st> <st c="8878">功能将由它们自己的应用管理。</st> <st c="8936">因此,让我们创建一个购物车应用。</st> <st c="8965">导航到最顶部的</st> `<st c="8985">moviesstore</st>` <st c="8996">文件夹(包含</st> `<st c="9032">manage.py</st>` <st c="9041">文件的文件夹)并在终端中运行以下命令:</st>
+ <st c="9086">对于 macOS,运行以下命令:</st> <st c="9106">以下命令:</st>
```py
<st c="9124">python3 manage.py startapp cart</st>
```
+ <st c="9156">对于 Windows,运行以下命令:</st>
```py
<st c="9196">python manage.py startapp cart</st>
```
*<st c="9227">图 10</st>**<st c="9237">.4</st>* <st c="9239">显示了新的项目结构。</st> <st c="9273">请确认它与您当前的文件夹结构相匹配。</st>

<st c="9492">图 10.4 – 包含购物车应用的 MOVIESSTORE 项目结构</st>
## <st c="9563">在设置中添加购物车应用</st>
<st c="9591">记住</st> <st c="9600">对于每个新创建的应用,我们必须在</st> `<st c="9661">settings.py</st>` <st c="9672">文件中注册它。</st> <st c="9679">在</st> `<st c="9682">/moviesstore/settings.py</st>`<st c="9706">中,在</st> `<st c="9714">INSTALLED_APPS</st>`<st c="9728">下,添加以下加粗行:</st> <st c="9754">以下内容:</st>
```py
…
INSTALLED_APPS = [
…
'movies',
'accounts', <st c="9807">'cart',</st> ]
…
包括购物车 URL 文件到项目级别的 URL 文件中
<st c="9880">/moviesstore/urls.py</st>
…
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('home.urls')),
path('movies/', include('movies.urls')),
path('accounts/', include('accounts.urls')), <st c="10103">path('cart/', include('cart.urls')),</st> ]
…
<st c="10181">cart.urls</st> <st c="10211">cart/</st> <st c="10278">cart.urls</st>
将电影添加到购物车
-
配置 <st c="10503">add_to_cart</st>URL。 -
定义 <st c="10531">add_to_cart</st>函数。 -
更新 <st c="10559">movies.show</st>模板。
配置 add_to_cart URL
<st c="10621">/cart/</st><st c="10641">urls.py</st>
from django.urls import path
from . import views
urlpatterns = [
path('<int:id>/add/', views.add, name='cart.add'),
]
<st c="10916">cart/<int:id>/add</st> <st c="10987">/cart</st> <st c="11020"><int:id></st> <st c="11178">id</st><st c="11280">cart/1/add</st> <st c="11341">id=1</st>
定义 add_to_cart 函数
<st c="11392">/cart/views.py</st>
from django.shortcuts import render <st c="11477">from django.shortcuts import get_object_or_404, redirect</st>
<st c="11533">from movies.models import Movie</st>
<st c="11565">def add(request, id):</st>
<st c="11587">get_object_or_404(Movie, id=id)</st>
<st c="11619">cart = request.session.get('cart', {})</st>
<st c="11658">cart[id] = request.POST['quantity']</st>
<st c="11694">request.session['cart'] = cart</st>
<st c="11725">return redirect('home.index')</st>
-
我们导入 <st c="11803">redirect</st>和 <st c="11816">get_object_or_404</st>函数。 我们还从“ movies " 应用程序中导入<st c="11869">Movie</st>模型。 -
我们定义了一个 <st c="11914">add</st>函数,它接受两个参数:请求和 电影 ID。 -
我们从数据库中获取 <st c="11999">Movie</st>对象,使用给定的 <st c="12027">id</st>(通过使用 <st c="12062">get_object_or_404</st>函数)。 如果没有找到此类对象,将引发一个 404(未找到)错误。 -
我们检查会话存储中是否存在一个名为 <st c="12200">'cart'</st>的键。如果该键不存在,则将一个 <st c="12237">{}</st>空字典分配给 <st c="12276">cart</st>变量。 -
我们修改了 <st c="12305">cart</st>变量。 我们根据电影 ID 在 <st c="12344">cart</st>字典中添加一个新的键,并根据用户想要添加到购物车中的电影数量设置相应的值(我们将在稍后通过 HTML 表单收集 <st c="12490">quantity</st>)。 例如,如果用户想要添加 <st c="12566">2</st>部 <st c="12580">id=1</st>的电影,将添加一个新键/值,例如 <st c="12615">cart["1"] = "2"</st>到 `字典中。 -
更新后的 <st c="12676">购物车</st>字典随后使用 <st c="12732">request.session['cart'] =</st><st c="12758">cart</st>. -
在 <st c="12770">更新购物车后</st>,我们将用户重定向到主页(</st>home.index `)。
<st c="12865">movies.show</st> <st c="12921">购物车</st>
更新 movies.show 模板
<st c="12972">/movies/templates/movies/show.html</st> <st c="13017">行:</st>
…
<p><b>Description:</b> {{
template_data.movie.description }}</p>
<p>
<b>Price:</b> ${{ template_data.movie.price }}
</p> <st c="13168"><p class="card-text"></st>
<st c="13189"><form method="post"</st>
<st c="13209">action="{% url 'cart.add'</st>
<st c="13235">id=template_data.movie.id %}"></st>
<st c="13266"><div class="row"></st>
<st c="13284">{% csrf_token %}</st>
<st c="13301"><div class="col-auto"></st>
<st c="13324"><div class="input-group col-auto"></st>
<st c="13359"><div class="input-group-text">Quantity</st>
<st c="13398"></div></st>
**<st c="13405"><input type="number" min="1" max="10"</st>**
**<st c="13443">class="form-control quantity-input"</st>**
**<st c="13479">name="quantity" value="1"></st>**
**<st c="13506"></div></st>**
**<st c="13513"></div></st>**
**<st c="13520"><div class="col-auto"></st>**
**<st c="13543"><button class="btn bg-dark text-white"</st>**
**<st c="13582">type="submit">Add to cart</button></st>**
**<st c="13617"></div></st>**
**<st c="13624"></div></st>**
****<st c="13631"></form></st>**
**<st c="13639"></p></st>** <st c="13644">…</st>**
****<st c="13648">添加了一个新的表单,允许用户将电影添加到购物车。</st> <st c="13819">该表单链接到</st> <st c="13882">id</st> <st c="13884">作为表单操作的</st>
<st c="13958">http://localhost:8000/movies</st>

<st c="14383">现在,让我们继续列出添加到</st>
列出添加到购物车的电影
<st c="14541">步骤:</st>
-
配置购物车 <st c="14572">索引 URL。</st> -
定义一个 <st c="14592">utils</st>文件。 -
定义一个 <st c="14611">过滤器。</st> -
定义一个 <st c="14631">索引</st>函数。 -
创建 <st c="14660">cart.index</st>模板。 -
更新 <st c="14694">add_to_cart</st>`函数。 -
在 <st c="14737">基本模板</st>中添加链接。
配置购物车索引 URL
<st c="14782">/cart/urls.py</st><st c="14805">行来添加下一个路径:</st>
from django.urls import path
from . import views
urlpatterns = [ <st c="14909">path('', views.index, name='cart.index'),</st> path('<int:id>/add/', views.add, name='cart.add'),
]
<st c="15017">''</st> <st c="15080">/cart</st> <st c="15133">/cart</st> <st c="15165">index</st> <st c="15190">views</st> <st c="15229">index</st>
定义一个 utils 文件
<st c="15444">calculate_cart_total</st> <st c="15575">views</st> <st c="15593">models</st> <st c="15645">utils</st>
<st c="15720">/cart/</st><st c="15753">utils.py</st>
def calculate_cart_total(cart, movies_in_cart):
total = 0
for movie in movies_in_cart:
quantity = cart[str(movie.id)]
total += movie.price * int(quantity)
return total
-
我们定义了一个 <st c="16018">calculate_cart_total</st>函数,它接受两个参数: <st c="16077">cart</st>和 <st c="16086">movies_in_cart</st>。 <st c="16106">cart</st>参数是一个字典,表示用户的购物车。 请记住,键是表示电影 ID 的字符串,值是表示购物车中每部电影数量的字符串。 <st c="16324">movies_in_cart</st>参数是一个表示购物车中电影的 <st c="16362">Movie</st>对象的列表。 -
我们通过 <st c="16429">total</st>变量 初始化 <st c="16447">0</st>。 -
我们遍历购物车中的电影列表。 对于每部电影,我们提取添加到购物车中的对应数量,并将其乘以电影的价格。 然后,我们将该电影的总成本添加到 <st c="16660">total</st>变量中。 -
最后,我们返回 total 变量。
在总结中,<st c="16714">calculate_cart_total</st> <st c="16747">通过遍历购物车中的每部电影,将电影的价格乘以其数量,并</st> <st c="16900">计算总成本来计算用户购物车中电影的总额。</st> <st c="16928">这个函数将在后面的</st> <st c="16968">views</st> <st c="16973">文件中使用。</st>
定义一个过滤器
<st c="17153">|</st> <st c="17155">)字符是自定义模板数据展示的强大工具。
我们想要列出添加到购物车中的电影,并显示每部电影的数量。
在<st c="17849">/cart/</st> <st c="17853">中创建一个<st c="17870">templatetags</st> <st c="17882">文件夹。</st> <st c="17891">然后,在</st> <st c="17900">/cart/templatetags/</st> <st c="17919">创建一个名为</st> <st c="17945">cart_filters.py</st> <st c="17960">的新文件。</st> <st c="17987">目前,请用以下内容填充它:</st> <st c="17987">
from django import template
register = template.Library()
@register.filter(name='get_quantity')
def get_cart_quantity(cart, movie_id):
return cart[str(movie_id)]
让我们解释一下之前的代码:
-
我们导入
<st c="18196">template</st><st c="18211">模块,它提供了用于处理</st><st c="18219">Django 模板</st><st c="18270">的实用工具。</st> -
我们使用
<st c="18287">register = template.Library()</st><st c="18328">代码来创建一个template.Library `的实例,它用于注册自定义模板标签和过滤器。 -
我们使用
<st c="18436">@register.filter(name='get_quantity')</st><st c="18485">装饰器来注册</st>get_cart_quantity 函数作为一个名为 <st c="18573">get_quantity</st><st c="18585">的自定义模板过滤器</st>。<st c="18591">name='get_quantity'</st><st c="18610">参数指定了过滤器在模板中使用时的名称。</st><st c="18672"> -
我们定义了 <st c="18700">get_cart_quantity</st>函数,它接受两个参数: <st c="18759">cart</st>会话字典,以及所需数量的电影的 ID。 -
我们通过使用 <st c="18856">quantity</st>值,通过 <st c="18884">cart</st>字典和 <st c="18904">movie_id</st>作为键来访问。 我们将 <st c="18936">movie_id</st>转换为字符串以确保与 <st c="18990">购物车键</st>的兼容性。 -
最后,我们 返回相应的 <st c="19038">quantity</st>值。
<st c="19159">{{</st> <st c="19162">request.session.cart|get_quantity:movie.id }}</st>
<st c="19238">get_quantity</st> <st c="19272">request.session.cart</st> <st c="19302">movie.id</st>
定义一个索引函数
<st c="19621">/cart/views.py</st>
from django.shortcuts import render
from django.shortcuts import get_object_or_404, redirect
from movies.models import Movie <st c="19789">from .utils import calculate_cart_total</st>
<st c="19828">def index(request):</st>
<st c="19848">cart_total = 0</st>
<st c="19863">movies_in_cart = []</st>
<st c="19883">cart = request.session.get('cart', {})</st>
<st c="19922">movie_ids = list(cart.keys())</st>
<st c="19952">if (movie_ids != []):</st>
<st c="19974">movies_in_cart =</st>
<st c="19991">Movie.objects.filter(id__in=movie_ids)</st>
<st c="20030">cart_total = calculate_cart_total(cart,</st>
<st c="20070">movies_in_cart)</st>
<st c="20086">template_data = {}</st>
<st c="20105">template_data['title'] = 'Cart'</st>
<st c="20137">template_data['movies_in_cart'] = movies_in_cart</st>
<st c="20186">template_data['cart_total'] = cart_total</st>
<st c="20227">return render(request, 'cart/index.html',</st>
<st c="20269">{'template_data': template_data})</st> def add(request, id):
…
-
我们从 <st c="20375">utils</st>文件中导入 <st c="20414">calculate_cart_total</st>函数。 -
我们定义了 <st c="20440">index</st>函数。 -
我们将 <st c="20474">cart_total</st>初始化为 <st c="20488">0</st>,并将 <st c="20495">movies_in_cart</st>初始化为一个空列表。 -
我们使用 <st c="20584">request.session.get('cart', {})</st>从会话中检索购物车信息。 -
根据 <st c="20683">购物车键</st>,我们提取添加到购物车中的电影 ID。 -
如果购物车中有任何电影 ID,该函数将使用
<st c="20800">Movie.objects.filter(id__in=movie_ids)</st><st c="20838">查询数据库以获取具有这些 ID 的电影。此外,我们使用<st c="20918">calculate_cart_total</st><st c="20938">函数</st>计算购物车中电影的总额,该函数更新了cart_total 变量。 ` -
最后,我们准备
<st c="21012">template_data</st><st c="21025">字典并渲染<st c="21052">cart/index.html</st><st c="21067">模板。</st>
总结来说,<st c="21094">index</st> <st c="21099">函数</st> `
创建 cart.index 模板
在 <st c="21249">/cart/</st> <st c="21255">,创建一个 <st c="21264">templates</st> <st c="21275">文件夹。</st> 然后,在 <st c="21293">/cart/templates/</st> <st c="21309">,创建一个 <st c="21320">cart</st> <st c="21324">文件夹。</st>
现在,在 <st c="21341">/cart/templates/cart/</st><st c="21373">新文件</st>,<st c="21383">index.html</st><st c="21420">以下内容:</st>
{% extends 'base.html' %}
{% block content %}
{% load static %}
{% load cart_filters %}
<div class="p-3">
<div class="container">
<div class="row mt-3">
<div class="col mx-auto mb-3">
<h2>Shopping Cart</h2>
<hr />
</div>
</div>
<div class="row m-1">
<table class="table table-bordered table-striped
text-center">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Name</th>
<th scope="col">Price</th>
<th scope="col">Quantity</th>
</tr>
</thead>
<tbody>
{% for movie in template_data.movies_in_cart %}
<tr>
<td>{{ movie.id }}</td>
<td>{{ movie.name }}</td>
<td>${{ movie.price }}</td>
<td>{{
request.session.cart|get_quantity:movie.id }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="row">
<div class="text-end">
<a class="btn btn-outline-secondary mb-2"><b>Total
to pay:</b> ${{ template_data.cart_total }}</a>
</div>
</div>
</div>
</div>
{% endblock content %}
让我们解释一下之前的代码:
-
我们使用
<st c="22360">{% load cart_filters %}</st><st c="22383">标签</st>,它加载在<st c="22440">cart_filters</st><st c="22452">文件</st>中定义的自定义模板过滤器。在我们的例子中,它包括名为<st c="22501">get_quantity</st><st c="22513">的过滤器。</st> -
我们创建一个
<st c="22528">HTML 表格。</st> -
我们遍历购物车中的电影。对于每部电影,我们显示其
<st c="22614">id</st>、 name <st c="22622">、</st><st c="22624">price</st>、 和quantity 在购物车中的数量。 为了显示购物车中的数量,我们使用get_quantity 过滤器。 ` -
最后,我们
<st c="22744">显示</st><st c="22756">购物车总额</st>`的值。
更新 add_to_cart 函数
在 <st c="22811">/cart/views.py</st> `
…
def add_to_cart(request, id):
get_object_or_404(Movie, id=id)
cart = request.session.get('cart', {})
cart[id] = request.POST['quantity']
request.session['cart'] = cart
return redirect('<st c="23046">cart</st>.index')
如果用户将电影添加到购物车,他们现在将被重定向到 <st c="23131">购物车页面。</st>
在基础模板中添加链接
<st c="23235">/moviesstore/templates/base.html</st>
…
<a class="nav-link"
href="{% url 'home.about' %}">About</a>
<a class="nav-link"
href="{% url 'movies.index' %}">Movies</a> <st c="23449"><a class="nav-link"</st>
<st c="23468">href="{% url 'cart.index' %}">Cart</a></st> …
<st c="23553">http://localhost:8000/movies</st>

从购物车中删除电影
-
配置一个 <st c="24213">清晰的</st>URL。 -
定义一个 <st c="24235">清晰的</st>函数。 -
更新 <st c="24264">cart.index</st>模板。
配置清晰的 URL
<st c="24310">/cart/urls.py</st>
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='cart.index'),
path('<int:id>/add/', views.add, name='cart.add'), <st c="24540">path('clear/', views.clear, name='cart.clear'),</st> ]
<st c="24601">cart/clear/</st> <st c="24640">views</st> <st c="24645">clear</st> <st c="24704">clear</st>
定义清晰的函数
<st c="24753">/cart/views.py</st>
… <st c="24826">def clear(request):</st>
<st c="24845">request.session['cart'] = {}</st>
<st c="24874">return redirect('cart.index')</st>
更新 cart.index 模板
<st c="25103">/cart/templates/cart/index.html</st>
…
<div class="row">
<div class="text-end">
<a class="btn btn-outline-secondary mb-2"><b>Total
to pay:</b> ${{ template_data.cart_total }}</a> <st c="25315">{% if template_data.movies_in_cart|length > 0 %}</st>
<st c="25363"><a href="{% url 'cart.clear' %}"></st>
<st c="25397"><button class="btn btn-danger mb-2"></st>
<st c="25434">Remove all movies from Cart</st>
<st c="25462"></button></st>
<st c="25472"></a></st>
<st c="25477">{% endif %}</st> </div>
</div>
…
<st c="25517">if</st>
<st c="25698">http://localhost:8000/movies</st>

总结
<st c="26252">utils</st>
第十二章:11
实现订单和项目模型
-
分析 商店发票 -
创建 订单模型 -
创建 项目模型 -
回顾电影商店 类图
技术要求
分析商店发票

-
ID :用于唯一标识每个订单。 在先前的图中,它表示为 #1 。 -
日期 :标识订单完成的日期。 在先前的图中,它表示为 2024-04-22 。 -
总计 :标识订单的总金额。 在先前的图中,它表示为 $50 。 -
用户 :标识进行购买的用户。 在先前的图中,它表示为 1 - daniel 。
-
ID :用于唯一标识每个项目。 在先前的图中,第一件商品的 ID 表示为 1 。 -
数量 :指定用户想要购买的电影数量。 在先前的示例中,第一件商品的数量表示为 为 2 。 -
价格 :指定用户购买商品时的电影价格。 在先前的示例中,第一件商品的价格表示为 为 12 。 -
电影 :指定与项目链接的电影。 在先前的示例中,第一件商品的链接电影表示为 1 - Inception 。 -
订单 :指定与项目链接的订单。 在先前的示例中,第一件商品的链接订单表示为 #1 。
创建订单模型
-
创建订单模型。 -
应用迁移。 -
将订单模型添加到 <st c="3634">管理面板。</st>
<st c="3669">了解它们。</st>
创建订单模型
<st c="3737">Order</st> <st c="3782">cart app</st>
<st c="3795">/cart/models.py</st> <st c="3835">在</st> **<st c="3838">粗体</st>**<st c="3842">:</st>
from django.db import models <st c="3874">from django.contrib.auth.models import User</st>
<st c="3917">class Order(models.Model):</st>
<st c="3944">id = models.AutoField(primary_key=True)</st>
<st c="3984">total = models.IntegerField()</st>
<st c="4014">date = models.DateTimeField(auto_now_add=True)</st>
<st c="4061">user = models.ForeignKey(User,</st>
<st c="4092">on_delete=models.CASCADE)</st>
<st c="4118">def __str__(self):</st>
<st c="4137">return str(self.id) + ' - ' + self.user.username</st>
<st c="4205">之前的代码:</st>
-
我们从 Django 的 <st c="4234">User</st>模型中导入 <st c="4259">django.contrib.auth.models</st>模块。 -
我们定义了一个名为 <st c="4325">Order</st>的 Python 类,它继承自 <st c="4352">models.Model</st>。这意味着 <st c="4382">Order</st>是一个 Django <st c="4400">模型类。</st> -
在 <st c="4424">Order</st>类中,我们定义了 <st c="4447">几个字段:</st>-
<st c="4462">id</st>:这是一个 <st c="4479">AutoField</st>,它会自动为数据库中添加的每个新记录增加其值。 <st c="4578">primary_key=True</st>参数指定这个字段是表的键,唯一标识</st>每个记录。 ` -
<st c="4698">total</st>:这是一个 <st c="4717">IntegerField</st>,它表示订单的总金额。 它存储整数值。 -
<st c="4804">date</st>:这是一个 <st c="4822">DateTimeField</st>,它表示订单创建的日期和时间。 <st c="4900">auto_now_add=True</st>确保在创建订单时自动将日期和时间设置为当前日期和时间。 -
<st c="5026">user</st>:这是一个与 <st c="5076">User</st>模型的 <st c="5080">外键关系,它建立了订单和用户之间的多对一关系。</st> <st c="5159">这意味着每个订单都与单个用户相关联,每个用户可以有多个订单。</st>on_delete=models.CASCADE <st c="5282">指定如果相关用户被删除,相关的订单也将</st>被删除。 `
-
-
<st c="5373">__str__</st>是一个返回订单字符串表示的方法。 在这种情况下,它返回一个由订单 ID 和下订单用户的用户名组成的字符串。
应用迁移
-
对于 macOS 系统, 运行以下命令: <st c="5741">python3 manage.py makemigrations</st> <st c="5774">python3 manage.py migrate</st> -
对于 Windows 系统, 运行以下命令: <st c="5823">python manage.py makemigrations</st> <st c="5855">python manage.py migrate</st>

将订单模型添加到管理面板
<st c="6111">订单</st> <st c="6139">/cart/admin.py</st>
from django.contrib import admin <st c="6237">from .models import Order</st>
<st c="6363">/admin</st>. The order model will now appear (as shown in *<st c="6416">Figure 11</st>**<st c="6425">.3</st>*):

<st c="6824">Figure 11.3 – Admin page with orders available</st>
<st c="6870">Now that we have created and applied our</st> `<st c="6912">Order</st>` <st c="6917">model, let’s create the</st> `<st c="6942">Item</st>` <st c="6946">model to complete the information</st> <st c="6981">required to</st> <st c="6993">store purchases.</st>
<st c="7009">Creating the Item model</st>
<st c="7033">Let’s continue by</st> <st c="7051">creating an</st> `<st c="7064">Item</st>` <st c="7068">model and follow</st> <st c="7086">these steps:</st>
1. <st c="7098">Create the</st> <st c="7110">Item model.</st>
2. <st c="7121">Apply migrations.</st>
3. <st c="7139">Add the item model to the</st> <st c="7166">admin panel.</st>
<st c="7178">Creating the Item model</st>
<st c="7202">In</st> `<st c="7206">/cart/models.py</st>` <st c="7221">file, add the following</st> <st c="7246">in</st> *<st c="7249">bold</st>*<st c="7253">:</st>
从 django.db 导入 models
从 django.contrib.auth.models 导入 User
… <st c="7390">class Item(models.Model):</st>
<st c="7701">Let’s explain the</st> <st c="7720">previous code:</st>
* <st c="7734">We import the</st> `<st c="7749">Movie</st>` <st c="7754">model from the</st> `<st c="7770">movies</st>` <st c="7776">app.</st>
* <st c="7781">We define a Python class named</st> `<st c="7813">Item</st>`<st c="7817">, which inherits from</st> `<st c="7839">models.Model</st>`<st c="7851">. This means that</st> `<st c="7869">Item</st>` <st c="7873">is a Django</st> <st c="7886">model class.</st>
* <st c="7898">Inside the</st> `<st c="7910">Item</st>` <st c="7914">class, we</st> <st c="7925">define</st> <st c="7932">several fields:</st>
* `<st c="7947">id</st>`<st c="7950">: This is an</st> `<st c="7964">AutoField</st>`<st c="7973">, which automatically increments its value for each new record added to the database.</st> <st c="8059">The</st> `<st c="8063">primary_key=True</st>` <st c="8079">parameter specifies that this field is the primary key for the table, uniquely identifying</st> <st c="8171">each record.</st>
* `<st c="8183">price</st>`<st c="8189">: This is an</st> `<st c="8203">IntegerField</st>`<st c="8215">, which represents the price at which the item</st> <st c="8262">was purchased.</st>
* `<st c="8276">quantity</st>`<st c="8285">: This is an</st> `<st c="8299">IntegerField</st>`<st c="8311">, which represents the desired quantity of the item</st> <st c="8363">to purchase.</st>
* `<st c="8375">order</st>`<st c="8381">: This is a foreign key relationship with the</st> `<st c="8428">Order</st>` <st c="8433">model, which defines a foreign key relating each item to a</st> <st c="8493">specific order.</st>
* `<st c="8508">movie</st>`<st c="8514">: This is a foreign key relationship with the</st> `<st c="8561">Movie</st>` <st c="8566">model, which defines a foreign key relating each item to a</st> <st c="8626">specific movie.</st>
* `<st c="8641">__str__</st>` <st c="8649">is a method that returns a string representation of the item.</st> <st c="8712">In this case, it returns a string composed of the item ID and the name of the</st> <st c="8790">associated movie.</st>
## <st c="8807">Applying migrations</st>
<st c="8827">Now that we have created the</st> `<st c="8857">Item</st>` <st c="8861">model, let’s update our database by running the following commands</st> <st c="8928">based on your</st> <st c="8943">operating system.</st>
* <st c="8960">For macOS,</st> <st c="8972">run this:</st>
```
<st c="8981">python3 manage.py makemigrations</st>
<st c="9014">python3 manage.py migrate</st>
```py
* <st c="9040">For Windows,</st> <st c="9054">run this:</st>
```
<st c="9063">python manage.py makemigrations</st>
<st c="9095">python manage.py migrate</st>
```py
<st c="9120">Now, you should see something</st> <st c="9151">like this:</st>

<st c="9283">Figure 11.4 – Applying the item migration</st>
## <st c="9324">Adding the item model to the admin panel</st>
<st c="9365">To add the</st> `<st c="9377">Item</st>` <st c="9381">model to admin, go to</st> `<st c="9404">/cart/admin.py</st>` <st c="9418">and register it by adding the</st> <st c="9448">following</st> <st c="9459">in</st> *<st c="9462">bold</st>*<st c="9466">:</st>
从 django.contrib 导入 admin
从 .models 导入 Order

<st c="10038">图 11.5 – 可用项目的管理页面</st>
现在我们已经完成了进行购买所需的数据结构。</st> <st c="10115">在继续购买流程之前,让我们回顾一下我们的模型与项目</st> <st c="10153">类图之间的关系。</st>
<st c="10267">回顾电影商店类图</st>
<st c="10308">我们在</st> *<st c="10367">第一章</st>* <st c="10376">中设计的电影商店类图是设计电影商店代码的蓝图。</st> <st c="10417">我们已经实现了完成项目代码所需的所有模型。</st> <st c="10443">因此,让我们快速回顾一下模型与类之间的这种关系。</st> <st c="10525">和类。</st>
*<st c="10594">图 11</st>**<st c="10604">.6</st>* `<st c="10606">显示了类图,突出了我们实现相应</st>` `<st c="10698">Django 模型</st>` 的位置:</st>

`<st c="11118">图 11.6 – 电影商店类图,突出模型位置</st>`
`<st c="11188">让我们分析一下</st>` `<st c="11207">前面的图:</st>`
+ `<st c="11223">《</st>` `<st c="11228">电影</st>` `<st c="11233">和</st>` `<st c="11238">评论</st>` `<st c="11244">模型在</st>` `<st c="11280">movies</st>` `<st c="11286">应用</st>` 中实现。</st>
+ `<st c="11291">《</st>` `<st c="11296">订单</st>` `<st c="11301">和</st>` `<st c="11306">项目</st>` `<st c="11310">模型在</st>` `<st c="11346">购物车</st>` `<st c="11350">应用</st>` 中实现。</st>
+ `<st c="11355">《</st>` `<st c="11360">用户</st>` `<st c="11364">模型尚未实现。</st>` `<st c="11378">相反,我们利用了位于</st>` `<st c="11472">admin.contrib.auth</st>` `<st c="11490">应用</st>` 中的 Django 内置模型。</st>
`<st c="11495">最后,让我们回顾一下特定类与模型之间的关系(</st>`*<st c="11559">图 11</st>**<st c="11569">.7</st>*<st c="11571">):</st>`
+ `<st c="11582">评论</st>` `<st c="11588">类名变成了一个</st>` `<st c="11609">评论</st>` `<st c="11615">Python 类。</st>` `<st c="11630">我们继承自</st>` `<st c="11648">models.Model</st>` `<st c="11660">来定义它为一个 Django</st>` `<st c="11686">模型类。</st>`
+ `<st c="11706">id</st>` `<st c="11708">,</st>` `<st c="11710">comment</st>` `<st c="11717">, 和</st>` `<st c="11723">date</st>` `<st c="11727">类属性变成了 Python 类属性。</st>` `<st c="11777">我们利用了</st>` `<st c="11793">models</st>` `<st c="11799">模块来利用类似于在类图中定义的字段类型。</st>`
+ `<st c="11919">评论</st>` `<st c="11925">和</st>` `<st c="11930">电影</st>` `<st c="11935">类变成了 Python 类属性。</st>` `<st c="11977">我们利用了</st>` `<st c="11993">models.ForeignKey</st>` `<st c="12010">方法来定义两个模型之间的外键关系</st>` `<st c="12067">。</st>`
+ `<st c="12111">评论</st>` `<st c="12117">和</st>` `<st c="12122">用户</st>` `<st c="12126">类变成了 Python 类属性。</st>` `<st c="12168">我们利用了</st>` `<st c="12184">models.ForeignKey</st>` `<st c="12201">方法来定义两个模型之间的外键关系</st>` `<st c="12246">。</st>`

`<st c="12531">图 11.7 – 类与模型之间的关系</st>`
`<st c="12585">我们已经完成了类图和 Django 模型之间的所有连接。</st>` `<st c="12673">现在,我们准备启用用户进行购买。</st>`
`<st c="12725">总结</st>`
<st c="12733">在本章中,我们学习了简单发票的工作原理。</st> <st c="12788">我们创建了一些模型(</st>`<st c="12819">订单</st>` <st c="12825">和</st> `<st c="12830">项目</st>`<st c="12834">)。</st> <st c="12838">这些模型将使我们能够存储有关用户购买信息。</st> <st c="12914">我们回顾了创建 Django 模型和应用迁移的过程。</st> <st c="12994">最后,我们回顾了类图如何作为创建项目模型的蓝图。</st> <st c="13089">在下一章中,我们将实现购买功能,并允许用户查看</st> <st c="13175">他们的订单。</st>
第十三章:12
实现购买和订单页面
-
创建 购买页面 -
创建 订单页面 -
回顾电影商店 MVT 架构
技术要求
code.visualstudio.com/
创建购买页面
-
配置购买 URL。 -
定义 <st c="1422">购买</st>函数。 -
更新 <st c="1454">购物车索引</st>模板。 -
创建 <st c="1488">购物车购买</st>模板。
配置购买 URL
<st c="1544">/cart/urls.py</st>
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='cart.index'),
path('<int:id>/add/', views.add, name='cart.add'),
path('clear/', views.clear, name='cart.clear'), <st c="1802">path('purchase/', views.purchase,</st>
<st c="1835">name='cart.purchase'),</st> ]
<st c="1873">cart/purchase/</st> <st c="1915">purchase</st> <st c="1948">purchase</st> <st c="1982">purchase</st>
定义购买函数
<st c="2041">/cart/views.py</st>
…
from movies.models import Movie
from .utils import calculate_cart_total <st c="2172">from .models import Order, Item</st>
<st c="2203">from django.contrib.auth.decorators import login_required</st> … <st c="2263">@login_required</st>
<st c="2278">def purchase(request):</st>
<st c="2301">cart = request.session.get('cart', {})</st>
<st c="2340">movie_ids = list(cart.keys())</st>
<st c="2370">if (movie_ids == []):</st>
<st c="2392">return redirect('cart.index')</st>
<st c="2422">movies_in_cart = Movie.objects.filter(id__in=movie_ids)</st>
<st c="2478">cart_total = calculate_cart_total(cart, movies_in_cart)</st>
<st c="2534">order = Order()</st>
<st c="2550">order.user = request.user</st>
<st c="2576">order.total = cart_total</st>
<st c="2601">order.save()</st>
<st c="2614">for movie in movies_in_cart:</st>
<st c="2643">item = Item()</st>
<st c="2657">item.movie = movie</st>
<st c="2676">item.price = movie.price</st>
<st c="2701">item.order = order</st>
<st c="2720">item.quantity = cart[str(movie.id)]</st>
**<st c="2756">item.save()</st>**
**<st c="2768">request.session['cart'] = {}</st>**
**<st c="2797">template_data = {}</st>**
**<st c="2816">template_data['title'] = 'Purchase confirmation'</st>**
**<st c="2865">template_data['order_id'] = order.id</st>**
**<st c="2902">return render(request, 'cart/purchase.html',</st>**
**<st c="2947">{'template_data': template_data})</st>**
**
-
<st c="3116">from .models import</st><st c="3137">Order, Item</st><st c="3148">from django.contrib.auth.decorators</st><st c="3185">import login_required</st>让我们分析这段 代码: -
我们从当前 <st c="3255">Order</st>和 <st c="3265">Item</st>模型从当前 <st c="3294">app 目录</st>导入。 -
我们导入 <st c="3323">login_required</st>装饰器。
-
-
<st c="3348">@</st>``<st c="3350">login_required</st><st c="3364">def purchase(request):</st><st c="3387">cart =</st><st c="3395">request.session.get('cart', {})</st><st c="3426">movie_ids =</st><st c="3439">list(cart.keys())</st><st c="3456">if (movie_ids == []):</st>**<st c="3478">return redirect('cart.index')</st>**让我们分析这段 代码: -
我们使用 <st c="3554">login_required</st>装饰器来确保用户必须登录才能访问 <st c="3635">purchase</st>函数。 -
我们定义了 <st c="3668">purchase</st>函数,该函数将处理购买过程。 -
我们从用户的会话中检索购物车数据。 <st c="3782">cart</st>变量将包含一个字典,其中以电影 ID 为键,数量为值。 -
我们从 <st c="3911">cart</st>字典中检索存储的电影 ID 并将它们转换为名为 <st c="3956">movie_ids</st>的列表。 -
我们检查 <st c="3983">movie_ids</st>列表是否为空(这表示购物车为空)。 在这种情况下,用户将被重定向到 <st c="4088">cart.index</st>页面(在这里, <st c="4115">purchase</st>函数完成其执行)。
-
*** <st c="4158">movies_in_cart =</st> <st c="4176">Movie.objects.filter(id__in=movie_ids)</st>
`<st c="4214">cart_total =</st>` `<st c="4228">calculate_cart_total(cart, movies_in_cart)</st>`
`<st c="4270">order =</st>` `<st c="4279">Order()</st>`
`<st c="4286">order.user =</st>` `<st c="4300">request.user</st>`
`<st c="4312">order.total =</st>` `<st c="4327">cart_total</st>`
`**<st c="4337">order.save()</st>**`
**`<st c="4350">for movie</st>` `<st c="4361">in movies_in_cart:</st>`**
**`<st c="4379">item =</st>` `<st c="4387">Item()</st>`**
**`<st c="4393">item.movie =</st>` `<st c="4407">movie</st>`**
**`<st c="4412">item.price =</st>` `<st c="4426">movie.price</st>`**
**`<st c="4437">item.order =</st>` `<st c="4451">order</st>`**
**`<st c="4456">item.quantity =</st>` `<st c="4473">cart[str(movie.id)]</st>`**
**`**<st c="4492">item.save()</st>**`**
****<st c="4504">让我们分析这段</st> <st c="4530">代码:</st>****
+ ****<st c="4538">如果购物车不为空,我们</st> <st c="4567">继续购买流程。</st>****
+ ****<st c="4598">根据购物车中存储的 ID 从数据库中检索电影对象</st> <st c="4679">使用</st> `<st c="4685">Movie.objects.filter(id__in=movie_ids</st>`<st c="4722">.</st>****
+ ****<st c="4723">我们使用</st> `<st c="4788">calculate_cart_total()</st>` <st c="4810">函数计算购物车中电影的总额。</st>****
+ ****<st c="4820">我们创建一个新的</st> `<st c="4837">Order</st>` <st c="4842">对象。</st> <st c="4851">我们设置其属性,例如</st> `<st c="4881">user</st>` <st c="4885">(登录用户) 和</st> `<st c="4911">total</st>` <st c="4916">(购物车总额),并将其保存到</st> <st c="4950">数据库中。</st>****
+ ****<st c="4963">我们遍历购物车中的电影。</st> <st c="5004">对于购物车中的每部电影,我们创建一个</st> `<st c="5017">Item</st>` <st c="5021">对象。</st> <st c="5057">对于每个</st> `<st c="5066">Item</st>`<st c="5070">,我们设置其</st> `<st c="5083">价格</st>` <st c="5088">和</st> `<st c="5093">数量</st>`<st c="5101">,链接相应的</st> `<st c="5126">电影</st>` <st c="5131">和</st> `<st c="5136">订单</st>`<st c="5141">,并将其保存到</st> <st c="5158">数据库中。</st>********* `<st c="5171">request.session['cart'] = {}</st>`
`<st c="5200">template_data = {}</st>`
`<st c="5219">template_data['title'] = '</st>``<st c="5246">购买确认'</st>`
`<st c="5269">template_data['order_id'] =</st>` `<st c="5298">order.id</st>`
`<st c="5306">return render(request, 'cart/purchase.html', {'</st>``<st c="5354">template_data': template_data})</st>`
<st c="5386">让我们分析这段</st> <st c="5412">代码:</st>
+ <st c="5420">购买完成后,我们通过将</st> `<st c="5505">request.session['cart']</st>` <st c="5528">设置为空字典来清除用户会话中的购物车。</st>
+ <st c="5552">我们准备要发送到购买确认模板的数据。</st> <st c="5579">这些数据包括页面标题和创建的订单 ID。</st>
+ <st c="5696">最后,我们渲染</st> `<st c="5720">cart/purchase.html</st>` <st c="5738">模板。</st>******
******
更新购物车.index 模板
<st c="5886">/cart/templates/cart/index.html</st>
…
<a class="btn btn-outline-secondary mb-2">
<b>Total to pay:</b> ${{ template_data.cart_total
}}</a>
{% if template_data.movies_in_cart|length > 0 %} <st c="6108"><a href="{% url 'cart.purchase' %}"</st>
<st c="6143">class="btn bg-dark text-white mb-2">Purchase</st><st c="6188"></a></st> <a href="{% url 'cart.clear' %}">
<button class="btn btn-danger mb-2">
Remove all movies from Cart
</button>
</a>
{% endif %}
…
创建购物车购买模板
<st c="6521">/cart/templates/cart/</st><st c="6563">purchase.html</st>
{% extends 'base.html' %}
{% block content %}
<div class="p-3">
<div class="container">
<div class="row mt-3">
<div class="col mx-auto mb-3">
<h2>Purchase Completed</h2>
<hr />
<p>Congratulations, purchase completed. Order
number is: <b>#{{ template_data.order_id }}</b>
</p>
</div>
</div>
</div>
</div>
{% endblock content %}
<st c="6993">base.html</st>
<st c="7158">http://localhost:8000/movies</st>


创建订单页面
<st c="8213">以下步骤:</st>
-
配置 <st c="8277">订单 URL。</st> -
定义 <st c="8302">订单</st>函数。 -
创建 <st c="8332">accounts.orders</st>模板。 -
在基础模板中添加链接。
配置订单 URL
<st c="8442">特定用户</st>。 <st c="8523">accounts</st> <st c="8540">/accounts/urls.py</st>
from django.urls import path
from . import views
urlpatterns = [
path('signup', views.signup, name='accounts.signup'),
path('login/', views.login, name='accounts.login'),
path('logout/', views.logout, name='accounts.logout'), <st c="8813">path('orders/', views.orders, name='accounts.orders'),</st> ]
<st c="8883">accounts/orders/</st> <st c="8960">views</st> <st c="8929">orders</st> <st c="8994">orders</st>
定义订单函数
在 <st c="9049">/accounts/views.py</st><st c="9076">行</st>
…
from django.shortcuts import redirect
from django.contrib.auth.decorators import login_required <st c="9200">from django.contrib.auth.models import User</st> … <st c="9245">@login_required</st>
<st c="9260">def orders(request):</st>
**<st c="9281">template_data = {}</st>**
**<st c="9300">template_data['title'] = 'Orders'</st>**
**<st c="9334">template_data['orders'] = request.user.order_set.all()</st>**
**<st c="9389">return render(request, 'accounts/orders.html',</st>**
**<st c="9436">{'template_data': template_data})</st>**
**<st c="9489">之前的代码:</st>
-
我们从 Django 的 <st c="9518">用户</st>模型 中导入。 <st c="9543">认证系统</st>。 -
我们使用 <st c="9577">login_required</st>装饰器来确保用户必须登录才能访问 <st c="9658">orders</st>函数。 -
我们定义了 <st c="9689">orders</st>函数,它 <st c="9711">接受</st><st c="9720">request</st>对象作为 <st c="9738">参数</st>。 -
我们定义了 <st c="9765">template_data</st>变量并将其 分配 <st c="9804">标题</st>。 -
我们检索属于当前登录用户的全部订单(
<st c="9877">request.user</st>)。 order_set属性用于通过其关系访问与用户相关联的相关订单(你可以在这里了解更多关于此类关系的信息 https://docs.djangoproject.com/en/5.0/topics/db/examples/many_to_one/ )。 记住, User外键 关系存在于 User模型和 Order模型之间。 -
最后,我们将订单传递给模板并渲染它。
创建accounts.orders模板
现在,在/accounts/templates/accounts/</st><st c="10371">新文件</st> <st c="10381">orders.html</st>
{% extends 'base.html' %}
{% block content %}
<div class="p-3">
<div class="container">
<div class="row mt-3">
<div class="col mx-auto mb-3">
<h2>My Orders</h2>
<hr />
{% for order in template_data.orders %}
<div class="card mb-4">
<div class="card-header">
Order #{{ order.id }}
</div>
<div class="card-body">
<b>Date:</b> {{ order.date }}<br />
<b>Total:</b> ${{ order.total }}<br />
<table class="table table-bordered
table-striped text-center mt-3">
<thead>
<tr>
<th scope="col">Item ID</th>
<th scope="col">Movie</th>
<th scope="col">Price</th>
<th scope="col">Quantity</th>
</tr>
</thead>
<tbody>
{% for item in order.item_set.all %}
<tr>
<td>{{ item.movie.id }}</td>
<td>
<a class="link-dark"
href="{% url 'movies.show'
id=item.movie.id %}">
{{ item.movie.name }}
</a>
</td>
<td>${{ item.movie.price }}</td>
<td>{{ item.quantity }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
{% endblock content %}
让我们解释一下之前的代码:
-
我们扩展了
<st c="11437">base.html</st>模板。 -
我们遍历存储在
<st c="11501">template_data.orders</st>中的每个订单对象。对于每个订单,我们显示其 <st c="11554">日期</st>和 <st c="11563">总计</st>。 -
然后,我们遍历当前订单中的每个项目。
The <st c="11639">order.item_set.all</st>检索与当前订单相关联的所有相关项目。 对于这些项目中的每一个,我们显示其 <st c="11761">价格</st>和 <st c="11771">数量</st>,以及相应的电影 <st c="11808">id</st>和名称。
在基础模板中添加链接
让我们在<st c="11908">/moviesstore/templates/base.html</st>
…
{% if user.is_authenticated %} <st c="12031"><a class="nav-link"</st>
<st c="12050">href="{% url 'accounts.orders' %}">Orders</st><st c="12092"></a></st> <a class="nav-link"
href="{% url 'accounts.logout' %}">Logout
({{ user.username }})
</a>
{% else %}
<a class="nav-link"
href="{% url 'accounts.login' %}">Login
</a>
<a class="nav-link"
href="{% url 'accounts.signup' %}">Sign Up
</a>
{% endif %}
…
现在,保存这些文件,运行服务器,并转到

图 12.3 – 订单页面
回顾电影商店 MVT 架构
<st c="13509">accounts</st><st c="13520">cart</st><st c="13526">home</st><st c="13536">movies</st>


-
我们实现了一个名为 <st c="14519">moviesstore</st>的项目级文件夹。这个文件夹包含了项目级的 URL 文件,它与应用程序级的 URL 文件相连接。 -
我们实现了四个 Django 应用程序。 对于这些应用程序中的每一个,我们都展示了三个主要层(模型、视图和模板)之间的通信。 -
我们学习了如何将代码分布在多个应用程序中以提高可维护性和 分离职责。 -
我们通过为我们的电影商店项目实现一系列功能来实践那些文件和层的实现。 Store project.
摘要
<st c="15276">订单</st> <st c="15286">商品</st>
第十四章:13
将应用程序部署到云端
-
管理 GitHub 和 Git -
将代码克隆到 PythonAnywhere -
配置 虚拟环境 -
设置您的 Web 应用 -
配置 静态文件
技术要求
管理 GitHub 和 Git
-
理解 Git 和 GitHub。 -
创建一个 GitHub 仓库。 -
将我们的代码 上传到 GitHub。
理解 Git 和 GitHub
创建 GitHub 仓库
前往 https://github.com/ ,如果你还没有账户,请注册一个。 然后,通过点击右上角的 + 创建一个新的仓库,并选择 新建仓库 ( 图 13 **.1 ):

给你的仓库起一个名字,例如 <st c="2905">moviesstore</st>。选择 公共 单选按钮,然后点击 创建仓库 ( 图 13 **.2 ):

将我们的代码上传到 GitHub
git
<st c="3942">git</st>

-
在顶部 <st c="4894">moviesstore</st>文件夹(包含 <st c="4940">manage.py</st>文件的文件夹)中打开你的终端。 然后,运行以下命令: 以下命令: <st c="4989">git init</st>之前的命令将你的文件夹标记为 Git 项目,允许你开始跟踪更改。 在项目目录中添加了一个名为 .git 的隐藏文件夹。 此文件夹存储了 Git 需要跟踪更改和管理项目的所有元数据、配置文件和元素。 -
接下来,运行以下命令: 以下命令: <st c="5317">git add .</st>之前的命令将我们项目中的所有内容(文件夹、子文件夹和文件)添加到暂存区,为下一次提交做准备。 -
然后,继续提交之前的更改: 以下更改: <st c="5531">git commit -m "first version"</st>之前的命令用于记录我们对暂存区所做的更改和包含内容。 当你运行 git commit 时,你实际上是在创建项目当前状态的快照。 你可以通过你提供的描述性消息来识别不同的提交。 -
接下来,运行以下命令: 以下命令: <st c="6076">git remote add origin <your-origin-path></st> command (*<st c="6126">Figure 13</st>**<st c="6136">.4</st>*) and run it in the Terminal (remember to replace <st c="6189"><your-origin-path></st> with yours):

<st c="6525">git remote add origin <your-origin-path></st>
-
要将代码从你的本地计算机移动到 GitHub,运行以下命令: 以下命令: <st c="6871">git push -u origin main</st>如果上传成功,你应该会看到类似这样的消息( 图 13 .5):


在 PythonAnywhere 上克隆您的代码
-
前往 https://www.pythonanywhere.com/registration/register/beginner/ 并注册一个免费的初学者账户,如果您还没有的话。 -
然后,点击 仪表板 | 新建控制台 | $ Bash ( 图 13.7 **.7 ):

上一步将打开一个 Bash 控制台。 回到您的 GitHub 仓库,点击 代码 并复制克隆的 URL( 图 13 **.8 ):

-
要克隆之前的 仓库,请返回 PythonAnywhere Bash 控制台并运行以下命令(将 <st c="11178"><repo-url></st>部分替换为您的,例如, <st c="11219">git</st><st c="11223">clone</st>https://github.com/danielgara/moviesstore.git ): <st c="11461">ls</st> command in Bash, and you will see a folder with the repository name containing the repository code (refer to Figure 13.9).

配置虚拟环境
-
在 PythonAnywhere Bash 控制台中创建虚拟环境,我们需要执行类似以下命令: <st c="12928">mkvirtualenv -p python3.10 <environment-name></st>。目前,我们将用 <st c="13000"><environment-name></st>替换为 <st c="13024">moviesstoreenv</st>并运行以下命令: <st c="13061">mkvirtualenv -p python3.10 moviesstoreenv</st>我们将在 Bash 中看到虚拟 env 的名称,例如,(moviesstoreenv)。 这意味着我们处于虚拟环境中( 图 13 .10):

-
回到我们的虚拟 env 中,我们需要安装 <st c="14270">django</st>和 <st c="14281">pillow</st>(就像我们在开发中做的那样)。 因此,运行以下命令: 以下命令: <st c="14338">pip install django==5.0 pillow</st>之前的执行可能需要几分钟到十分钟。 PythonAnywhere 拥有非常快的互联网,但文件系统访问可能较慢,Django 在安装过程中会创建大量的 小文件 。幸运的是,您只需做一次。 一旦完成,您应该会看到一个类似于图 13 .11 中所示的消息:

设置您的 Web 应用
-
您的 Django 项目顶级文件夹的路径(包含 <st c="15879">manage.py</st>文件的文件夹)。 对于这个项目,它通常是 <st c="15946">/home</st>和 <st c="15956">/<pythonanywhere-user></st>以及 <st c="15983">/<github-repo-name></st>的组合。在我们的例子中,它 是 <st c="16024">/home/danielgara/moviesstore</st>。 -
您主项目文件夹的名称(即包含您的 <st c="16141">settings.py</st>文件的文件夹名称)。 在我们的例子中,它 是 <st c="16179">moviesstore</st>。 -
您的虚拟环境名称。 在我们的例子中,它 是 <st c="16239">moviesstoreenv</st>。
在您的浏览器中,打开一个新标签页并转到 PythonAnywhere 仪表板。 然后,点击 Web 标签页并点击 添加新的 Web 应用 ( 图 13 **.12 ):

PythonAnywhere 将要求您提供 您的 Web 应用的域名 。只需点击 下一步 ( *图 13 .13 *):

在 选择 Python Web 框架 部分,选择 手动配置 ( 图 13 **.14 ):

确保你选择
选择正确的 Python 版本(与您创建虚拟环境时使用的版本相同)。 在我们的例子中,它是 <st c="17732">Python 3.10</st>( 图 13 **.15 )。 最后,当被要求选择 手动配置 时,点击 下一步 ( 图 13 **.16 )。


一旦创建 Web 应用,你需要在 <st c="18621">moviesstoreenv</st>中输入你的虚拟环境名称,它将自动完成其完整路径 在 <st c="18689">/home/username/.virtualenvs/moviesstoreenv</st>:

接下来,在 代码部分 中输入你的用户文件夹路径( <st c="19097">/home/<your-username>/</st>),用于 源代码 和 工作目录 ( 图 13 **.18 ):

点击 <st c="19395">wsgi.py</st>文件,位于 代码部分 内,而不是你本地 Django 项目文件夹中的那个( 图 13 **.19 ):

-
删除除 Django 部分之外的所有内容,并取消注释该部分。 您的 WSGI 文件将类似于以下内容: 以下: # +++++++++++ DJANGO +++++++++++ # To use your own django app use code like this: import os import sys path = '<st c="20013">/home/danielgara/moviesstore</st>' if path not in sys.path: sys.path.append(path) os.environ['DJANGO_SETTINGS_MODULE'] = '<st c="20132">moviesstore.settings</st>' from django.core.wsgi import get_wsgi_application application = get_wsgi_application() path = '<st c="20423">DJANGO_SETTINGS_MODULE</st> (where the <st c="20457">settings.py</st> file is located):os.environ['DJANGO_SETTINGS_MODULE'] = '<st c="20619">settings.py</st>. Go to the PythonAnywhere <st c="20733">settings.py</st> file (*<st c="20751">Figure 13</st>**<st c="20761">.20</st>*):

-
点击 <st c="21465">settings.py</st>文件。 在 <st c="21486">settings.py</st>中,修改 <st c="21510">ALLOWED_HOSTS</st>变量: … # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [<st c="21631">'*'</st>] …保存 文件。 -
然后,转到 Web 选项卡,并 点击 Reload 按钮以刷新您的域名( 图 13 **.21 ):

<st c="21926">ALLOWED_HOSTS</st> <st c="22096">*</st>
转到您的项目 URL(例如,在之前的屏幕截图中的蓝色链接,例如, <st c="22321">danielgara.pythonanywhere.com</st>),主页现在应该出现( 图 13 **.22 ):

配置静态文件
-
在 PythonAnywhere 中,返回到 <st c="22921">settings.py</st>文件。 我们需要在 粗体 中添加以下内容: … STATIC_URL = 'static/' <st c="23002">STATIC_ROOT = os.path.join(BASE_DIR, 'static')</st> # Default primary key field type # https://docs.djangoproject.com/en/4.0/ref/settings/ #default-auto-field DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' MEDIA_ROOT = os.path.join(BASE_DIR,'media') MEDIA_URL = '/media/' …以下 <st c="23281">STATIC_ROOT</st>变量定义了一个中央位置,我们将所有 静态文件 收集到该位置。 -
在 PythonAnywhere 中,转到 的 控制台 选项卡,并点击您的 Bash 控制台 。然后,通过执行以下命令连接到您的虚拟环境: <st c="23905">settings.py</st> and copies them into <st c="23938">STATIC_ROOT</st>:

接下来,设置静态文件映射,以便我们的 web 服务器为您提供服务。 在 <st c="24597">/static/</st>. 在 <st c="24650">static/</st>, 例如, <st c="24672">/home/danielgara/moviesstore/static/</st>( 图 13 **.24 ):

然后,在 Web 选项卡中,点击 重新加载 ,打开您的网站,现在您的静态图像应该会出现( 图 13 **.25 ):

总结


浙公网安备 33010602011771号