日志一我们学习了Django的配置,详见:
Django学习日志一Django的配置
日志二我们学习了创建第一个django项目,详见:
Django学习日志二——创建第一个django项目
日志三我们学习了django模型层,详见:
Django学习日志三:模型层
下面我们来学习视图层的使用:
Django遵循MVT模式,模型的核心其实是通过用户从浏览器中访问某个URL地址,来告诉Django项目需要使用哪个函数进行业务逻辑处理,而函数处理完对应的业务逻辑后再返回对应的结果并渲染到浏览器中。
include()函数
hello项目中的setting.py文件有一个参数ROOT_URLCONF=‘hello.urls’,该参数指定了hello项目中的urls.py为URL的根模块,该根模块用于维护URL调度器,ROOT_URLCONF参数可根据实际情况修改。/hello/urls.py文件代码如下:
urlpatterns = [
path('admin/', admin.site.urls),
path('app/', include('app.urls'))
]
在Django项目中,一个项目可能包含多个Django应用app,而每个app都有自己的URL映射规则,这时候如果将所有的URL映射都写在URL根模块中会非常不利于网站的开发和维护,所以在Django中可以使用include等其他URLconf模块,从而使不同应用app可以维护自己的URL映射规则。
URL匹配规则
当用户访问以app/开头的URL的时候,请求的URL会被转到hello/app/urls.py中,在hello/app/urls.py中可以找到URL映射规则对应的视图函数。
from django.contrib import admin
from django.urls import path, re_path
from app import views
urlpatterns = [
path(r'app/student/1801', views.student_1801),
path(r'app/student//' , views.year_student),
path(r'app/student///' , views.month_student),
re_path(r'^app/student/(?P\d+)/(?P\d+)/(?P\d+)/$' , views.detail_student),
]
代码分析:
Diango在匹配URL规则的时候,会查询urlpatterns参数,该参数是一个包含django.urls.path()函数或django.urls.re_path()函数的实例对象列表。
django.urls.path()函数是Django 2.0新增的函数,它可以使用一种更加简洁的,可读的路由语法,该语法和flask中定义路由的语法类似。
django.urls.re_path()函数替换了Diango 2.0之前版本中的django.conf.urls.url()函数,在定义的正则表达式中,命名式分组语法为(?P
Django会按照正则匹配的方式,依次匹配每个URL模式,并在第一个与请求的URL匹配的地方停下来,即便后面还有符合匹配规则的URL也会被直接忽略。如果没有匹配到URL 规则,则直接抛出异常,Django会返回一个错误页面。
每个正则表达式前面的“r”是可选的,建议加上,它向 Python解释器说明这个字符串不需要转移。
在每个正则表达式中“^”和“$”分别为匹配开始和匹配结束的标识。
当请求地址为http://127.0.0.1:8000/app/student/1801/时,Django会匹配URL规则中的路由地址,并调用函数student_1801(request, ‘1801’)。
当请求地址为http://127.0.0.1:8000/app/student/2022/时,Django会匹配列表中的第2个匹配规则,并调用函数views.year_student(request, 2018’)。
当请求地址为http://127.0.0.1:8000/app/student/2022/03/时,Django会匹配列表中的第3个匹配规则,并调用函数views.month_ student(request, ‘2018’, ‘10’)。
当请求地址为http://127.0.0.1:8000/app/student/2022/03/1/时,Django会匹配列表中的第4个匹配规则,在匹配规则中会对参数进行命名,接收的第1个参数为“2018”,可以通过year变量来获取;接收的第2个参数为“10”,可以通过month变量来获取;接收的第3个参数为“1”,可以通过day变量来获取。调用的的函数为views.detail_student(request, year, month, day)。
模式 | 描述 |
---|---|
^ | 匹配字符串的开头 |
$ | 匹配字符串的末尾 |
. | 匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符在内的任意字符 |
[…] | 用来表示一组字符,单独列出:[amk]匹配“a”、“m”或“k” |
[^…] | 不在[]中的字符:[^abc]匹配除了a、b、c之外的字符 |
re* | 匹配0个或多个的表达式 |
re+ | 匹配1个或多个的表达式 |
re? | 匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式 |
re{n} | 精确匹配n个前面的表达式,例如,o{2}不能匹配"Bob"中的"o",但是能匹配"food"中的两个o |
re{n,} | 匹配n个前面的表达式,例如,o{2,}不能匹配"Bob"中的"o",但是能匹配"foooood"中的所有的o。"o{1,}“等价于"o+”。"o{0,}“等价于"o*”。 |
re{n,m} | 匹配n到m次由前面的正则表达式定义的片段,贪婪方式 |
a|b | 匹配a或b |
(re) | 匹配括号内的表达式,也表示一个组 |
(?imx) | 正则表达式包含3种可选标志:i、m或x,只影响括号中的区域 |
(?-imx) | 正则表达式关闭i、m或x可选标志,只影响括号种的区域 |
(?:re) | 类似(…),但是不表示一个组 |
(?imx:re) | 在括号中使用i、m或x可选标志 |
(?imx:-re) | 在括号中不使用i、m或x可选标志 |
\w | 匹配字母、数字及下划线 |
\W | 匹配非字母、数字及下划线 |
\s | 匹配任意空白字符,等价于[\t\n\r\f] |
\S | 匹配任意非空字符 |
\d | 匹配任意数字,等价于[0-9] |
\D | 匹配任意非数字 |
\A | 匹配字符串开始 |
\Z | 匹配字符串结束,如果存在换行,则只匹配到换行前的结束字符串 |
\z | 匹配字符串结束 |
\G | 匹配最后匹配完成的位置 |
\b | 匹配一个单词边界,也就是指单词和空格之间的位置。例如,'er\b’可以匹配"never"中的"er",但不能匹配"verb"中的"er" |
\B | 匹配非单词边界。“er\B"能匹配"verb"中的"er”,但不能匹配"never"中的"er" |
\n,\t等 | 匹配一个换行符,匹配一个制表符 |
\1…\9 | 匹配第n个分组的内容 |
\10 | 匹配第n个分组的内容,如果匹配不成功,则表示八进制字符码的表达式 |
视图函数简称视图,是MVT模式中最核心的V所代表的模块。视图其实就是Python函数,并且统一在应用app的view.py文件中定义。不管视图函数的业务逻辑是什么,都必须接受一个HttpResponse对象,响应对象可以是网页HTML源码、重定向、图片、文档等。
示例:
在hello/app/urls.py中定义URL匹配规则:
from django.contrib import admin
from django.urls import path, re_path
from app import views
urlpatterns = [
re_path(r'^app/student/(?P\d+)/(?P\d+)/(?P\d+)/$' , views.detail_student),
# 或者
# path(r'app/student////', views.detail_student),
]
在hello/app/views.py中定义视图函数:
from django.shortcuts import render
from django.http import HttpResponse
def detail_student(request, year, month, day):
if request.method == 'GET':
return HttpResponse('学生入校的时间为年份:{}月份:{}日期:{}'.format(year, month, day))
在上述示例中,定义了一个正则表达式来匹配app/student/2022/3/4这样的URL,在视图函数中,year变量的值等于2022,month的值为3,day的值为4。通过URL提供参数,在后面的处理业务逻辑的时候非常方便。detail_student()函数中通过requests.method来判断HTTP请求方式,并返回HttpResponse()响应,响应是字符串,也可以是render()渲染一个页面。
当Web服务器接收到来自页面的一个HTTP请求的时候,Django会把Web服务器传过来的请求转化为一个请求对象。在视图函数中,可以从请求Request中获取参数,随后视图会创建并返回一个响应对象Response,Django会将这个响应对象Response转化为Web服务器可以接收的格式,并将响应发送给客户端。
请求Request
在程序中可以通过request.GET和request.POST来判断当前HTTP的请求方式。request.GET和request.POST的结果为QueryDict类型,它和Python中的dict非常类似,二者都是以键值对的形式存储数据,存取数据的语法和字典的语法也是一样的,但是在QueryDict中键值对的键可以重复。在QueryDict类型中可以使用getlist方法来获取这些重复的键对应的值。
示例:QueryDict取值,并返回响应对象
# 在urls.py中配置路由
from django.contrib import admin
from django.urls import path, re_path
from app import views
urlpatterns = [
path(r'student/', views.student),
# 在views.py文件中,定义视图函数student()
from django.shortcuts import render
from django.http import HttpResponse
def student(request):
if request.method == 'GET':
names = request.GET.getlist('name')
print(type(request.GET))
print(names)
return HttpResponse(names)
在浏览器中输入启动的IP:http://127.0.0.1:8080/student/?name=张三&name=小明&name=小花,在控制台中可以看到如下输出:
['张三', '小明', '小花']
同时在浏览器上,也出现了一串字符串:张三小明小花
分析request的请求:
判断HTTP的请求方式,请求request中有一个属性method,可以通过该属性判断用户发送的HTTP请求是GET还是POST请求。
request.GET的结果为QueryDiet类型,可以使用字典来获取其中的键值对,但使用字典get()方法会更好。例如,要获取一个URL中传递的page参数,可以使用request.GET[‘page’]和request.GET.get(‘page’)两种方式,但是如果要查找的参数并没有在URL中指定,使用request.GET[‘page’]方法会报错,而使用request.GET.get(‘page’)不会报错,只会返回一个None值。
如果使用request.GET.get(‘name’)方式获取URL中传递的name参数,只能获取到一个,这时候必须使用getlist方法,即request.GET.getlist(‘name’)来获取。
在获取到URL中传递的name参数以后,将变量names作为响应的数据传递给客户端。
响应Response
示例:在urls.py文件中定义路由:
from django.contrib import admin
from django.urls import path, re_path
from app import views
urlpatterns = [
path(r'response_html/', views.response_html),
]
在views.py文件中定义视图函数,响应返回HTML界面:
from django.shortcuts import render
from django.http import HttpResponse
def response_html(request):
if request.method == 'GET':
res = HttpResponse()
res.write('')
res.write('')
res.write('')
res.write('人生苦短,我用Python
')
res.write('')
res.write('')
return res
在构建返回给客户端的HTML字符串时,可以使用write()方法一点一点地构建响应内容。响应对象里还可以设置Cookie、删除Cookie,以及设置请求头等信息。
请求request与响应response的属性与方法介绍如下:
属性 | 描述 |
---|---|
path | 字符串类型,表示请求页面的完整路径,不包含域名。 |
method | 字符串类型,表示请求使用的HTTP方法,常用值包括GET、POST。 |
ncoding | 字符串类型,表示提交的数据的编码方式。 |
GET | 类字典QueryDiet对象,包含GET请求方式的所有参数。 |
POST | 类字典QueryDiet对象,包含POST请求方式的所有参数。 |
FILES | 类字典MultiValueDiet对象,包含所有的上传文件。 |
COOKIES | 一个标准的Python字典,包含所有的Cookie,键和值都为字符串。 |
Session | 可读可写、类似于字典的对象,表示当前的会话,只有当Django启用会话时才可用。 |
user | 默认为AnonymousUser对象,是Django的认证用户。在重构用户的登录功能时用于获取当前登录用户信息,中间件会使用到该属性。 |
方法 | 描述 |
---|---|
is_ajax | 如果请求是通过XMLHttpRequest发起的,则返回True。 |
属性 | 描述 |
---|---|
content | 字符串类型,表示返回的内容。 |
charset | 字符串类型,表示response采用的编码字符集。 |
status_code | 表示HTTP响应状态码。 |
方法 | 描述 |
---|---|
write(content) | 以文件的方式写。 |
flush() | 以文件的方式输出缓存区。 |
set_cookie(key,value=’’,max_age=None,expires=None) | 设置Cookie、key、value都是字符串类型。 max_age是一个整数,表示在指定秒数后过期; expires是一个datetime或timedelta对象,表示会话将在这个指定的日期/时间过期; max_age与expires二选一,如果不指定过期时间,则关闭浏览器时失效。 |
delete_cookie(key) | 删除指定的key的Cookie,如果key不存在则什么也不发生。 |
在视图函数中会接收一个请求request,并返回一个响应response,这个响应对象可以不是常用的HttpResponse对象,可以为HttpResponseRedirect对象,该对象只接收一个参数,即用户将要重定向的URL地址。HttpResponseRedirct的构造函数中可以使用reverse()函数,这个函数可以避免我们在视图函数中硬编码URL,reverse()函数只需要我们提供跳转的namespace命名空间和该视图函数对应的URL模式中的name参数。
语法如下:
HttpResponseRedirect()
简化语法:
redirect()
常用的重定向函数可以传递的参数有相对地址和绝对地址,还可以通过反查得到URL,即reverse(‘namespace:name’)。重定向的使用,分为以下4个步骤。
在/hello/urls.py中定义namespace命令空间
from django.urls import path, include
from app import views
urlpatterns = [
path('app/', include(('app.urls', 'app'), namespace='app/')),
]
在/app/urls.py中定义name参数
from django.urls import path, re_path
from app import views
urlpatterns = [
path(r'redirect_stu/(?p\d+)/' , views.redirect_stu, name='red_tu'),
re_path(r'student/(\d+)/', views.sel_one_student),
]
在/hello/app/views.py中定义视图函数
from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse
from app.models import Student
def sel_one_student(request, g_id):
return HttpResponseRedirect(
reverse('app:red_tu', kwargs={'g_id': g_id})
)
def redirect_stu(request, g_id):
if request.method == 'GET':
stus = Student.objects.filter(id=g_id)
return HttpResponse('查询所有学生信息')
# 获取query对象某一行特定值可以使用stus.values()[0]['s_name']
在浏览器中访问地址http://127.0.0.1:8080/app/student/1/,即可看到输出的信息;查询所有学生的信息。
在代码中使用硬编码URL,即直接写为HttpResponseRedirect(/app/student/)时,’/app/student/'这种硬编码和强耦合的链接地址,对于一个包含很多应用的项目来说修改起来十分困难。
为了解决这个问题,可以使用namespace和name参数,其中name参数时为URL定义的名字,现在跳转的地址就可以更改为HttpResponseRedirect(reverse(‘app:red_tu’, kwargs={‘g_id’:g_id})),且将返回一个URL地址’/app/redirect_student/’。
在reverse()函数中还有一个字典参数kwargs,使用reverse(‘app:red_tu’,kwargs={‘g_id’:g_id})将会返回字符串’/app/redirect_student/[g_id]’,其中[g_id]为整型参数,并且指定了该参数的变量名为g_id。
在请求request中,最常用的就是request.COOKIES,它是一个字典类型的数据,存储着请求HTTP中的Cookie信息。由于Cookie存储在客户端,其中的持久化的数据很容易被篡改,所以存储在Cookie中的数据很不安全,因此引入了更安全的Session。
Cookie
Cookie用于在客户端持久化数据信息,当客户端向服务端发送请求后,服务端可以从request.COOKIES中获取到客户端Cookie中的信息,并在Cookie中记录某些信息再返回给客户端。
Cookie语法如下:
- 设置Cookie
set_cookie(key, value, max_age='', expires='')
Cookie是以字典的形式存储数据的,参数中key和value分别表示键值对,而max_age表示Cookie的生效时间(单位为秒),expires表示过期日期。max_age与expires参数二选一。
- 删除Cookie
delete_cookie(key)
示例:Cookie的使用
# 定义res为HttpResponse对象
# 设置Cookie
res.set_cookie('login_status','1',max_age=100)
res.set_cookie('name','海飞',expires=60 * 60 * 24)
# 删除Cookie
res.delete_cookie('name')
# 从请求request中获取Cookie
request.COOKIES.get('name')
expires格式可以为:
1.时间格式的字符串 : " Wdy, DD-Mth-YY HH:MM:SS GMT "
2.秒数
3.datetime.datetime 对象
例:
expires = 'Thu, 15-Mar-2022 20:11:06 GMT' # 24小时 格林威治时间
expires = 60 * 60 * 24
expires = datetime.datetime(2022, 3, 15, 20, 11, 06))
Session
Session是将数据保存在服务端的数据库中,并在可无端的Cookie中加入一个随机字符串Sessionid值。至此Cookie和Session的配合使用可以解决HTTP无状态的问题,例如,网站的用户认证系统。
Session语法如下:
(1)设置Session中的数据:
request.session['name']=value
(2)删除Session中的数据:
del request.session['name']
request.session.pop('name')
(3)获取Session中的数据:
request.session['name']
(4)获取Sesion中的随机字符串:
request.session.session_key
(5)删除当前的会话数据,并删除会话的Cookies:
request.session.flush()
(6)删除当前Session中的所有数据:
request.session.delete(request.session.session_key)
(7)设置Session过期时间:
request.session.set_expiry(value)
value值可以是秒数,也可以是datatime类型的日期
用户认证系统的Cookie和Session的应用步骤如下:
步骤一:用户使用用户名和密码进行登录认证,如果认证成功,则返回一个response响应,并绑定Cookie。Cookie中需设置一个键值对,键为Sessionid,值为随机字符串。
步骤二:服务端以发送给客服端Cookie中的随机字符串为键,以用户的基本信息为值,将数据保存起来。
步骤三:当用户下次发送URL请求时,服务端可以通过Cookie中的随机字符串,找到在服务端中保存的用户信息。
示例:用户认证系统的工作流程
在urls.py文件中定义路由:
from django.urls import path
from app import views
urlpatterns = [
path(r'app/login/', views.login),
]
在view.py文件中定义视图函数:
from django.http import HttpResponse, HttpResponseRedirect
def login(request):
if request.method == 'GET':
request.session['username'] = '小明'
return HttpResponse('设置session')
以上案例用户认证的过程可以分为以下3个步骤:
步骤一:当用户第一次访问地址http://127.0.0.1:8080/app/login/时,服务端把唯一随机字符串Session加到Cookie中,并返回给客服端。可以在浏览器的开发者工具(按F12键)中查看Cookies的信息。
步骤二:服务端保存相关数据。服务端将数据保存在django_sesion表,该表会在第一次执行迁移命令时创建。session_key为Cookies中的Sessionid值,session_data为存储的数据(数据经过加密处理),expire_data为Session的过期时间,默认为两周。
步骤三:当用户再次访问时,仍可以通过request.session[‘username’]获取到存储在服务端的信息,即request.session默认通过Cookies中的Sessionid值在服务端查询相关数据信息。
本日志所有内容学习自《Python Web开发从入门到精通》(王海飞编著)、网络资源以及本人的调试 ↩︎