笔记主要基于官方文档,从中提取要点和记录笔记,关键处包含了官方文档链接。详见官方文档。
官方文档:Django documentation
博客推荐:Django2.2教程
官方文档视图层:视图层
目录
1.URL路由
1.1.URL配置过程(重要)
1.2.Django 如何处理一个请求(了解)
......
2.自定义错误页面
3.HttpRequest objects
4.Ajax
5.Cookie
6.session
视图
视图负责接受Web请求HttpRequest,进行逻辑处理,与M和T进行交互,返回Web响应HttpResponse给请求者。也可能重定向 redirect,还可以返回json数据。
使用视图的过程
视图就是一个python函数,被定义在"应用/views.py"文件中。 使用视图时需要进行两方面操作,两个操作不分先后。
1)在"应用/views.py"中定义视图。
2)配置URLconf,将视图函数和url对应起来
部分相关官方文档:
博客:https://www.liujiangblog.com/course/django/134
Django开发的网站,由哪一个视图进行处理请求,是由url匹配找到的。所以需要配置URLconf,将视图函数和url对应起来。
URL路由在Django项目中的体现就是urls.py
文件,提倡项目有个根urls.py
,各app下各有一个app urls.py
,既集中又分治,是一种解耦的模式。
注:路由的编写方式是Django2.x和1.x最大的区别所在。Django官方将原来的正则匹配表达式,改为更加简单的path表达式,但依然通过re_path()方法保持对1.x版本的兼容
具体配置例子和path函数可参考入门篇的:编写视图和URLConf
可能在URL配置过程中,可能会知其然而不知其所以然,这就需要了解Django 如何处理一个请求(跳转官方文档)。
当用户请求一个页面时,Django根据下面的逻辑执行操作:
- 决定要使用的根URLconf模块。。通常,这是
ROOT_URLCONF
设置的值,但如果传入HttpRequest
对象拥有urlconf
属性(通过中间件设置),它的值将被用来代替ROOT_URLCONF
设置。通俗的讲,就是可以自定义项目入口url是哪个文件!- Django 加载该模块并寻找可用的
urlpatterns
。它是django.urls.path()
和(或)django.urls.re_path()
实例的一个列表(sequence)。- Django 依次匹配每个URL 模式,在与请求的URL 匹配的第一个模式停下来。即url匹配是从上往下的短路操作,所以url在列表中的位置非常关键。
- 一旦有 URL 匹配成功,Djagno 导入并调用相关的视图,这个视图是一个简单的 Python 函数(或基于类的视图 class-based view )。视图会获得如下参数:
- 一个
HttpRequest
实例。- 如果匹配的 URL返回了没有命名的组,那么匹配的内容将作为位置参数提供给视图。
- 关键字参数由表达式匹配的命名组组成,并由
django.urls.path()
或django.urls.re_path()
的可选kwargs
参数中指定的任何参数覆盖。- 如果没有 URL 被匹配,或者匹配过程中出现了异常,Django 会调用一个适当的错误处理视图。参加下面的错误处理( Error handling )。
默认情况下,Django内置下面的路径转换器:
str
:匹配任何非空字符串,但不含斜杠/
,如果你没有专门指定转换器,那么这个是默认使用的;int
:匹配0和正整数,返回一个int类型slug
:可理解为注释、后缀、附属等概念,是url拖在最后的一部分解释性字符。该转换器匹配任何ASCII字符以及连接符和下划线,比如building-your-1st-django-site
;uuid
:匹配一个uuid格式的对象。为了防止冲突,规定必须使用破折号,所有字母必须小写,例如075194d3-6885-417e-a8a8-6c931e272f00
。返回一个UUID对象;path
:匹配任何非空字符串,重点是可以包含路径分隔符’/‘。这个转换器可以帮助你匹配整个url而不是一段一段的url字符串。要区分path转换器和path()方法。
路径转换器用于path的参数route
中。
下面是一个简单的 URLconf:
from django.urls import path
from . import views
urlpatterns = [
path('articles/2003/', views.special_case_2003),
path('articles//', views.year_archive),
path('articles///', views.month_archive),
path('articles////', views.article_detail),
]
有一个方便的小技巧是指定视图参数的默认值。 下面是一个URLconf 和视图的示例:
# URLconf url.py文件
from django.urls import path
from . import views
urlpatterns = [
path('blog/', views.page),
path('blog/page/', views.page),
]
# View (in blog/views.py)
def page(request, num=1):
# Output the appropriate page of blog entries, according to num.
...
在上面的例子中,两个URL模式指向同一个视图views.page
。但是第一个模式不会从URL中捕获任何值。 如果第一个模式匹配,page()函数将使用num参数的默认值"1"。 如果第二个模式匹配,page()将使用捕获的num值。
目的地URLconf会收到来自父URLconf捕获的所有参数,看下面的例子:
# In settings/urls/main.py 父URLconf
from django.urls import include, path
urlpatterns = [
path('/blog/', include('foo.urls.blog')),
]
# In foo/urls/blog.py 目的地URLconf
from django.urls import path
from . import views
urlpatterns = [
path('', views.blog.index),
path('archive/', views.blog.archive),
]
在上面的例子中,捕获的"username"变量将被传递给include()指向的URLconf,再进一步传递给对应的视图
URLconfs具有一个钩子(hook),允许把其他参数作为 Python 字典来传递给视图函数,像下面这样:
from django.urls import path
from . import views
urlpatterns = [
path('blog//', views.year_archive, {'foo': 'bar'}),
]
在上面的例子中,当请求到 /blog/2005/
时,Django将调用views.year_archive(request, year='2005', foo='bar')
。理论上,你可以在这个字典里传递任何你想要的传递的东西。在 syndication framework 中使用了这个办法,来向视图传递元数据和可选参数
注意,URL模式捕获的命名关键字参数和在字典中传递的额外参数有可能具有相同的名称,这会发生冲突,要避免。
类似上面,也可以传递额外的参数给 include()
。参数会传递给include指向的urlconf中的每一行,不管视图是否接受这些额外参数。因此,这个技巧仅在确定所包含的 URLconf 中的每一个视图接受你传递的额外选项时有用。
下面两个 URLconf 配置在功能上是相同的:
配置一:
# main.py
from django.urls import include, path
from mysite import views
urlpatterns = [
path('blog/', include('inner')),
]
# inner.py
from django.urls import path
urlpatterns = [
path('archive/', views.archive, {'blog_id': 3}),
path('about/', views.about, {'blog_id': 3}),
]
配置二:
# main.py
from django.urls import include, path
urlpatterns = [
path('blog/', include('inner'), {'blog_id': 3}),
]
# inner.py
from django.urls import path
from mysite import views
urlpatterns = [
path('archive/', views.archive),
path('about/', views.about),
]
更多关于URLconf内容请参考官方文档。
当 Django 找不到所匹配的请求 URL 时,或引发了异常时,Django 会调用一个错误处理视图。
Django默认的自带的错误视图包括400、403、404和500,分别表示请求错误、拒绝服务、页面不存在和服务器错误。它们的默认值应该满足大部分项目,但是通过赋值给它们以进一步的自定义也是可以的。
这些值是:
handler400
-- 查看 django.conf.urls.handler400
.handler403
-- 查看 django.conf.urls.handler403
.handler404
-- 查看 django.conf.urls.handler404
.handler500
-- 查看 django.conf.urls.handler500
.这些值可以在根URLconf中设置。在其它app中的二级URLconf中设置这些变量无效。
Django有内置的HTML模版,用于返回错误页面给用户,但是这些403,404页面太简陋,通常都自定义错误页面。
完整的细节请参见 自定义错误视图 。
实例:
(一)只需要简单的自定义错误页面时,在全局的templates根目录下,创建对应的400.html、403.html、404.html、500.html页面文件即可。Django会自动调用html。{{ request_path }}获取请求路径。如下
404错误页面 页面找不到--{{ request_path }}
(二)
(1)在根URLconf中(项目的根urls.py文件)额外增加下面的条目,并导入app的views模块:
from django.contrib import admin from django.urls import path from appname import views urlpatterns = [ path('admin/', admin.site.urls), #... ] # 增加的条目 handler400 = views.bad_request handler403 = views.permission_denied handler404 = views.page_not_found handler500 = views.server_error
(2)在app的 views.py 文件中增加四个处理视图:
def bad_request(request, exception, template_name='400.html'): return render(request, template_name) def permission_denied(request, exception, template_name='403.html'): return render(request, template_name) def page_not_found(request, exception, template_name='404.html'): return render(request, template_name, status=404) # 可以写上status def server_error(request, template_name='500.html'): return render(request, template_name)
(3)根据需求,在全局的templates根目录下创建对应的400.html、403.html、404.html、500.html四个页面文件。
注:在setting.py中,修改以下两项如下,错误视图才会生效。这两项的修改在网站开发完后必须要做。
DEBUG = False ALLOWED_HOSTS = [‘*’]
注意:自定义的视图函数可能会报错。这是由于,不同Django版本下,views模块中自定义的错误处理函数的方法参数可能已经改变,以上基于Django2.2 。
服务器接收到http协议的请求后,会根据报文创建HttpRequest对象(contains metadata about the request),这个对象不需要我们创建,直接使用服务器构造好的对象就可以。视图的第一个参数必须是HttpRequest对象(即request),浏览器提交的信息就保存在HttpRequest对象。每个视图负责返回一个HttpResponse对象。
HttpRequest
and HttpResponse objects are defined in the django.http
module. django.http模块可查询相关
API。
属性
下面除非特别说明,属性都是只读的。
QueryDict
类型对象,类似于字典,包含get请求方式的所有参数。保存的是get方式提交的参数。HttpRequest.body
属性。更多详情见:HttpRequest。
QueryDict
对象QueryDict
类型的对象。要了解GET、POST就要先了解QueryDict。dict.get('键',默认值)
可简写为
dict['键']
dict.getlist('键',默认值)
更多:文档:
QueryDict,博客:
https://www.liujiangblog.com/course/django/139
请求格式:在请求地址结尾使用?,之后以"键=值"的格式拼接,多个键值对之间以&连接。
例:网址如下
http://127.0.0.1:8000/?a=10&b=20&c=python
其中的请求参数为:
a=10&b=20&c=python
使用form表单请求时,method方式为post则会发起post方式的请求,需要使用HttpRequest对象的POST属性接收参数,POST属性是一个QueryDict类型的对象。
问:表单form如何提交参数呢?
答:表单控件name属性的值作为键,value属性的值为值,构成键值对提交。
Ajax即异步的javascript。在不全部加载某一个页面部的情况下,对页面进行局的刷新,ajax请求都在后台(在浏览器开发人员选项network可见)。
使用ajax提交时,以下两点很重要:
1) 首先分析出请求地址时需要携带的参数。
2) 视图函数处理完成之后,所返回的json的格式。
Ajax需要用到JQuery。注:图片,css文件,js文件都是静态文件。
(在Django用静态文件可参考 静态文件 。 更多关于设置和框架的资料,参考 静态文件解惑 和 静态文件指南。部署静态文件 介绍了如何在真实服务器上使用静态文件。)
Ajax的流程:
例子:(在Django项目中)
test_ajax.html
ajax {% load static %}应用/urls.py
from django.urls import path,include from booktest import views urlpatterns = [ # ... path('test_ajax', views.ajax_test, name='test_ajax'), # 现实ajax页面 path('ajax_handle', views.ajax_handle, name='ajax_handle'), # ajax处理 ]
视图views.py
from django.shortcuts import render, redirect from django.http import HttpResponse, JsonResponse # Create your views here. #...省略 # /test_ajax def ajax_test(request): '''显示ajax页面''' return render(request, 'booktest/test_ajax.html') def ajax_handle(request): '''ajax请求处理''' # 返回的json数据 {'res': 1} return JsonResponse({'res': 1}) # 返回的字典会转换成Json数据
以上,运行服务器,访问http://127.0.0.1:8000/test_ajax,
- 点击"ajax请求" 按钮,就会跳出返回的"1"。
- 将那几个alert的注释打开,会发现只要发起请求,不等回调函数执行,代码就会直接往下走。这就是异步。
- 设置'async'为 false,则为同步的Ajax请求
应用Ajax的场景很多,比如登录页面的校验。
Cookie是由服务器端生成,发送给User-Agent(一般是浏览器),浏览器会将Cookie的key/value保存到某个目录下的文本文件内,下次请求同一网站时就发送该Cookie给服务器(前提是浏览器设置为启用cookie)。Cookie名称和值可以由服务器端开发自己定义,这样服务器可以知道该用户是否是合法用户以及是否需要重新登录等。
Cookie是存储在浏览器中的一段纯文本信息,建议不要存储敏感信息如密码。典型应用:记住用户名,网站的广告推送。
Cookie的特点
在Django中实现Cookie的读写
设置cookie:需要一个HttpResponse类或其子类的对象(JsonResponse, HttpResponseRedirect),set_cookie 用于设置cookie。参考:
HttpResponse.set_cookie(key, value='', max_age=None, expires=None, path='/', domain=None, secure=None, httponly=False, samesite=None)
读取cookie:浏览器发送给服务器的cookie保存在request对象的 COOKIES属性( 见HttpRequest.COOKIES )。
例子:(注:运行后访问,在浏览器开发人员选项-->network可见)
# 1.文件views.py
# ...省略
# /set_cookie
def set_cookie(request):
'''设置cookie信息'''
response = HttpResponse('设置cookie')
# 设置一个cookie信息,名字为num, 值为1
response.set_cookie('num', value='1', max_age=14*24*3600) # max_age,expires 都可以用来设置过期时间
# response.set_cookie('num2', value='2')
# response.set_cookie('num', value='1', expires=datetime.now()+timedelta(days=14))
# 返回response
return response
# /get_cookie
def get_cookie(request):
'''获取cookie的信息'''
# 取出cookie num的值
num = request.COOKIES['num']
return HttpResponse(num)
# 2.文件urls.py
#。。。省略
urlpatterns = [
# ...
path('set_cookie', views.set_cookie, name='set_cookie'), # 设置cookie
path('get_cookie', views.get_cookie, name='get_cookie'), # 获取cookie
]
对于敏感、重要的信息,建议要储在服务器端,不能存储在浏览器中,如用户名、余额、等级、验证码等信息。
Session就是在服务器端进行状态保持的方案。
session的特点:
1) session是以键值对进行存储的。
2) session依赖于cookie。唯一的标识码保存在sessionid cookie中。
3) session有过期时间,如果不指定,默认两周就会过期。
Django项目默认启用Session。
在settings.py文件,中间件MIDDLEWARE_CLASSES中包含Session中间件:'django.contrib.sessions.middleware.SessionMiddleware'
session存储方式
可以存储在数据库、缓存、Redis等。在项目settings.py文件,设置SESSION_ENGINE项指定Session数据存储的方式。
1)存储在数据库中,如下设置可以写,也可以不写,这是默认存储方式。
SESSION_ENGINE='django.contrib.sessions.backends.db'
2)存储在缓存中:存储在本机内存中,如果丢失则不能找回,比数据库的方式读写更快。
SESSION_ENGINE='django.contrib.sessions.backends.cache'
3)混合存储:优先从本机内存中存取,如果没有则从数据库中存取。
SESSION_ENGINE='django.contrib.sessions.backends.cached_db'
若存储在数据库中,需要在项INSTALLED_APPS中安装Session应用:'django.contrib.sessions' (默认)。
设置和获取session用:HttpRequest.session
设置session:request.session[‘username’] = ‘smart’
获取session:request.session['username']、request.session.get('键',默认值)、
清除所有session,在存储中只删除值部分:request.session.clear()
清除session数据,在存储中删除session的整条数据:request.session.flush()
删除session中的指定键及值,在存储中只删除某个键及对应的值:del request.session['键']
设置会话的超时时间,如果没有指定过期时间则两个星期后过期:request.session.set_expiry(value)
例:
# 1.文件views.py
# ...省略
# /set_session
def set_session(request):
'''设置session'''
request.session['username'] = 'smart'
request.session['age'] = 18
# request.session.set_expiry(5)
return HttpResponse('set session')
# /get_session
def get_session(request):
'''获取session'''
username = request.session['username']
age = request.session['age']
return HttpResponse(username+':'+str(age))
# /clear_session
def clear_session(request):
'''清除session信息'''
# request.session.clear() # 在存储中只删除值部分
request.session.flush() # 在存储中删除session的整条数据
return HttpResponse('清除成功')
# 2.文件urls.py
#。。。省略
urlpatterns = [
# ...
path('set_session', views.set_session, name='set_session'), # 设置session
path('get_session', views.get_session, name='get_session'), # 获取session
path('clear_session', views.clear_session, name='clear_session'), # 清除session
]
以上可在表django_session中查看前后变化。
-----end-----