Django学习笔记

301 阅读48分钟

第1章 Django 建站基础

1.1 网站的定义与组成

1.目前大多数网站由域名、空间服务器、DNS域名解析、网站程序和数据组成

2.常见的域名(Domain Name):

  • .com:商业性质的机构或公司
  • .NET:从事Internet相关的网络服务的机构或公司
  • .ORG:非营利的组织、团体
  • .GOV:政府部门
  • .CN:中国国内域名
  • .COM.CN:中国从事Internet相关的网络服务的机构或公司
  • .ORG.CN:中国非营利的组织、团体
  • .GOV.CN:中国政府部门

3.空间服务器主要有虚拟机、独立服务器和VPS

1.2网站的分类

1.资讯门户类网站(以提供信息资讯为主要目的):

  • 承载的信息类型,例如是否承载多媒体信息、是否承载结构化信息等
  • 信息发布的方式和流程
  • 信息量的数量级
  • 网站用户管理

2.企业品牌网站的分类:

  • 企业形象网站:塑造企业形象、传播企业文化、推介企业业务、报道企业活动和展示企业实力
  • 品牌形象网站:当企业拥有众多品牌且不同品牌之间的市场定位和营销策略各不相同时,企业可根据不同品牌建立其品牌网站,以针对不同的消费群体
  • 产品形象网站:针对某一产品的网站,重点在于产品的体验

3.交易类网站的分类:

  • B2C(Business To Consumer)网站:商家—消费者,主要是购物网站,用于商家和消费者之间的买卖,如传统的百货商店和购物广场等
  • B2B(Business To Business)网站:商家—商家,主要是商务网站,用于商家之间的买卖,如传统的原材料市场和大型批发市场等
  • C2C(Consumer To Consumer)网站:消费者—消费者,主要以拍卖网站为主,用于个人物品的买卖,如传统的旧货市场

1.3网站运行原理

1.常用术语

  • 客户端:在计算机上运行并连接到互联网的应用程序,简称浏览器,如Chrome、Firefox。用户通过操作客户端实现网站和用户之间的数据交互
  • 服务器:能连接到互联网且具有IP地址的计算机。服务器主要接收和处理用户的请求信息。
  • IP地址:互联网地址协议,TCP/IP网络设备的数字标识符。
  • 域名:用于标识一个或多个IP地址
  • DNS:域名系统,用于跟踪计算机的域名及其在互联网上相应的IP地址
  • TCP/IP:传输控制协议/互联网协议,是广泛使用的通信协议
  • HTTP:超文本传输协议,是浏览器和服务器通过互联网进行通信的协议

2.网站的运行原理

(1)在浏览器中输入网址

(2)浏览器解析网址中包含的信息,如HTTP协议和域名

(3)浏览器与ISP通信,在DNS中查找网站对应的IP地址,然后将IP地址发送给浏览器的DNS服务,最后向网站的IP发送访问请求

(4)浏览器从网站地址中获取IP地址和端口,并打开TCP套接字连接,实现浏览器和Web服务器的连接

(5)浏览器根据用户操作向服务器发送相应的HTTP请求

(6)当Web服务器接收请求后,根据请求信息查找该HTML页面。

3.网站的开发流程

(1)需求分析:当拿到一个项目时,必须进行需求分析,清楚知道网站的类型、具体功能、业务逻辑以及网站的风格,此外还要确定域名、网站空间或服务器以及网站备案等

(2)规划静态内容:重新确定需求分析,并根据用户需求规划网站的内容板块草图

(3)设计阶段:根据网站草图由美工制成效果图。

(4)程序开发阶段:根据草图划分页面结构和设计,前端和后台可以同时进行。前端根据美工效果制作静态页面;后台根据页面结构和设计,设计数据库数据结构和开发网站后台

(5)测试和上线:在本地搭建服务器,测试网站是否存在Bug

(6)维护推广:在网站上线之后,根据实际情况完善网站的不足,定期修复和升级,保障网站运营顺畅,然后对网站进行推广宣传等

1.4走进Django

1.Django采用MTV的框架模式,即模型(Model)、模版(Template)和视图(Views):

  • 模型:数据存取层,处理与数据相关的所有事物
  • 模板:表现层、处理与表现相关的决定
  • 视图:业务逻辑层,存取模型及调取恰当模板的相关逻辑,模型与模版的桥梁

2.Django的特点:

  • 对象关系映射(Object Relational Mapping, ORM):通过定义映射类来构建数据模型,将模型与关系型数据库连接起来,使用ORM框架内置的数据库接口可实现复杂的数据操作
  • URL设计:开发者可以设计任意的URL(网站地址),而且还支持使用正则表达式设计
  • 模板系统:提供可扩展的模板语言,模板之间具有可继承性
  • 表单处理:可以生成各种表单模型,而且表单具有有效性检验功能
  • Cache系统:完善的缓存系统,可支持多种缓存方式
  • Auth认证系统:提供用户认证、权限设置和用户组功能,功能扩展性强
  • 国际化:内置国际化系统,方便开发出多种语言的网站
  • Admin后台系统:内置Admin后台管理系统,系统扩展性强

1.6安装Django

拥有python环境在终端中输入

pip insatll Django

1.7创建项目

django-admin startproject MyDjango

在MyDjango项目中包含MyDjango文件夹和manage.py文件,而My Django文件夹又包含4个.py文件。

  • manage.py:命令行工具,内置多种方法与项目进行交互。在命令提示窗口下,将路径切换到MyDjango项目并输入python manage.py help,可以查看该工具的指令信息
  • __ init __.py:初始化文件,一般无需修改
  • settings.py:项目的配置文件,项目的所有功能都需要在该文件中进行配置
  • urls.py:项目的路由设置,设置网站的具体网址内容
  • wsgi.py:全称为Python Web Server Gateway Interface,即Python服务器网关接口,是Python应用与Web服务器之间的接口,用于Django项目在服务器上的部署和上线,一般不需要修改

完成项目的创建后,接着创建项目应用,项目应用简称为App,相当于网站功能,每个App代表网站的一个功能。App的创建由文件manage.py实现

cd MyDjango
python manage.py startapp index

进入MyDjango,然后python manage.py startapp XXX创建,其中XXX是应用的名称。

从上图可以看到,index目录中有多个.py文件和migrations文件夹:

  • migrations:用于生成数据迁移文件,通过数据迁移文件可自动在数据库里生成相应的数据表

  • __ init __.py:index文件夹的初始化文件

  • admin.py:用于设置当前App的后台管理功能

  • apps.py:当前App的配置信息

  • models.py:定义数据库的映射类,每个类可以关联一张数据表,实现数据持久化,即MTV里面的模型(Model)

  • views.py:视图文件,处理功能的业务逻辑,即MTV里面的视图(Views)

    完成项目和App的创建后,最后在命令提示符输入指令启动项目

python manage.py runserver 

1.9Django入门基础

在终端中输入python manage.py help,可以查看相关的指令信息

changepassword修改内置用户表的用户密码
createsuperuser为内置用户表创建超级管理员账号
remove_stale_contenttypes删除数据库中已不使用的数据表
check检测整个项目是否存在异常问题
compilemessages编译语言文件,用于项目的区域语言设置
createcachetable创建缓存数据表,为内置的缓存机制提供存储功能
dbshell进入Django配置的数据库,可以执行数据库的SQL语句
diffsettings显示当前settings.py的配置信息与默认配置的差异
dumpdata导出数据表的数据并以JSON格式存储,如python manage.py dumpdata index > data.json,这是index的模型所对应的数据导出,并保存在data.json文件中
flush清空数据表的数据信息
inspectdb获取项目所有模型的定义过程
loaddata将数据文件导入数据表,如python manage.py loaddata data.json
makemessages创建语言文件,用于项目的区域语言设置
makemigrations从模型对象创建数据迁移文件并保存在App的migrations文件夹
migrate根据迁移文件的内容,在数据库里生成相应的数据表
sendtestmail向制定的收件人发送测试的电子邮件
shell进入Django的Shell模式,用于调试项目功能
showmigrations查看当前项目的所有数据迁移文件
sqlflush查看清空数据的SQL语句脚本
sqlmigrate根据迁移文件内容输出相应的SQL语句
sqlsequencereset重置数据表递增字段的索引值
squashmigrations对迁移文件进行压缩处理
startapp创建项目应用App
startproject创建新的Django项目
test运行App里面的测试程序
testserver运行App里面的测试层序
clearsessions清楚会话Session数据
collectstatic收集所有的静态文件
findstatic查找静态文件的路径信息
runserver在本地计算机上启动Django项目

打开setting.py,找到配置属性INSTALLED_APPS和TEMPLATES,分别将项目应用index和模板文件夹templates添加到相应的配置属性

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

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

最后在项目的urls.py(MyDjango文件夹的urls.py)、views.py(项目应用index的views.py文件)和index.html(templates文件夹的index.html)文件里编写相应的代码,即可实现简单的Hello World网页

#MyDjango的urls.py
from django.contrib import admin
from django.urls import path
from index import views

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

#index的views.py
from django.shortcuts import render

def index(request):
    return render(request, 'index.html')

#templates的index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello World</title>
</head>
<body>
    <span>Hello World</span>
</body>
</html>
  • 当用户在浏览器访问网址的时候,该网址在项目所设置的路由(urls.py文件)里找到相应的路由信息

  • 然后从路由信息里找到对应的视图函数(views.py文件),由视图函数处理用户请求

  • 视图函数将处理结果传递到模板文件(index.html文件),由模板文件生成网页内容,并在浏览器里展现

    启动MyDjango项目,并在浏览器上访问路由地址即可看到Hello World网页

第2章Django配置信息

2.1基本配置信息

一个简单的项目必须具备的基本配置信息有:项目路径、密钥配置、域名访问权限、App列表和中间件

以MyDjango项目为例,settings.py的基本配置信息如下:

import os
from pathlib import Path
#项目路径
BASE_DIR = Path(__file__).resolve().parent.parent
#密钥配置
SECRET_KEY = 'django-insecure-fpy7oby07m68ne6!y$nyptxwx@(hurcz09(r5n!2mfp_kb54q6'
#调试模式
DEBUG = True
#域名访问权限
ALLOWED_HOSTS = []
#App列表
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'index'
]
  • 项目路径BASE_DIR:主要通过os模块读取当前项目在计算机系统的具体路径,该代码在创建项目时自动生成

  • 密钥配置SECRET_KEY:这是一个随机值,在项目创建的时候自动生成,一般情况下无须修改。主要用于重要数据的加密处理,提高项目的安全性,避免遭受恶意攻击。密钥主要用于用户密码、CSRF机制和会话Session等数据加密

    • 用户密码:Django内置一套Auth认证系统,该系统具有用户认证和存储用户信息等功能,在创建用户的时候,将用户密码通过密钥进行加密处理,保证用户的安全性
    • CSRF机制:该机制主要用于表单提交,防止窃取网站的用户信息来制造恶意请求
    • 会话Session:Session的信息存放在Cookie中,以一串随机的字符串来表示,用于标识当前访问网站的用户身份,记录相关用户信息
  • 调试模式DEBUG:该值为布尔类型。如果在开发调试阶段,那么应设置为True,在开发调试过程中会自动检测代码是否大声更改,根据检测结果执行是否刷新系统。如果项目部署上线,那应该将其改为False,否则会泄露项目的相关信息

  • 域名访问权限ALLOWED_HOSTS:设置可访问的域名,默认值为空列表。当DEBUG为True并且ALLOWED_HOSTS为空列表时,项目只允许以localhost或127.0.0.1在浏览器上访问。当DEBUG为False时,ALLOWED_HOST为必填项,否则程序无法启动,如果想允许所有域名访问,可设置ALLOW_HOSTS=['*']

  • App列表INSTALLED_APPS:告诉Django有哪些App。在项目创建时已有admin、auth和session等配置信息,这些都是Django内置的应用功能

    • admin:内置的后台管理系统
    • auth:内置的用户认证
    • contenttypes:记录项目中所有model元数据(Django的ORM框架)
    • session:Session会话功能,用于标识当前访问网站的用户身份,记录相关用户信息
    • message:消息提示功能
    • staticfiles:查找静态资源路径

2.2资源配置

资源文件配置分为静态资源和媒体资源。静态资源的配置方式由配置属性STATIC_URL、STATICFILES_DIRS和STATIC_ROOT进行设置;媒体资源的配置方式由配置属性MEDIA_URL和MEDIA_ROOT决定的

1.资源路由—STATIC_URL

静态资源指的是网站中不会改变的文件。在一般的应用程序中,静态资源包括CSS文件、JavaScript文件以及图片等资源文件。

CSS也称层叠样式表(Cascading Style Sheets),是一种用来表现HTML(标准通用标记语言的一个应用)或XML(标准通用标记语言的一个子集)等文件样式的计算机语言。CSS不仅可以静态地修饰网页,还可以配个各种脚本语言对网页元素进行格式化

JavaScript是一种直译式脚本语言,也是一种动态类型、弱类型、基于原型的语言,内置支持类型。

一个项目在开发过程中肯定需要使用CSS和JavaScript文件,这些静态文件的存放主要由配置文件settings.py设置,Django默认配置信息如下:

#Static files(CSS, JavaScript, Images)
#https://docs.djangoproject.com/en/2.0/howto/static-files/
STATIC_URL = '/static/'

默认情况下,Django只能识别项目应用App的static文件夹里面的静态资源。当项目启动时,Django会从项目应用App里面查找相关的资源文件,查找功能主要由App列表INSTALLED_APPS的staticfiles实现。在index中创建static文件夹并在文件夹中放置图片bihzi.jpeg

Django在调试模式(DEBUG=True)下只能识别项目应用的App的static文件夹里面的静态资源,如果该文件夹改为其他名字,Django就无法识别,若将static文件夹放在MyDjango的目录下,那也是无法识别的。

进一步验证Django在调试模式下只能识别App的static文件夹,我们在index文件夹里创建Mystatic并放置图片,在MyDjango的根目录下创建static文件夹并放置图片

启动MyDjango并在浏览器中分别访问http://127.0.0.1:8000/static/bizhi.jpeghttp://127.0.0.1:8000/static/rs7.jpeghttp://127.0.0.1:8000/static/aodi.jpeg,可以发现只有第一个可以访问

从上述例子说明,若资源路由STATIC_URL的值为/static/,则浏览器访问静态资源的网站必须为static,否则无法访问,并且Django在调试模式下只能识别App目录下的static文件夹

2.资源集合—STATICFILES_DIRS

在配置文件settings.py中设置STATICFILES_DIRS属性。该属性以列表的形式表示,设置方式如下:

#设置根目录的静态资源文件夹static
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static'),
#设置App(index)的静态资源文件夹Mystatic                  	
os.path.join(BASE_DIR, 'index/Mystatic'),]

再次启动MyDjango并在浏览器上 访问图片,可以发现三者都可以正常访问。

浏览器访问图片的时候,图片路径皆为http://127.0.0.1:8000/static/xxx.jpeg,图片路径的static是指资源路径STATIC_URL的值,若将STATIC_URL的值改为Allstatic,则再次重启MyDjango项目并在浏览器上将图片资源路径的static改为Allstatic即可

3.资源部署—STATIC_ROOT

静态资源配置还有STATIC_ROOT,其作用是在服务器上部署项目,实现服务器和项目之间的映射。STATIC_ROOT主要收集整个项目的静态资源并存放在一个新文件夹,然后由该文件夹与服务器之间构建映射关系。STATIC_ROOT配置如下:

STATIC_ROOT = os.path.join(BASE_DIR,  'AllStatic')

当项目的配置属性DEBUG设为True的时候,Django会自动提供静态文件代理服务,此时整个项目处于开发阶段,因此无需使用STATIC_ROOT。当配置属性DEBUG设为False的时候,意味着项目进入生产环境,Django不再提供静态文件代理服务,此时需要在项目的配置文件中设置STATIC_ROOT

设置STATIC_ROOT需要使用Django操作指令collectstatic来收集所有静态资源,这些静态资源都会保存在STATIC_ROOT所设置的文件夹里。

4.媒体资源—MEDIA

媒体资源和静态资源是可以同时存在的,而且两者都可以独立运行,互不影响,而媒体资源只有配置属性MEDIA_URL和MEDIA_ROOT。以MyDjango为例,在MyDjango的根目录下创建media文件夹并存放图片

然后在配置文件settings.py里设置配置属性MEDIA_URL和MEDIA_ROOT,MEDIA_URL用于设置媒体资源的路由地址,MEDIA_ROOT用于获取media文件夹在计算机系统的完整路径信息

#设置媒体路由地址信息
MEDIA_URL = '/media/'
#获取media文件夹的完整路径信息
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

配置属性设置后,还需要将media文件夹注册到Django里,让Django知道如何找到媒体文件,否则无法在浏览器上访问该文件夹的文件信息。打开MyDjango文件夹的urls.py文件,为媒体文件夹media添加相应的路由地址

from django.contrib import admin
from django.urls import path, re_path
#导入项目应用index
from index import views
#配置媒体文件夹media
from django.views.static import serve
from django.conf import settings

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.index),
    #配置media的路由地址
    re_path('media/(?<Path>.*)', serve, {'document_root':settings.MEDIA_ROOT}, name='media')
]

再次启动MyDjango,并在浏览器上访问http://127.0.0.1:8000/media/pic.jpeghttp://127.0.0.1:8000/static/rs7.jpeg,皆可正常访问

2.3模版配置

在Web开发中,模版是一种比较特殊的HTML文档。这个文档HTML文档嵌入了一些能够让Django识别的变量和指令,然后由Django的模板引擎解析这些变量和指令,生成完整的HTML网页返回给用户浏览。创建项目时,Django已有初始的模板配置信息,如下所示:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

模版配置是以列表格式呈现的,每个元素具有不同的含义:

  • BACKEND:定义模板引擎,用于识别模板里面的变量和指令,内置的模板引擎有Django Templates和jinja2.Jinja2,每个模板引擎都有自己的变量和指令语法

  • DIR:设置模板所在路径,告诉Django在哪个地方查找模板的位置,默认为空列表

  • APP_DIRS:是否在 App里查找模板文件

  • OPPTIONS:用于填充在RequestContext的上下文(模板里面的变量和指令),一般情况下不做任何修改

    模板配置通常配置DIRS的属性值即可,在项目的根目录和index下分别创建templates文件夹,并在文件夹下分别创建文件index.html和app_index.html

    一般情况下,根目录的templates通常存放共用的模板文件,能为各个App的模板文件调用,这个模式符合代码重复使用原则。配置属性TEMPLATES的配置信息

    TEMPLATES = [
        {
          'BACKEND':'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR, 'templates'),
                    os.path.join(BASE_DIR, 'index/templates')],
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]
    

2.4数据库配置

1.mysqlclient连接MySQL

数据库配置是选择项目所使用的数据库类型, 不同的数据库需要设置不同的数据库引擎,数据库引擎用于实现项目与数据库的连接,Django提供4种数据库引擎:

  • 'django.db.backends.postgresql'
  • 'django.db.backends.mysql'
  • 'django.db.backends.sqlite3'
  • 'django.db.backends.oracle'

项目创建时默认使用Sqlite3数据库,这是一款轻型的数据库,常用于嵌入式系统开发,而且占用的资源非常少。Sqlite3数据库配置信息如下:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

如果把上述连接信息改为MySQL数据库,首先安装mysqlclient模块。

pip insatll mysqlclient

完成mysqlclient模块的安装后,在项目的配置文件settings.py中配置MySQL数据库连接信息

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'django_db',
        'USER': 'root',
        'PASSWORD': '12345678',
        'HOST': '127.0.0.1',
        'POST': '3306',
    }
}

在MySQL中创建数据库django_db,刚创建的数据库django_db是一个空白的数据库,接着在PyCharm的Termin界面下输入Django操作指令python manage.py migrate来创建Django内置功能的数据表。因为Django自带内置功能,如Admin后台系统、Auth用户系统和会话机制等功能,该指令可以将内置的迁移文件生成数据表。然后在数据库中查看是否生成相应的数据表

2.pymsql连接MySQL

除了使用mysqlclient模块连接之外,还可以使用pymysql模块连接数据库。pymsql模块也可用pip。pymsql模块安装完成之后,项目配置文件settings.pyd数据库配置信息无须修改,只要在MyDjango文件夹的__ init __.py中设置数据库连接模块即可

#MyDjango文件夹的__init__.py
import pymsql
pymsql.install_as_MySQLdb()

3.多个数据库的连接方式

一个项目里可能需要多个数据库才能满足开发需求,特别对于数据量过大的系统,单个数据库存储的数据越多就会使服务器负载越大,因此会将数据划分为多个数据库服务器共同存储,若Django想利用这些数据开发功能系统,则需要对各个数据库服务器进行连接

从Django单个数据库连接信息看到,配置属性DATABASES的属性值是以字典的格式表示的,字典里的每一对键值代表某一个数据库。因此,我们在配置属性DATABASES里设置多对键值对即可实现多个数据库连接。

DATABASES = {
#first database
'default': {
		'ENGINE': 'django.db.backends.mysql',
		'NAME':'django_db',
		'USER': 'root',
		'PASSWORD': '****',
		'HOST': '127.0.0.1',
		'PORT': 3306,
	},
#second database
'default': {
		'ENGINE': 'django.db.backends.mysql',
		'NAME':'django_db',
		'USER': 'root',
		'PASSWORD': '****',
		'HOST': '127.0.0.1',
		'PORT': 3306,
	},
#third database
'default': {
		'ENGINE': 'django.db.backends.sqlite3',
		'NAME': os.path.join(BASE_DIR, 'sqlite3'),
	},	
}

若项目中连接了多个数据库,则数据库之间的使用需要遵循一定的规则和设置。比如项目中定义了多个模型,每个模型所对应的数据表可以选择在某个数据库中生成,如果模型没有指向某个数据库,模型就会在key为default的数据库里生成

2.5中间件

中间件(Middleware)是一个用来处理Django的请求(Request)和响应(Response)的框架级别的钩子,它是一个轻量、低级别的插件系统,用于在全局范围内改变Django的输入和输出

当用户在网站中进行某个操作时,这个过程是用户向网站发送HTTP请求(Request):而网站会根据用户的操作返回相关的网页内容,这个过程称为相应处理(Response)。从请求到响应的过程中,当Django接收到用户请求时,首先经过中间件处理请求信息,执行相关的处理,然后将处理结果返回给用户。

从图中可以看出,中间的作用是处理用户请求信息和返回响应内容。开发者可以根据自己的开发需求自定义中间件,只要将自定义的中间件添加到配置属性MIDDLEWARE中即可激活

一般情况下,Django默认的中间件配置均可满足大部分的开发需求。我们在项目的MIDDLEWARE中添加LocaleMiddleware中间件,使得Django内置的功能支持中文显示:

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

配置属性MIDDLEWARE的数据格式列表类型,每个中间件的设置顺序是固定的,如果随意变更中间件,就很容易导致程序异常

  • SecurityMiddleware:内置的安全机制,保护用户与网站的通信安全
  • SessionMiddleware:会话Session功能
  • LocaleMiddleware:国际化和本地化功能
  • CommonMiddleware:处理请求信息,规划化请求内容
  • CsrfViewMiddleware:开启CSRF防护功能
  • AuthenticationMiddleware:开启内置的用户认证系统
  • Message Middleware:开启内置的信息提示功能
  • XFrameOptionsMiddleware:防止恶意程序单击劫持

第3章初探路由

一个完整的路由包含:路由地址、视图函数(或视图类)、可选变量和路由命名

3.1路由定义规则

路由称为URL(Uniform Resource Locator,统一资源定位符),也可以称为URLconf,是对可以从互联网上得到的资源位置和访问方法的一种简洁的表示,是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的路由,用于指出网站文件的路径位置。

1.Django 2.x路由定义

我们知道完整的路由包含:路由地址、视图函数(或者视图类)、可选变量和路由命名。其中基本的信息必须有:路由地址和视图函数

对MyDjango的目录进行调整,使其更符合开发规范性。在index文件夹里添加一个空白内容的urls.py。在App(index文件夹)里添加urls.py是将所有属于App的路由都写入该文件中,这样更容易管理和区分每个App的路由地址,而MyDjango文件夹的urls.py是将每个App的urls.py统一管理。这种路由设计是Django常用的,其工作原理如下:

(1)运行MyDjango项目时,Django从MyDjango文件夹的urls.py找到各个App所定义的路由信息,生成完整的路由列表

(2)当用户在浏览器上访问某个路由地址时,Django就会收到该用户的请求信息

(3)Django从当前请求信息获取路由地址,并在路由列表里匹配响应的路由信息,再执行路由信息所指向的视图函数,从而完成整个请求响应过程

MyDjango文件夹的urls.py代码:

from django.contrib import admin
from django.urls import path, include
urlpatterns = [
    #指向内置Admin后台系统的路由文件sites.py
    path('admin/', admin.site.urls)
    #指向index的路由文件urls.py
    path('', include('index.urls')),
]

MyDjango文件夹的urls.py定义两条路由信息,分别是Admin站点管理和首页地址(index)。其中,Admin站点管理在创建项目时已自动生成,一般情况下无须更改:首页地址是指index文件夹urls.py。

由于首页地址分发给index的urls.py处理,因此下一步需要对index的urls.py编写路由信息,代码如下

#index.urls
from django.urls import path
from . import views 
urlpatterns = [
    path('', views.index)
]

index的urls.py的编写规则与MyDjango文件夹的urls.py大致相同,这是最为简单的定义方法,此外还可以参考内置的Admin功能的路由定义方法

在index的urls.py导入index的views.py文件,该文件用于编写视图函数或视图类,主要用于处理当前请求信息并返回响应内容给用户。路由信息path("", views.index)的view.index是指视图函数index处理网站首页的用户请求和响应过程。因此,在index的views.py中编写index函数的过程处理,代码如下:

from django.shortcuts import render

def index(request):
    value = 'This is a test!'
    print(value)
    return render(request, 'index.html')

index函数必须设置一个参数,参数名不固定,但常以request进行命名,代表当前用户的请求对象,该对象包含当前请求的用户名、请求内容和请求方式等

视图函数执行完成后必须使用return将处理结果返回,否则程序会抛出异常信息。启动MyDjango项目,在浏览器里访问127.0.0.1:8000

从上述例子看到,当启动MyDjango项目时,Django会从配置文件settings.py读取属性ROOT_URLCONF的值,默认值为MyDjango.urls,其代表MyDjango文件夹的urls.py,然后根据ROOT_URLCONF的值来生成整个项目的路由列表

路由文件urls.py的路由定义规则是相对固定的,路由列表由urlpatterns表示,每个列表代表一条路由。路由是由Django的path函数定义的,该函数第一个参数是路由地址,第二个参数是由所对应的处理函数(视图函数或视图类),这两个参数是路由定义的必选参数

2.Django 1.X路由定义

Django1版本的路由定义规则是由Django的url函数定义的,url函数的第一个参数是路由地址,第二个参数是路由所对应的处理函数,这两个参数也是必选参数。而路由地址需设置路由符号^和表当前路由地址的相对路径;。^代表当前路由地址的相对路径;代表当前路由地址的终止符

我们分别改写项目的MyDjango文件夹和index文件夹的urls.py文件,使用Django1.X的路由定义规则;

#MyDjango/urls.py
from django.contrib import admin
from django.conf.urls import url
from django.urls import include
urlpatterns = [
    #指向内置Admin后台系统的路由文件sites.py
    url('admin/', admin.site.urls),
    #指向index的路由文件urls.py
    url('^', include('index.urls'))
]
#index/urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
    url('^$', views.index),
    url('^new/$', views.new)
]

index文件夹的urls.py定义两条路由信息,因此需要在index文件夹的views.py里定义相应的视图函数,代码如下:

from django.shortcuts import render
from django.http import HttpResponse

def index(request):
    return render(request, 'index.html')

def new(request):
    return HttpResponse('This is a new page')

在MyDjango文件夹的urls.py文件里,url('^', include('index.urls'))的路由符号^代表当前路由地址的相对地址,即http://127.0.0.1:8000。该路由使用了Django的路由函数include,它将路由交给index文件夹的urls.py完成路由定义

路由符号代表路由地址的终止位置,如果没有终止符号代表路由地址的终止位置,如果没有终止符号,那么在浏览器输入任意地址都能成功访问该路由地址。以url('^new/,views.new)为例,若将终止符号', views.new)为例,若将终止符号去掉,则在浏览器访问http://127.0.0.1:8000/new/XXX时,其XXX内容不限,这样的路由地址都符合 url('^new/', views.new)定义规则,从而执行视图函数new完成相应过程

3.路由变量的设置

在日常开发过程中,有时一个路由可以代表多个不同的页面,如编写带有日期的路由,若根据前面的编写方式,一年就需要编写365个不同的路由才能实现,这种做法并不可取。因此Django在定义路由时,可以对路由设置变量值,使路由具有多样性。

路由的变量类型有字符类型、整型、slug和uuid,最为常用的是字符类型和整型。

  • 字符类型:匹配任何非空字符串,但不含斜杠。如果没有指定类型,就默认使用该类型
  • 整型:匹配0和正整数
  • slug:可理解为注释、后缀或附属等概念,常作为路由的解释性字符。可匹配任何ASCII字符以及连接符和下划线,能使路由更加清晰易懂。比如网页的标题是“13的孩子”,其路由地址可以设置为“13-sui-hai-zi”
  • uuid:匹配一个uuid格式的对象。为了防止冲突,规定必须使用“-”并且所有的字母必须小写,例如057194d3-6885-417e-a8a8-6c931e272f00

根据上述变量类型,在MyDjango项目的index文件夹的urls.py里新定义路由,并且带有字符类型、整型和slug的变量,代码如下:

#index/urls.py
from django.urls import path
from . import views
urlpatterns = [
    #addstr
    path('<year>/<int:month>/<slug:day>', views.myvariable)
]

在路由中,使用变量符号“<>”可以为路由设置变量。在括号里面以冒号划分两部分,冒号前面代表的是变量的数据类型,冒号后面代表的是变量名,变量名可以自行命名,如果没有设置变量的数据类型,就默认为字符类型。

在上述新增的路由中,路由的处理函数为myvariable,因此index的views.py中编写视图函数myvariable的处理过程,代码如下:

#views.py func-myvariable
from django.http import HttpResponse
def myvariable(request, year, month, day):
	return HttpResponse(str(year) + '/' + str(month) + '/' + str(day))

视图函数中myvariable有4个参数,其中参数year、month和day的参数值分别来自路由地址所设置的变量< year >、<int: month>和< slug:day> 。启动项目,在浏览器上输入127.0.0.1:8000/2021/9/24

除了在路由地址设置变量外,Django还支持在路由地址外设置变量(路由的可选变量)。我们在index的urls.py和views.py中分别新增路由和视图函数,代码如下:

#index/urls.py
from django.urls import path
from . import views
urlpatterns = [
    path('<year>/<int:month>/<slug:day>', views.myvariable),
    path('', views.index, {'month': '2021/09/25'})
]
#index/views.py
from django.http import HttpResponse
def myvariable(request, year, month, day):
    return HttpResponse(str(year) + '/' + str(month) + '/' + str(day))

def index(request, month):
    return HttpResponse('这是路由地址之外的变量:'+ month)

重新运行MyDjango,访问127.0.0.1:8000运行结果如下

4.正则表达式的路由定义

为了进一步规范日期格式,可以使用正则表达式限制路由地址变量的取值范围。在index文件夹的urls.py里使用正则表达式定义路由地址,代码如下:

#index/urls.py
from django.urls import re_path
from . import views
urlpatterns = [
    re_path('(?P<year>[0-9]{4})/(?P<month>[09]{2})/(?P<day>[0-9]{2}).html', views.mydate)
]

路由的正则表达式是由函数re_path定义的,其作用是对路由变量进行截取与判断,正则表达式是以小括号为单位的,每个小括号的前后可以使斜杠或者其他字符将其分割与结束。以(?P[0-9]{4})为例,?P是固定格式,字母P必须为大写;为变量名;[0-9]{4}是正则表达式的匹配模式,代表变量的长度为4,只允许取0~9的值

上述路由的处理函数为mydate函数,因此还需要在index的views.py中编写视图函数mydate,代码如下:

#views.mydate
from django.http import HttpResponse
def mydate(request, year, month, day):
	return HttpResponse(str(year) + '/' + str(month) + '/' str(day))

启动MyDjango项目,在浏览器上输入127.0.0.1:8000/2021/09/25.html即可查看运行结果

路由地址的末端设置了".html",这是一种伪静态URL技术,可将网站设置为静态网址,用于SEO搜索引擎的爬取,如百度、Goole。此外,在末端设置".html"是为变量day设置终止符,假如末端没有设置".html",在浏览器上输入无限长的字符串也能正常访问

3.2命名空间与路由命名

1.命名空间namespace

在MyDjango项目创建新的项目应用user,并且在user文件夹里创建urls.py文件,然后在配置文件settings.py的INSTALLED_APPS中添加项目应用user,使得Django在运行的时候能够识别项目应用user

在MyDjango文件夹的urls.py中重新定义路由信息,分别指向index文件夹的urls.py和user文件夹的urls.py,代码如下:

from django.contrib import admin
from django.urls import path, include
urlpatterns = [
	path('admin/', admin.site.urls),
	path('', include(('index.urls', 'index'), namespace='index')),
	path('user/', include(('user.urls', 'user'), namespace='user'))
]

路由函数include设有参数arg和namespace,参数arg指向项目应用App的urls.py文件,其数据格式以元组或字符串表示;可选参数namespace是路由的命名空间。

若要对路由设置参数namespace,则参数arg必须以元组格式表示,并且元组的长度必须为2。元组的元素说明:

  • 第一个元素为项目应用的urls.py文件
  • 第二个元素可以自行命名,但不能为空,一般情况是以项目应用的名称进行命名。

2.路由命名name

在上一节的基础上,我们在index文件夹的urls.py和user文件夹的urls.py中重新定义路由,代码如下:

#index/urls.py
from django.urls import path, re_path
from . import views
urlpatterns = [
    re_path('(?P<year>[0-9]{4}).html', views.mydate, name='mydate'),
    path('',  views.index, name='index')
]
#user/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('index', views.index, name='index'),
    path('login', views.userLogin, name='userLogin')
]

每个项目应用的urls.py都定义了两条路由,每条路由都由相应的视图函数进行处理,因此在index/views.py和user/views.py中定义视图函数,代码如下:

#index/views.py
from django.shortcuts import render
from django.http import HttpResponse
def mydate(request, year, month, day):
    return HttpResponse(str(year))
def index(request, month):
    return render(request, 'index.html')
#user/views.py
from django.http import HttpResponse
def index(request):
    return HttpResponse('This is a userIndex')
def userLogin(request):
    return HttpResponse('This is a userLogin')

项目应用index和user的urls.py所定义的路由都设置了参数name,这是对路由进行命名,它是路由函数path或re_path的可选参数。如果路由里使用路由函数include,就可以对该路由设置参数name,因为路由的命名空间namespace是路由函数include的可选参数,而路由命名name是路由函数path或re_path的可选参数,两者隶属于不同的路由函数,因此可以在同一条路由里共存

3.3路由的使用方式

1.在模版中使用路由

从网站开发的角度分析,网址代表路由,若想将项目定义的路由显示在网页上,则要在模版上使用模版语法来生成路由地址。Django内置了一套模版语法,它能将Python的语法转换成HTML语言,然后通过浏览器解析HTML语言并生成相应的网页内容

打开MyDjango项目,该项目仅有一个项目应用文件夹index和模版文件夹templates,在index和templates中分别创建urls.py和index.html并且在settings.py中添加配置信息。

项目环境搭建成功后,在MyDjango文件夹的urls.py中使用路由函数path和include定义项目应用文件夹index的路由,代码如下:

#MyDjango/urls.py
from django.contrib import admin
from django.urls import path
from django.urls import include
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('index.urls'))
]

在index里,分别在urls.py和views.py文件中定义路由和视图函数;并且在模板文件夹templates的index.html文件中编写模版内容,代码如下:

#index/urls.py
from django.urls import path, re_path
from . import views
urlpatterns = [
    path('<year>/<int:month>/<slug:day>', views.mydate, name='mydate'),
    path('',  views.index)
]
#index/views.py
from django.shortcuts import render
from django.http import HttpResponse
def mydate(request, year, month, day):
    return HttpResponse(str(year) + '/' + str(month) + '/' + str(day))
def index(request, month):
    return render(request, 'index.html')

#templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello World</title>
</head>
<body>
    <span>Hello World</span>
    <br>
    <a href="{% url 'mydate' '2021' '09' '25' %}">查看日期</a>
</body>
</html>

在index.html里,我们使用了Django内置的模版语法url来生成路由地址,模板语法url里设有4个不同的参数,其说明如下:

  • mydate:代表命名mediate的路由,即index里的urls.py设有字符类型、整型和slug的路由、
  • 2021:代表路由地址year
  • 09:代表路由地址变量month
  • 25:代表路由地址变量day

模板语法url的参数设置与路由定义是相互关联的,具体说明:

  • 若路由地址存在变量,则模板语法url需要设置相应的参数值,参数值之间使用空格隔开
  • 若路由地址不存在变量,则模板语法url只需设置路由命名name即可,无须设置额外的参数
  • 若路由地址的变量与模板url的参数数量不相同,则在浏览器访问网页的时候会提示NoReverseMath at/的错误信息

上述例子中,MyDjango文件夹的urls.py在使用函数include定义路由是并没有设置命名空间namespace。若设置了命名空间namespace,则模板里使用路由的方式有所变化。下面对MyDjango文件urls.py和模板文件夹templates的index.html代码进行修改:

#MyDjango/urls.py
from django.contrib import admin
from django.urls import path, re_path, include
#导入项目应用index
from index import views
#配置媒体文件夹media
from django.views.static import serve
from django.conf import settings
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('index.urls')),
    path('', include(('index.urls', 'index'), namespace='index')),
]

#templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello World</title>
</head>
<body>
    <span>Hello World</span>
    <br>
    {# <a href="{% url 'mydate' '2021' '09' '26' %}">查看日期</a> #}
    <a href="{% url 'index:mydate' '2021' '09' '25' %}">查看日期</a>
</body>
</html>

从模板文件index.html可以看出,若项目应用的路由设有命名空间namespace,则模板语法url在使用路由时,需要在命名路由name前面添加命名空间namespace并且使用冒号隔开,如“namespace:name”。若路由在定义过程中使用命名空间namespace,而模板语法url没有添加命名空间namespace,则会报错

2.反向解析reverse和reslove

路由除了在模板里使用之外,还可以在视图里使用。我们知道Django的请求生命周期是指用户在浏览器访问网页时,Django根据网址在路由列表里查找相应的路由,再从路由里找到视图函数或视图类进行处理,将处理结果作为响应内容返回浏览器并生成网页内容。这个生命周期是不可逆的,而在视图里使用这一过程被称为反向解析

Django的反向解析主要由函数reverse和resolve实现:函数reverse时通过路由命名或可调用视图对象来生成路由地址;函数resolve是通过路由地址来获取路由对象信息的

以MyDjango为例,在MyDjango文件夹的urls.py和index的urls.py里定义路由地址,代码如下:

#MyDjango/urls.py
from django.urls import path, re_path, include
urlpatterns = [
    path('', include(('index.urls', 'index'), namespace='index')),
]
#index/urls.py
from django.urls import path
from . import views
urlpatterns = [
    path('<year>/<int:month>/<slug:day>', views.mydate, name='mydate'),
    path('',  views.index, name='index')
]

上述代码定义了项目应用index的路由信息,路由命名分别为index和mydate,路由定义过程中设置了命名空间namespace和路由命名name

由于反向解析函数reverse和resolve常用于视图(views.py)、模型(models.py)或Admin后台(admin.py)等,因此在视图(views.py)的函数mydate和index里分别使用reverse和resolve,代码如下:

from django.shortcuts import reverse
from django.http import HttpResponse
from django.urls import resolve

def mydate(request, year, month, day):
    args = ['2021', '09', '25']
    result = resolve(reverse('index:mydate', args=args))
    print('kwargs:', result.kwargs)
    print('url_name:', result.url_name)
    print('namespace:', result.namespace)
    print('view_name:', result.view_name)
    print('app_name', result.app_name)
    return HttpResponse(str(year) + '/' + str(month) + '/' + str(day))

def index(request):
    kwargs = {'year': 2021, 'month': 9, 'day': 26}
    args = ['2021', '9', '25']
    print(reverse('index:mydate', args=args))
    print(reverse('index:mydate', kwargs=kwargs))
    return HttpResponse(reverse('index:mydate', args=args))

函数index主要是使用反向解析函数reverse来生成路由mydate的路由地址。为了进一步了解函数reverse,我们打开其源码

  • viewname:代表路由命名或可调用视图对象,一般情况是以路由命名name来生成路由地址的
  • urlconf:设置反向解析的URLconf模块。默认情况下,使用配置文件settings.py的ROOT_URLCONF属性(MyDjango文件夹的urls.py)
  • args:以列表方式传递路由地址变量,列表元素顺序和数量应与路由地址变量的顺序和数量一致
  • kwargs:以字典方式传递路由变量,列表元素顺序和数量应与路由地址变量的顺序和数量一致
  • current_app:提示当前正在执行的视图所在的项目,主要起到了提示作用。

运行MyDjango项目,在浏览器上访问127.0.0.1:8000,当前请求将视图函数index处理,该函数使用reverse来获取路由命名为mydate的路由地址并显示在网页上

接下来分析视图函数mydate,它是在函数reverse的基础上使用函数resolve,源码如下

  • path:代表路由地址,通过路由地址来获取对应的路由对象信息
  • urlconf:设置反向解析的URLconf模块。默认情况下,使用配置文件settings.py的ROOT_URLCONF属性(MyDjango文件的urls.py)

函数resolve是以路由对象作为返回值的,该对象内置多种函数方法来获取具体的路由信息

函数方法说明
func路由的视图函数对象或视图对象
args以列表格式获取路由的变量信息
kwargs以字典格式获取路由的变量信息
url_name获取路由命名name
app_name获取路由函数include的参数arg的第二个元素值
app_names与app_name功能一致,但以列表格式表示
namespace获取路由的命名空间namespace
namespaces与namespace功能一致,但以列表格式表示
view_name获取整个路由的名称,包括命名空间

运行MyDjango项目,访问127.0.0.1:8000/2021/9/25,在PyCharm的下方查看函数resolve的对象信息

4.路由重定向

重定向称为HTTP协议重定向,也可以称为网页跳转,它对应的HTTP状态码为301、302、303、307、308。Django的网页重定向有两种方式:第一种方式是路由重定向;第二种方式是自定义视图的重定向。两种重定向方式各有优点,前者是使用Django内置的视图类RedirectView实现的,默认支持HTTP的GET请求;后者是在自定义视图的响应状态设置重定向,能让开发者实现多方面的开发需求。

我们在MyDjango项目里分别简述Django的两种重定向方式,在index的urls.py中定义路由trunTo,代码如下:

from django.urls import path, re_path
from . import views
from django.views.generic import RedirectView
urlpatterns = [
    path('<year>/<int:month>/<slug:day>', views.mydate, name='mydate'),
    path('',  views.index, name='index'),
    path('trunTo', RedirectView.as_view(url='/'), name='trunTo')
]

在路由里使用视图类RedirectView必须使用as_view方法将视图类实例化,参数url用于设置网页跳转的路由地址,'/'代表网站首页(路由命名为index的路由地址)。然后在index的views.py中定义视图函数mydate和index,代码如下:

from django.http import HttpResponse
from django.shortcuts import reverse,redirect

def mydate(request, year, month, day):
    return HttpResponse(str(year) + '/' + str(month) + '/' + str(day))

def index(request):
    print(reverse('index:trunTo'))
    return HttpResponse(reverse('index:mydate', args=[2021, 9, 25]))

视图函数index是使用重定向函数redirect实现网页重定向的,这是Django内置的重定向函数,其函数参数只需传入路由地址即可实现重定向

第4章 探究FBV视图

视图(Views)是Django的MTV架构模式的V部分,主要负责处理用户请求和生成相应的响应内容,然后在页面或其他类型文档中显示。也可以理解为MVC架构里的C部分(控制器),主要处理功能和业务上的逻辑。我们习惯使用视图函数处理HTTP请求,即在视图定义def函数,这种方式称为FBV(Function Base Views)。

4.1设置响应方式

网站的运行原理是遵从HTTP协议,分为HTTP请求和HTTP响应。HTTP响应方式也称为HTTP状态码,分为5种状态:消息、成功、重定向、请求错误和服务器错误。若以使用频率划分,则HTTP状态码可分为:成功、重定向和异常响应(请求错误和服务器错误)

1.返回响应内容

视图函数是通过return方式返回响应内容,然后生成相应的网页内容呈现在浏览器上。return是Python的内置语法,用于设置函数的返回值,若要设置不同的响应方式,则需要使用Django内置的响应类。

响应类型说明
HttpResponse('Hello world')状态码200,请求已成功被服务器接收
HttpResponseRedirect('/')状态码302,重定向首页地址
HttpResponsePermanentRedirect('/')状态码301,永久重定向首页地址
HttpResponseBadRequest('400')状态码400,访问的页面不存在或请求错误
HttpResponseNotFound('404')状态码404,网页不存在或网页URL失效
HttpResponseForbidden('403')状态码403,没有访问权限
HttpResponseNotAllowed('405')状态码405,不允许使用该请求方式
HttpResponseServerError('500')状态码500,服务器内容错误
JsonResponse({'foo': 'bar'})默认状态码200,响应内容为JSON
StreamingHttpResponse()默认状态码200,响应内容以流式输出

不同的响应方式代表不同的HTTP状态码,其核心作用是Web Server服务器用来告诉浏览器当前的网页请求发生了什么事,或者当前Web服务器的响应状态。上述的响应类主要来自于模块django.http,该模块是实现响应功能的核心。以HttpResponse为例,在MyDjango项目的index文件夹的urls.py和views.py中编写功能代码:

#index/urls.py
from django.urls import path
from . import views
urlpatterns = [
    path('',  views.index, name='index'),
]
#index/views.py
from django.http import HttpResponse
def index(request):
    html = '<h1>Hello World</h1>'
    return HttpResponse(html, status=200)

视图函数index使用响应类HttpResponse实现响应过程。从HttpResponse的参数可知,第一个参数是响应内容,一般是网页内容或JSON数据,网页内容是以HTML语言为主的,JSON数据用于生成API接口数据。第二个参数用于设置HTTP状态码,它支持HTTP所有的状态码

从HttpResponse的使用过程可知,如果生成网页内容,就需要将HTML语言以字符串的形式表示,如果网页内容过大,就会增加视图函数的代码量,同时也没有体现模版的作用。因此,Django在此基础上进行了封装处理,定义了函数render、render_to_response和redirect。

render和render_to_response实现是一致的。现在对render进行讲解。

render的参数request和template_name是必需参数,其余的参数是可选参数:

  • request:浏览器向服务器发送的请求对象,包含用户信息、请求内容和请求方式
  • Template_name:设置模版文件名,用于生成网页内容
  • content:对模版上下文(模版变量)赋值,以字典格式表示,默认情况是一个空字典
  • content_type:响应内容的数据格式,一般情况使用默认值即可
  • status:HTTP状态码,默认为200
  • using:设置模版引擎,用于解析模版文件,生成网页内容

以MyDjango为例,在index/views.py和templates/index.html编写以下代码:

#index/views.py
from django.shortcuts import render
def index(request):
    value = {'title': 'Hello MyDjango'}
    return render(request, 'index.html', context=value)
#templates/index.html
<!DOCTYPE html>
<html>
<body>
<h3>{{ title }}</h3>
</body>
</html>

视图函数index定义的变量value作为render的参数contenxt,而模版index.html里面通过使用模版上下文(模版变量){{titile}}来获取变量value的数据,上下文的命名必须与变量value的数据命名(字典的key)相同,这样的Django内置的模版引擎才能将参数context(变量value)的数据与模版上下文进行配对,从而将参数context的数据转换成网页内容。

在实际开发过程中,如果视图传递的变量过多,在设置参数context时就显得非常冗余,而且不利于日后的维护和更新。因此,可以使用Python内置语法locals()取代参数context,在index/views.py和templates/index.html中重新编写代码

#index/views.py
from django.shortcuts import render
def index(request):
    title = {'key': 'Hello MyDjango'}
    content = {'key': 'This is my django'}
    return render(request, 'index.html', locals())
#templates/index.html
<!DOCTYPE html>
<html>
<body>
<h3>{{ title.key }}</h3>
<div>{{ content.key  }}</div>
</body>
</html>

下图为运行结果

HttpResponse、HttpResponseRedirect和HttpResponseNotFound等,其中最为核心的响应类是HttpResponse,它是所有响应类的基础。render,该函数能直接读取模板文件,并且能设置多种响应方式

2.设置重定向

重定向的状态码为301和302,前者是永久性跳转,后者是临时跳转,两者的区别在于搜索引擎的网页抓取。301重定向是永久的重定向,搜索引擎在抓取新内容的同时会讲旧的网址替换为重定向之后的网址。302跳转是暂时的跳转,搜索引擎会抓去新内容而保留旧的网址。

重定向类HttpResponseRedirect和HttpResponsePermanentRedirect分别代表HTTP状态302和301,查看两者源码,发现二者都继承HttpResponseRedirectBase类

接下来查看HttpResponseRedirectBase的定义过程,发现该类继承了HttpResponse,并重写了一些方法

HttpResponseRedirect或HttpResponsePermanentRedirect的使用只需传入路由地址即可,两者只支持路由地址而不支持路由命名传入。为例进一步完善功能,Django在此基础上定义了重定向函数redirect,该函数支持路由地址或路由命名的传入,并且能通过函数参数来设置重定向的状态码。

  • 判断参数permanent的真假性来选择重定向的函数。若参数permanent为True,则调用HttpResponsePermanentRedirect来完成重定向过程;若为False,则调用HttpResponseRedirect。
  • 由于HttpResponseRedirect和HttpResponsePermanentRedirect只支持路由地址的传入,因此函数redirect调用resolve_url方法对参数t0进行判断。若参数t0是路由地址,则直接将参数t0的参数值返回;若参数t0是路由命名,则使用reverse函数转换路由地址;若参数t0是模型对象,则将模型转换成相应的路由地址

函数redirect是将HttpResponseRedirect和HttpResponsePermanentRedirect的功能进行完善和组合。我们在MyDjango项目里讲述这三者的使用方法

#index/urls.py
from django.urls import path
from . import views
urlpatterns = [
    path('',  views.index, name='index'),
    path('shop', views.shop, name='shop')
]
#index/views.py
from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
from django.http import HttpResponseRedirect
from django.shortcuts import render, redirect, reverse

def index(request):
    return redirect('index:shop', permanent=True)
	#设置302的重定向
    #url = reverse('index:shop')
    #return HttpResponseRedirect(url)
    #设置301的重定向
    #return HttpResponsePermanentRedirect(url)
    
def shop(request):
    return render(request, 'index.html')

视图函数index的响应函数redirect将参数设为True,并跳转到路由命名为shop的网页;若使用HttpResponseRedirect和HttpResponsePermanentRedirect,则需要使用reverse函数将路由命名转换成路由地址。

3.异常响应

异常响应是指HTTP状态码为404或500的响应状态,它与正常的响应过程是一样的,只是HTTP状态码有所不同,因此使用redirect作为响应过程,并设置参数status的状态码即可实现异常响应

在MyDjango/urls.py中定义路由以及在index/views.py中定义视图函数,代码如下:

#MyDjango/urls.py
from django.urls import path, re_path, include
urlpatterns = [
    path('', include(('index.urls', 'index'), namespace='index')),
]
#set 404 page
handler404 = 'index.views.page_not_found'
#set 500 page
handler500 = 'index.views.page_error'

#index/views.py
from django.shortcuts import render

def page_not_found(request, exception):
    return render(request, '404.html', status=404)

def page_error(request, exception):
    return render(request, '500.html', status=500)

同时在index/templates中创建404.html,500.html,代码如下:

<!-- 404.html -->
<!DOCTYPE html>
<html lang="en">
<body>
<h3>This is a 404 page</h3>
</body>
</html>

<!-- 500.html -->
<!DOCTYPE html>
<html>
<body>
<h3>This is a 500 page</h3>
</body>
</html>

上述内容是Django全局404和500的异常响应,只需在项目的urls.py中设置handler404和handler500.变量值是指向某个项目应用的视图函数,而被指向的视图函数需要设置相应的模版文件和状态响应码

为了验证全局404和500的异常响应,需要对MyDjango项目进行功能调整,修改settings.py的DEBUG和ALLOWED_HOSTS,代码如下:

#settings.py
DEBUG = False

ALLOWED_HOSTS = ['*']

#index/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('',  views.index, name='index'),
]

#index/views.py
from django.shortcuts import render
from django.http import Http404

def index(request):
    if request.GET.get('error', ''):
        raise Http404("page does not exist")
    else:
        return render(request, 'index.html')

4.文件下载功能

响应内容除了返回网页信息外,还可以实现文件下载功能,是网站最常用的功能之一。Django提供了三种方式实现文件下载,分别是HttpResponse、StreamingHttpResponse和FileResponse:

  • HttpResponse是所有响应过程的核心类,它的底层功能类是HttpResponseBase
  • StreamingHttpResponse是在HttpResponseBase的基础上进行继承与重写的,它实现流式响应输出(流式响应输出是使用Python的迭代器将数据进行分段处理并传输的),适用于大规模数据响应和文件传输响应
  • FileResponse是在StreamingHttpResponse的基础上进行继承与重写的,它实现文件的流失响应输出,只适用于文件传输响应

  • 参数streaming_content的数据格式可设为迭代器对象或字节流,代表数据或文件内容
  • 形参 *args和 **kwargs设置HttpResponseBase的参数,即响应内容的数据格式content_type和响应状态码status等参数

以MyDjango为例,在MyDjango/urls.py、index/urls.py、index/views.py 和templates/index.html中分别定义路由、视图函数和模版文件,代码如下:

#MyDjango/urls.py
from django.urls import path, re_path, include
urlpatterns = [
    path('', include(('index.urls', 'index'), namespace='index')),
]

#index/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('',  views.index, name='index'),
    path('downloadfile1', views.downloadfile1),
    path('downloadfile2', views.downloadfile2),
    path('downloaffile3', views.downloaffile3),
]

#index/views.py
from django.shortcuts import render
from django.http import Http404, HttpResponse, StreamingHttpResponse, FileResponse

def index(request):
        return render(request, 'index.html')

def filedownload1(request):
    file_path = '/Users/zhouyuchen/BRICKS/pics/1.png'
    try:
        r = HttpResponse(open(file_path, 'rb'))
        r['content_type'] = 'application/octet-stream'
        r['Content-Disposition'] = 'attachment;filename=1.png'
        return r
    except Exception:
        raise Http404


def filedownload2(request):
    file_path = '/Users/zhouyuchen/BRICKS/pics/2.png'
    try:
        r = HttpResponse(open(file_path, 'rb'))
        r['content_type'] = 'application/octet-stream'
        r['Content-Disposition'] = 'attachment;filename=2.png'
        return r
    except Exception:
        raise Http404


def filedownload3(request):
    file_path = '/Users/zhouyuchen/BRICKS/pics/3.png'
    try:
        r = HttpResponse(open(file_path, 'rb'))
        r['content_type'] = 'application/octet-stream'
        r['Content-Disposition'] = 'attachment;filename=3.png'
        return r
    except Exception:
        raise Http404
        
#templates的index.html
<!DOCTYPE html>
<html>
<body>
    <a href="{%url 'index:download1' %}">HttpResponse-下载</a>
    <br>
    <a href="{%url 'index:download2' %}">StreamingHttpResponse-下载</a>
    <br>
    <a href="{%url 'index:download3' %}">FileResponse-下载</a>
</body>
</html>

上述例子证明HttpResponse、StreamingHttpResponse和FileResponse都能实现文件下载功能,但三者有一定差异:

  • HttpResponse实现文件下载存在很大的弊端,其工作原理是将文件读取并载入内存,然后输出到浏览器实现下载功能。
  • StreamingHttpResponse和FileResponse的实现原理是相同的,两者都是将下载的文件分批写入本地磁盘,实现文件的流失响应输出
  • 从适用范围来说,StreamingHttpResponse的适用范围更为广泛,可支持大规模数据或文件输出,而FileResponse只支持文件输出
  • 从使用方式来说,由于StreamingHttpResponse支持数据或文件输出,因此在使用时 需要设置相应输出类型和方式,而FileResponse 只需设置三个参数即可实现文件下载功能

4.2HTTP请求对象

1.获取请求信息

请求方式说明
OPTIONS返回服务器针对特定资源所支持的请求方法。
GET向特定资源发出请求(访问网页)
POST向指定资源提交数据处理请求(提交表单、上传文件)
PUT向指定资源位置上传数据内容
DELETE请求服务器删除request-URL所标识的资源。
HEAD与GET 请求类似,返回的响应中没有具体内容,用于获取报头
TRACE回复和显示服务器收到的请求,用于测试和诊断
CONNECTHTTP/1.1 协议中能够将连接改为管道方式的代理服务器

对于Django,当它收到HTTP请求之后, 会根据http请求携带的请求参数以及请求信息来创建一个WSGIrequest对象,并且作为视图函数的首个参数。这个参数通常写成request该参数包含用户所有的请求信息。

从类SWGIRequest的定义,看到他继承并重写HttpRequest若要获取请求信息,则只需从类SWGIRequest读取相关的类属性即可。

  • COOKIE: 获取客户端(浏览器)的cookie信息以字典形式表示,并且键值对都是字符串类型。
  • FILES:django.http.request.QueryDict对象,包含所有的文件上传信息
  • GET:获取GET请求的请求参数,它是Django.http.request.QueryDict对象, 操作起来类似于字典。
  • POST:获取POST请求的请求参数,它是django.hhtp.request.QueryDict对象,操作起来类似于字典
  • META: 获取客户端的请求头信息,以字典形式存储。
  • method: 获取当前请求的请求方式。
  • path: 获取当前请求的路由地址。
  • session: 一个类似于字典的对象,用来操作服务器的会话信息,可临时存放用户信息。
  • user:当Django启动AuthenticationMiddleware中间件时才可用。它的值是内置数据模型User的对象,表示当前登陆的用户。如果用户没有登陆,那么user将设置为django.contrib.auth.models.AnonymousUser的一个实例

由于类WSGIRequest继承并重写HttpRequest,因此类HttpRequest里定义的类方法同样适用于类WSGIRequest。

类HttpRequest一共定义了31个类方法,我们选择一些常用的方法进行讲述

  • is_secure():是否采用HTTPS协议
  • is_ajax():是否采用AJAX发送HTTP请求。判断原理是请求头中是否存在X-Requested-With:XMLHttpRequest
  • get_host((): 获取服务器的域名,如果在访问的时候设有端口就加上端口号。如127.0.0.1:8000
  • Get_full_path():返回路由地址。如果该请求为GET请求并且设有请求参数,返回路由地址就会将请求参数返回,如/?user=xy&pw=123
  • get_raw_uri(): 获取完整的网址信息,将服务器的域名,端口和路由地址一并返回。

代码实现如下:

#MyDjango/urls.py
from django.urls import path, re_path, include
urlpatterns = [
    path('', include(('index.urls', 'index'), namespace='index')),
]

#index/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('',  views.index, name='index'),
]

#index/views.py
from django.shortcuts import render

def index(request):
    if request.method == 'GET':
        #类方法的使用
        print(request.is_secure())
        print(request.is_ajax())
        print(request.get_host())
        print(request.get_full_path())
        print(request.get_raw_uri())
        #属性的使用
        print(request.COOKIES)
        print(request.content_type)
        print(request.contnet_params)
        print(request.scheme)
        #获取GET的请求参数
        print(request.GET.get('user', ''))
        return render(request, 'index.html')
    elif request.method == 'POST':
        print(request.POST.get('user', ''))
        return render(request, 'index.html')
    
#templates/index.html
<!DOCTYPE html>
<html>
<body>
    <h3>Hello World</h3>
    <form action="" method="POST">
        #Django 的 CSRF防御机制
        {% csrf_token %}
        <input type="text" name="user"/>
        <input type="submit" name="user"/>
    </form>
</body>
</html>

视图函数index的参数request是类WSGIRequest的实例化对象,通过参数request的method属性来判断HTTP请求方式。当在浏览器访问127.0.0.1:8000/?user=xy&pw=123时,相当于向Django发送GET请求,从PyCharm的查看输出情况

2.文件上传功能

文件上传功能是网站开发最常见的功能之一,比如上传图片和导入文件,无论上传的文件是什么格式的,其上传原理是将文件以二进制数据格式读入,并写入网站指定的文件夹里。

#index/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('',  views.upload, name='upload'),
]

#index/views.py
from django.shortcuts import render
from django.http import HttpResponse
import os

def upload(request):
    #request method == 'post',execute upload
    if request.method == "POST":
        #get file
        myFile = request.FILES.get("myfile", None)
        if not myFile:
            return HttpResponse("no files for upload")
        #open as b write
        f = open(os.path.join("/Users/zhouyuchen/BRICKS/upload"), 'wb+')
        #block
        for chunk in myFile.chunks:
            f.write(chunk)
        f.close()
        return HttpResponse("upload over")
    else:
        return render(request, 'upload.html')

#index/upload.html
<!DOCTYPE html>
<html>
<body>
<form enctype="multipart/form-data" action="" method="post">
    #defense
    {% csrf_token %}
    <input type="file" name="myfile"/>
    <br>
    <input type="submit" value="上传文件"/>
</form>
</body>
</html>

3.Cookie实现反爬虫

浏览器向服务器发送请求,服务器作出响应之后,二者会便会断开连接下次用户再来请求服务器服务器没有办法识别此用户是谁。Cookie 是从浏览器向服务器传递数据,让服务器能够识别当前用户,而服务器对cookie的识别机制,是通过session实现的先生存储了当前用户的基本信息。 Cookie除了解决http协议无状态的弊端之外,还可以利用cookie实现反爬虫机制,随着大数据和人工智能的发展,爬虫技术日益完善网站。为了保护自身数据的安全性和负载能力,都会在网站里设置反爬虫机制。

4.请求头实现反爬虫

Django获取请求是有固定格式的,必须为“HTTP_XXX”,其中XXX代表请求头的某个属性,而且必须为大写字母。 一般情况下,自定义请求头必须有一套完整的加密机制前端的AJAX负责数据加密服务器负责数据解密,从而提高爬虫开发者的破解难度。

第5章探究CBV视图

5.1数据显示视图

数据显示视图是将后台的数据展示在网页上,数据主要来自模型,一共定义了四个试图类分别是RedirectView、TemplateView、ListView和DetailView,说明如下:

  • RedirectView用于实现HTTP重定向,默认情况只定义GET请求的处理方法
  • TemplateView是视图类的基础视图,可将数据传递给HTML模版,默认情况下只定义GET请求的处理方法
  • ListView是在TemplateView的基础上将数据以列表显示,通常将某个数据表的数据以列表表示
  • DetailView是在TemplateView的基础上将数据详细显示,通常获取表的单挑数据

1.重定向视图RedirectView

  • permanet:根据属性值的真假来选择重定向方式,若为True,则状态码为302,否则状态码为301
  • url: 代表重定向的路由地址
  • pattern_name: 代表重定向的路由命名,如果已设置参数URL则无需设置该参数,否则提示异常信息
  • query_string: 是否将当前路由地址的请求参数传递到重定向的路由地址
  • get_redirect_url(): 根据属性pattern_name所指向的路由命名来生成相应的路由地址。
  • get():触发HTTP的GET请求所执行的响应处理
  • 剩余的类方法head()、post()、options()、delete()、put()和patch()是HTTP的不同请求方式,他们都由get()方法完成响应处理

以MyDjango为例,在index/urls.py、views.py和templates/index.html中编写代码

#index/urls.py
from django.urls import path
from .views import *

urlpatterns = [
    path('', index, name='index'),
    path('turnTo', trunTo.as_view(), name='turnTo')
]

#index/views.py
from django.shortcuts import render
from django.views.generic import RedirectView

def index(request):
    return render(request, 'index.html')

class turnTo(RedirectView):
    permanent = False
    url = None
    pattern_name = 'index:index'
    query_string = True

    def get_redirect_url(self, *args, **kwargs):
        print('This is a get_redirect_url')
        return super().get_redirect_url(*args, **kwargs)

    def get(self, request, *args, **kwargs):
        print(request.META.get('HTTP_USER_AGENT'))
        return super().get(request, *args, **kwargs)
    
#templates/index.html
<!DOCTYPE html>
<html>
<body>
    <h3>Hello RedirectView</h3>
    <a href="{% url 'index:turnTo' %}?k=1">ToTurn</a>

</body>
</html>

在index的views.py中定义了视图类turnTo,它继承父类RedirectView,对父类的属性进行重设,并将父类的类方法get_redirect_url()和get()进行重写,通过这样的方式可以对视图类进行功能扩展。定义路由的时候,若使用视图类turnTo进行实例化处理。as_view()方法可在类View里找到具体的定义过程

2.基础视图TemplateView

视图类TemplateView是所有视图类最基础的应用视图类,开发者可以直接调用应用视图类,它继承多个父类:TemplateResponseMixin、ContextMixin和View。

从视图类Template的源码可以看到,它只定义了类方法get(),该方法分别调用函数方法get_ocntext_data()和render_to_response(),从而完成HTTP请求的响应过程。类方法get()所调用的函数方法主要来自父类TemplateResponseMixin和ContextMixin

TemplateView.get():

  • 视图类ContextMixin的get_context_data()方法用于获取模版上下文内容,模版上下文是将视图类里的数据传递到模版文件,再由模版文件将数据转换成HTML网页数据
  • 视图类TemplateResponseMixin的render_to_response()用于实现响应处理,由响应类TemplateResponse完成

我们可以在视图类TemplateView的源码文件里找到视图类TempalteResponseMixin的定义过程:

  • template_name:设置模版文件的文件名
  • template_enginee:设置解析模版文件的模版引擎
  • response_class:设置HTTP请求的响应类,一般情况下使用默认值即可
  • Render_to_response():实现响应处理,由响应类TemplateResponse完成
  • get_template_names():获取属性template_name的值

实现代码如下:

#index/urls.py
from django.urls import path
from .views import *

urlpatterns = [
    path('', index.as_view(), name='index')
]

#
from django.views.generic.base import TemplateView
class index(TemplateView):
    template_name = 'index.html'
    template_engine = None
    content_type = None
    extra_context = {'title' : 'This is GET'}
    
    #override
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['value'] = 'I am MyDjango'
        return context
    
    #def http post method
    def post(self, request, *args, **kwargs):
        self.extra_context = {'title': 'This is POST'}
        context = self.get_context_data(**kwargs)
        return self.render_to_response(context)
    
    
#templates/index.html
<!DOCTYPE html>
<html>
<body>
    <h3>Hello RedirectView</h3>
    <div>{{ value }}</div>
    <br>
    <form action="" method="post">
        {% csrf_token %}
        <input type="submit" value="Submit">
    </form>
</body>
</html>

上述代码是将网站首页的视图函数index改为视图类index,自定义视图类index继承视图类TemplateView:

  • template_name:将模版文件index.html作为网页文件
  • template_engine:设置解析模版文件的模版引擎,默认值为None,即默认使用配置文件settings.py的TEMPLATE所设置的模版引擎BACKEND
  • content_type:设置响应内容的数据格式,默认值为None,即代表数据格式为text/html
  • extra_context:为模版文件的上下文(模版变量)设置变量值,可将数据转换成网页数据展示在浏览器上
  • get_context_data():继承并重写视图类TemplateView的类方法,在变量context里新增数据value
  • post():自定义POST请求的处理方法,当触发POST请求时,将会重设属性extra_context的值,并调用get_context_data()将属性extra_context重新写入,从而实现动态改变模版上下文的数据内容

3.列表视图ListView

可以看出ListView继承自TemplateResponseMixin、MultipleObjectMixin,和View,在这些底层类的基础上加入了模型的操作方法,所以ListView有以下属性和方法:

  • Allow_empty:由MultipleObjectMixin定义,在模型查询数据不存在的情况下是否显示页面,若为False并且数据不存在,则引发404异常,默认值为True
  • queryset:由MultipleObjectMixin定义,代表模型的查询对象,这是对模型对象进行查询操作所生成对查询对象
  • model:由MultipleObjectMixin定义,代表模型,模型以类表示,一个模型代表一张数据表
  • paginate_by:由MultipleObjectMixin定义,属性值为整数,代表每一页所显示的数据量
  • paginate_orphans:由MultipleObjectMixin定义,属性值为整数,默认值为0,代表最后一页可以包含的“溢出”的数据量,防止最后一页的数据量过少
  • context_object_name:由MultipleObjectMixin定义,设置模版上下文,即为模版变量进行命名
  • paginator_class:由MultipleObjectMixin定义,设置分页的功能类,默认情况下使用内置分页功能django.core.paginator.Paginator
  • page_kwargs:由MultipleObjectMixin定义,属性值为字符串,默认值为page,设置分页参数的名称
  • ordering:由MultipleObjectMixin定义,属性值为字符串或字符串列表,主要对属性queryset的查询结果进行排序
  • get_queryset():由MultipleObjectMixin定义,获取属性queryset的值
  • get_ordering():由MultipleObjectMixin定义,获取属性ordering的值
  • paginate_queryset():由MultipleObjectMixin定义,根据属性queryset的数据来进行分页处理
  • get_paginate_by():由MultipleObjectMixin定义,获取每一页所显示的数据量
  • get_paginator():由MultipleObjectMixin定义,返回当前页数所对应的数据信息
  • get_paginate_orphans():由MultipleObjectMixin定义,获取最后一页可以包含的“溢出”的数据量
  • get_allow_empty():由MultipleObjectMixin定义,获取属性allow_empty的属性值
  • get_context_object_name():由MultipleObjectMixin定义,设置模版上下文(模版变量)的名称,若context_object_name未设置,则上下文名称将由模型名称的小写+'_list'表示,比如模型PersonInfo,其模版上下文的名称为personinfo_list
  • get_context_data():由MultipleObjectMixin定义,获取模版上下文(模版变量)的数据内容
  • template_name_suffix:由MultipleObjectTemplateResponseMixin定义,设置模版后缀名,用于设置模版的模版文件
  • get_template_names():由MultipleObjectTemplateResponseMixin定义,获取属性template_name的值
  • get():由BaseListView定义,定义HTTP的GET请求的处理方法

由于视图类ListView需要使用模型对象,因此在MyDjango项目里定义PersonInfo模型,在index/models.py中编写一下代码

#index/models.py
from django.db import models

class PersonInfo(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=20)
    age = models.IntegerField()

上述代码只是搭建PersonInfo类和数据表personinfo的映射关系,但在数据库并没有生成响应的数据表。因此,下一步通过两者的映射关系在数据库里生成响应的数据表。以MySQL3为例输入指令

python manage.py makemigrations
python manage.py migrate

完成上述操作后,下一步在MyDjango里使用视图类ListView,在index的views.py里定义视图类index,并重新编写模版文件index.html代码:

#index/views.py
from django.views.generic import ListView
from .models import PersonInfo

class index(ListView):
    template_name = 'index.html'
    extra_context = {'title' : '人员信息表'}
    queryset = PersonInfo.objects.all()
    paginate_by = 1
<!DOCTYPE html>
<html>
<head>
    <title>{{ title }}</title>
</head>
<body>
    <h3>{{ title }}</h3>
    <table border="1">
        {% for i in personinfo_list %}
        <tr>
            <th>{{ i.name }}</th>
            <th>{{ i.age }}</th>
        </tr>
        {% endfor %}
    </table>
    <br>
    {% if is_paginated %}
    <div class="pagination">
        <span class="page-links">
            {% if page_obj.has_previous %}
            <a href="/?page={{ page_obj.previous_page_number }}">上一页</a>
            {% endif %}
            {% if page_obj.has_next %}
            <a href="/?page={{ page_obj.next_page_number}}">下一页</a>
            {% endif %}
            <br>
            <br>
            <span class="page-current">
                第{{ page_obj.number }}页,
                共{{ page.paginator.num_pages }}页.
            </span>
        </span>
    </div>
    {% endif %}
</body>
</html>

视图类index继承父类ListView,并且仅设置4个属性就能完成模型数据的展示。视图类ListView 虽然定义了多个属性和方法,但是大部分的属性和方法已有默认值和处理过程,这些就能满足日常的开发需求,上述的试图类index仅支持http的get请求处理,因为父类ListView有自定义的get方法,如果想让视图类index能也能够处理POS的请求,那么只需在该类下自定义POS T的方法即可。

4.详细视图

视图类DetailView的底层类由TemplateResponseMixin、ContextMixin和View组成:

  • template_name_field:由SingleObjectTemplateResponseMixin定义,用于确定模版的名称
  • template_name_suffix:由SingleObjectTemplateResponseMixin定义,设置模版后缀名,默认后缀是_detail,用于设置默认模版文件
  • get():由BaseDetailView定义,定义HTTP的GET请求的处理方法
  • model:由SingleObjectMixin定义,代表模型,模型以类表示,一个模型代表一张数据表
  • queryset:由SingleObjectMixin定义,这是对模型对象进行查询操作所生产的查询对象
  • context_object_name:由SingleObjectMixin定义,设置模版上下文,即为模版变量进行命名
  • slug_field:由SingleObjectMixin定义,设置模型的某个字段作为查询对象,默认值为slug
  • slug_url_kwarg:由SingleObjectMixin定义,代表路由地址的某个变量,作为某个模型字段的查询范围,默认值为slug
  • query_pk_and_slug:由SingleObjectMixin定义,若为True,则使用pk_url_kwarg和slug_url_kwarg同时对模型进行查询,默认值为False
  • get_object():由SingleObjectMixin定义,对模型进行单条数据查询操作
  • get_queryset():由SingleObjectMixin定义,获取属性queryset的值
  • get_slug_field():由SingleObjectMixin定义,根据属性slug_field查找与之对应的数据表字段
  • get_context_object_name():由SingleObjectMixin定义,设置模版上下文的名称,若context_object_name未设置,则上下文名称由模型名称的小写表示
  • get_context_data():由SingleObjectMixin定义,获取模版上下文的数据内容

代码实现:

#index/urls.py
from django.urls import path
from .views import *

urlpatterns = [
    path('<pk>/<age>.html', index.as_view(), name='index')
]

#index/views.py
from django.views.generic import DetailView
from .models import PersonInfo

class index(DetailView):
    template_name = 'index.html'
    extra_context = {'title' : '人员信息表'}
    slug_field = 'age'
    pk_url_kwarg = 'pk'
    model = PersonInfo
<!DOCTYPE html>
<html>
<head>
    <title>{{ title }}</title>
</head>
<body>
    <h3>{{ title }}</h3>
    <table border="1">
        <tr>
            <th>{{ personinfo.name }}</th>
            <th>{{ personinfo.age }}</th>
        </tr>
    </table>
    <br>
</body>
</html>

路由index设有两个路由变量pk和age这两个变量为模型PersonInfo的字段id和age提供查询范围。视图类index的属性model以模型PersonInfo作为查询对象;属性pk_url_kwarg和slug_url_kwarg用于获取指定的路由变量。

5.2数据操作视图

数据操作是图示对模型进行操作,如增、删、改,从而实现Django与数据库的数据交互,操作视图有四个视图类:

  • FormView视图类使用内置的表单功能,通过表单实现数据验证、响应输出等功能,用于显示表单数据
  • CreateView实现模型的数据新增功能,通过内置的表单功能实现数据新增
  • UpdateView实现模型的数据修改功能,通过内置的表单功能实现数据修改
  • DeleteView实现模型的数据删除功能,通过内置的表单功能实现数据删除

1.表单视图FormView

视图类FormView是表单在视图里的一种使用方式,表单是收集用户信息的各种表单元素的集合,作用是实现网上的数据交互,用户在网站输入数据信息,然后提交到网站服务器端进行处理,如数据录入和用户登录注册等。

视图类FormView的底层是由TemplateResponseMixin、ContextMixin和View组成的,设计模式和其他视图类十分相似。

  • initial:由FormMixin定义,设置表单初始化的数据
  • form_class:由FormMixin定义,设置表单类
  • success_url:由FormMixin定义,设置重定向的路由地址
  • prefix:由FormMixin定义,设置表单前缀(即表单在模版的上下文),可在模版里生成表格数据
  • get_initial():由FormMixin定义,获取表单初始化的数据
  • get_prefix():由FormMixin定义,获取表单的前缀
  • get_form_class():由FormMixin定义,获取表单类
  • get_form():由FormMixin定义,调用get_form_kwargs()完成表单类的实例化
  • get_form_kwargs():由FormMixin定义,执行表单实例化的过程
  • get_success_url():由FormMixin定义,获取重定向的路由地址
  • form_valid():由FormMixin定义,表单有效将重定向到指定的路由地址
  • get_context_data():由FormMixin定义,获取模版上下文(模版变量)的数据内容
  • get():由ProcessFormView定义,定义HTTP的GET请求的处理方法
  • post():由ProcessFormView定义,定义HTTP的POST请求的处理方法

代码实现如下:

#index/urls.py
from django import forms
from .models import PersonInfo
class PersonInfo(forms.ModelForm):
	#
    class Meta:
        model = PersonInfo
        fields = '__all__'
#index/urls.py
from django.urls import path
from .views import *

urlpatterns = [
    path('', index.as_view(), name='index'),
    path('result', result, name='result')
]

#index/views.py
from django.views.generic.edit import FormView
from .form import PersonInfoForm
from django.http import HttpResponse

def result(request):
    return HttpResponse('Success')
    
class index(FormView):
    initial = {'name': 'Betty', 'age': 20}
    template_name = 'index.html'
    success_url = '/result'
    form_class = PersonInfoForm
    extra_context = {'title': '人员信息表'}
<!DOCTYPE html>
<html>
<head>
    <title>{{ title }}</title>
</head>
<body>
    <h3>{{ title }}</h3>
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value="确定">
    </form>
</body>
</html>

上述代码是视图类FormView的简单应用,它涉及模型和表单的使用,说明:

  • index的form.py里定义了表单类PersonInfoForm,该表单是根据模型PersonInfo定义的模型表单,表单的字段来自模型的字段。
  • 路由index的请求处理由视图类FormView完成,而路由result为视图类index的属性success_url提供路由地址
  • 视图类index仅设置了5个属性,属性extra_context的值对应模版的上下文title;属性form_class所设置的表单在实例化之后可在模版里使用上下文form.as_p生成表格,模版上下文form的命名是固定的,它来自类FormMixin的get_context_data()
  • 在网页上单击“确定”按钮,视图类index就会触发父类FormView所定义的post()方法,然后调用表单内置的is_vaild()方法对表单数据进行验证。

2.新增视图CreateView

CreateView的底层类是由TemplateResponseMixin、ContextMixin和View组成的,整个设计共继承10个类。

  • field:由ModelFormMixin定义,设置模型字段,以列表表示,每个字段代表一个列表元素,可生成表单的数据列表,为用户提供数据输入
  • get_form_class:由ModelFormMixin定义,重写FormMxin的方法,根据属性fields和form_class的组合情况进行判断,从而选择表单的生成方式
  • get_form_kwargs():由ModelFormMixin定义,重写FormMixin的方法
  • get_success_url():由ModelFormMixin定义,重写FormMixin的方法,判断属性success_url是否为空,若为空,则从模型的内置方法get_absolute_url()获取重定向的路由地址
  • form_vaild():由ModelFormMixin定义,重写FormMixin的表单验证方法, 新增表单数据表保存到数据库的功能
  • template_name_suffix:由ModelFormMixin定义, 设置模板的后缀名,用于设置默认的模板文件。

视图类CreateView有两种表单的生成方式。第一种是设置form_class,通过属性form_class指定表单对象,这种方式需要开发者自定义表单对象;第二种是设置属性model和fields,由模型对象和模型字段来生成响应的表单对象,生成的表单字段与模型的字段要求相符,可以减少异常情况,并无须开发者自定义表单对象

实现代码

#index/views.py
from django.views.generic.edit import CreateView
from .form import PersonInfoForm
from .models import PersonInfo
from django.http import HttpResponse


def result(request):
    return HttpResponse('Success')


class index(CreateView):
    initial = {'name': 'Betty', 'age': 20}
    template_name = 'index.html'
    success_url = '/result'
    #
	form_class = PersonInfoForm
    extra_context = {'title' : '人员信息表'}

运行项目,然后在MySQL中查询表的数据

3.修改视图

代码实现:

#index/urls.py
from django.urls import path
from .views import *

urlpatterns = [
    path('<age>.html', index.as_view(), name='index'),
    path('result', result, name='result')
]

#index/views.py
from django.views.generic.edit import UpdateView
from .models import PersonInfo
from django.http import HttpResponse


def result(request):
    return HttpResponse('Success')

class index(UpdateView):
    template_name = 'index.html'
    success_url = '/result'
    model1 = PersonInfo
    fields = ['name', 'age']
    slug_url_kwarg = 'age'
    slug_field = 'age'
    context_object_name = 'personinfo'
    extra_context = {'title' : '人员信息表'}
<!DOCTYPE html>
<html>
<head>
    <title>{{ title }}</title>
</head>
<body>
    <h3>{{ title }} - {{ personinfo.name }}</h3>
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value="确定">
    </form>
</body>
</html>

在网页中修改zs的年龄,然后再去mysql中查看信息