django 路由层
1. django2.x 版本的path
1. 基本用法
django2.0的re_path和1.0的url一样
urlpatterns = [
re_path('articles/(?P<year>[0-9]{4})/', year_archive),
re_path('article/(?P<article_id>[a-zA-Z0-9]+)/detail/', detail_view),
re_path('articles/(?P<article_id>[a-zA-Z0-9]+)/edit/', edit_view),
re_path('articles/(?P<article_id>[a-zA-Z0-9]+)/delete/', delete_view),
]
# 考虑下面两个问题:
1. 函数 year_archive 中year参数是字符串类型的,因此需要先转化为整数类型的变量值
当然year=int(year) 不会有诸如如TypeError或者ValueError的异常。
那么有没有一种方法,在url中,使得这一转化步骤可以由Django自动完成?
2. 三个路由中article_id都是同样的正则表达式,但是你需要写三遍
当之后article_id规则改变后,需要同时修改三处代码,那么有没有一种方法,只需修改一处即可?
# 在Django2.0中,可以使用 path 解决以上的两个问题
# 示例
from django.urls import path
from . import views
urlpatterns = [
path('articles/2003/', views.special_case_2003),
path('articles/<int:year>/', views.year_archive),
path('articles/<int:year>/<int:month>/', views.month_archive),
path('articles/<int:year>/<int:month>/<slug>/', views.article_detail),
# path才支持,re_path不支持
path('order/<int:year>',views.order),
]
# 基本规则
1. 使用尖括号(<>)从url中捕获值
2. 捕获值中可以包含一个转化器类型(converter type),比如使用 <int:name> 捕获一个整数变量
若果没有转化器,将匹配任何字符串,当然也包括了 / 字符
3. 无需添加前导斜杠
示例分析表
2. path转化器
Django默认支持以下5个转化器:
- str, 匹配除了路径分隔符(
/
)之外的非空字符串,这是默认的形式 - int, 匹配正整数,包含0。
- slug, 匹配字母、数字以及横杠、下划线组成的字符串。
- uuid, 匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00
- path, 匹配任何非空字符串,包含了路径分隔符(/)(不能用?)
1. 例子
path('login/<int:year>', views.login),
path('login/<str:year>', views.login),
path('login/<path:p>', views.article),
2. 高级例子
# 实现匹配这种路径:http://127.0.0.1:8000/liuqingzheng/p/4444.html
path('<str:name>/p/<int:id>.html', views.article),
re_path(r'^(?P<name>.*?)/p/(?P<id>\d+).html$', views.login),
url(r'^(?P<name>.*?)/p/(?P<id>\d+).html$', views.login), # url在2.x以后不建议使用
3. 转换器可以不可以在re_path中使用
3. 注册自定义转化器
对于一些复杂或者复用的需要,可以定义自己的转化器。转化器是一个类或接口,它的要求有三点:
-
regex
类属性,字符串类型 -
to_python(self, value)
方法,value是由类属性regex
所匹配到的字符串,返回具体的Python变量值,以供Django传递到对应的视图函数中。 -
to_url(self, value)
方法,和to_python
相反,value是一个具体的Python变量值,返回其字符串,通常用于url反向引用。
示例
1. 写一个类
regex属性:这里写的正则表达式就能匹配
to_python 方法
to_url 方法
2. 注册这个类
register_converter(类名, 'lqz')
3. 在path中使用
path('<lqz:name>/', views.article),
class FourDigitYearConverter:
regex = '[0-9]{4}'
def to_python(self, value):
return int(value)
def to_url(self, value):
return '%04d' % value
# 使用register_converter将其注册到URL配置中:
from django.urls import register_converter, path
from . import converters, views
register_converter(converters.FourDigitYearConverter, 'myconverter')
urlpatterns = [
path('articles/2003/', views.special_case_2003),
path('articles/<myconverter:year>/', views.year_archive),
...
]
4. django版本区别
# 区别1
django1.X路由层使用的是url方法
url()第一个参数支持正则
django2.X 3.X版本中路由层使用的是path方法
path()第一个参数是不支持正则的(精准匹配)
# 2.X和3.X里面的re_path就等价于1.X里面的url
# 如果不习惯使用path django提供了另外一个方法
from django.urls import path, re_path
re_path(r'^index/',index)
from django.conf.urls import url
url(r'^login/',login)
# 区别2
# path不支持正则 但是它的内部支持五种转换器
path('index/<int:id>/',index)
# 将第二个路由里面的内容先转成整型然后以关键字的形式传递给后面的视图函数
def index(request,id):
print(id,type(id))
return HttpResponse('index')
# 区别3 除了有默认的五个转换器之外 还支持自定义转换器
# 区别4
# 模型层里面1.X外键默认都是级联更新删除的
# 2.X和3.X中需要手动配置参数
models.ForeignKey(to='Publish')
models.ForeignKey(to='Publish',on_delete=models.CASCADE...)
django 视图层
1. 视图函数
# 视图函数简介
视图函数,简称视图,是一个简单的Python函数,它接受Web请求并且返回Web响应。
响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或一张图片,是任何东西都可以。
无论视图本身包含什么逻辑,都要返回响应。代码写在哪里也无所谓,只要它在你的Python目录下面。
除此之外没有更多的要求了——可以说“没有什么神奇的地方”。
为了将代码放在某处,约定是将视图放置在项目或应用程序目录中的名为views.py的文件中。
# 示例: 返回当前日期和时间作为HTML文档的视图
from django.shortcuts import render, HttpResponse, HttpResponseRedirect, redirect
import datetime
def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>It is now %s.</body></html>" % now
return HttpResponse(html)
# 代码分析
1. 从django.shortcuts模块导入了HttpResponse类,以及Python的datetime库
2. 定义了current_datetime函数,它就是视图函数
每个视图函数都使用HttpRequest对象作为第一个参数,并且通常称之为request
3. 视图函数的名称并不重要,不需要用一个统一的命名方式来命名,以便让Django识别它
我们将其命名为current_datetime,是因为这个名称能够精确地反映出它的功能
4. 这个视图会返回一个HttpResponse对象,其中包含生成的响应
每个视图函数都负责返回一个HttpResponse对象
'''视图层 熟练掌握两个对象即可:请求对象(request)和响应对象(HttpResponse)'''
2. 视图层之请求对象
HttpRequest对象
django将请求报文中的请求行、首部信息、内容主体封装成 HttpRequest 类中的属性
除了特殊说明的之外,其他均为只读的
1. request属性
1. HttpRequest.GET
一个类似于字典的对象,包含HTTP GET的所有参数。详情请参考QueryDict对象
2. HttpRequest.POST
一个类似于字典的对象,如果请求中包含表单数据,则将这些数据封装成QueryDict对象
POST请求可以带有空的POST字典
如果通过HTTP POST方法发送一个表单,但是表单中没有任何的数据,QueryDict 对象依然会被创建
因此检查使用的是否是POST方法不应该使用
if request.POST
应该使用
if request.method == "POST"
另外:如果使用POST上传文件的话,文件信息将包含在FILES属性中
注意:键值对的值是多个的时候,比如checkbox类型的input标签,select标签,需要用:
request.POST.getlist("hobby")
3. HttpRequest.body
一个字符串,代表请求报文的主体。在处理非HTTP形式的报文时非常有用
例如:
二进制图片 XML Json等
但如果要处理表单数据的时候,推荐还是使用HttpRequest.POST
4. HttpRequest.path
一个字符串,表示请求的路径组件(不含域名)
例如:
"/music/bands/the_beatles/"
5. HttpRequest.method
一个字符串,表示请求使用的HTTP方法,必须使用大写
例如:
"GET"、"POST"
6. HttpRequest.encoding
一个字符串,表示提交的数据的编码方式
(如果为None则表示使用DEFAULT_CHARSET的设置,默认为'utf-8')
这个属性是可写的,你可以修改它来修改访问表单数据使用的编码
接下来对属性的任何访问(例如从GET或POST中读取数据)将使用新的encoding值
如果你知道表单数据的编码不是DEFAULT_CHARSET,则使用它。
7. HttpRequest.META
一个标准的Python字典,包含所有的HTTP首部,具体的头部信息取决于客户端和服务器
示例:
CONTENT_LENGTH —— 请求的正文的长度(是一个字符串)
CONTENT_TYPE —— 请求的正文的MIME类型
HTTP_ACCEPT —— 响应可接收的Content-Type
HTTP_ACCEPT_ENCODING —— 响应可接收的编码
HTTP_ACCEPT_LANGUAGE —— 响应可接收的语言
HTTP_HOST —— 客服端发送的HTTP Host头部
HTTP_REFERER —— Referring页面
HTTP_USER_AGENT —— 客户端的user-agent字符串
QUERY_STRING —— 单个字符串形式的查询字符串(未解析过的形式)
REMOTE_ADDR —— 客户端的IP地址
REMOTE_HOST —— 客户端的主机名
REMOTE_USER —— 服务器认证后的用户
REQUEST_METHOD —— 一个字符串,例如"GET"或"POST"
SERVER_NAME —— 服务器的主机名
SERVER_PORT —— 服务器的端口(是一个字符串)
从上面可以看到,除CONTENT_LENGTH和CONTENT_TYPE之外,请求中的任何HTTP首部转换为META的键时,都会将所有字母大写并将连接符替换为下划线最后加上HTTP_前缀
所以,一个叫做X-Bender的头部将转换成META中的HTTP_X_BENDER键
8. HttpRequest.FILES
一个类似于字典的对象,包含所有的上传文件信息
FILES中的每个键为<input type="file" name="" /> 中的name,值则为对应的数据
FILES只有在请求的方法为POST且提交的<form>带有enctype="multipart/form-data"的情况下,才会包含数据。否则,FILES将为一个空的类似于字典的对象
9. HttpRequest.COOKIES
一个标准的Python字典,包含所有的cookie,键和值都为字符串
10. HttpRequest.session
一个既可读又可写的类似于字典的对象,表示当前的会话
只有当Django启用会话支持时才可用
11. HttpRequest.user(用户认证组件下使用)
一个 AUTH_USER_MODEL 类型的对象,表示当前登录的用户
如果用户当前没有登录,user将设置为django.contrib.auth.models.AnonymousUser的一个实例
可以通过 is_authenticated() 区分它们
例如:
if request.user.is_authenticated():
# Do something for logged-in users.
else:
# Do something for anonymous users.
user只有当Django启用AuthenticationMiddleware中间件时才可用
匿名用户
class models.AnonymousUser
django.contrib.auth.models.AnonymousUser类实现了django.contrib.auth.models.User接口
但具有下面几个不同点:
id永远为None
username永远为空字符串
get_username()永远返回空字符串
is_staff和is_superuser永远为False
is_active永远为False
groups和user_permissions永远为空
is_anonymous()返回True而不是False
is_authenticated()返回False而不是True
set_password()、check_password()、save()和delete()引发NotImplementedError
New in Django 1.8:
新增 AnonymousUser.get_username() 以更好地模拟 django.contrib.auth.models.User
2. request常用方法
1. HttpRequest.get_full_path()
返回path,如果可以将加上查询字符串
例如:
"/music/bands/the_beatles/?print=true"
注意和path的区别
http://127.0.0.1:8001/order/?name=lqz&age=10p0
2. HttpRequest.is_ajax()
如果请求是通过XMLHttpRequest发起的,则返回True
方法是检查HTTP_X_REQUESTED_WITH相应的首部是否是字符串'XMLHttpRequest'
大部分现代的JavaScript库都会发送这个头部
如果你编写自己的XMLHttpRequest调用(在浏览器端),你必须手工设置这个值来让is_ajax()可以工作
如果一个响应需要根据请求是否是通过AJAX 发起的
并且你正在使用某种形式的缓存例如Django的cache middleware
你应该使用vary_on_headers('HTTP_X_REQUESTED_WITH')装饰你的视图以让响应能够正确地缓存
3. 示例
def index(request):
'''
request: django封装的对象,它的类是WSGIRequest,它里面包含了所有http请求的东西
'''
print(request)
print(type(request))
# from django.core.handlers.wsgi import WSGIRequest
# 1. 第一部分
print(request.method)
print(request.GET)
print(request.POST)
# 2. 第二部分
# 自定制请求头
# 上传文件使用的编码方式是form-data,默认编码方式urlencoded
print(request.is_ajax()) # 是不是ajax请求
print(request.path) # 请求路径 示例: /app01/ab_file/
print(request.path_info) # 请求路径 示例: /app01/ab_file/
print(request.get_full_path()) # 请求全路径,带数据 示例: /app01/ab_file/?username=jason
print(request.body) # 请求体,二进制,如果传文件,这个报错
'''
使用form表单,默认情况下数据被转成name=lqz&password=123放到请求体中
request.POST其实是从body中取出bytes格式的,转成了字典
requet.GET其实是把路径中?后面的部分拆出来,转成了字典
'''
print(request.encoding) # 客户端向服务端传递时,使用的编码方法
print(request.META) # 重点,字典,请求用户的ip地址,请求头中数据,用户自定制请求头的数据
# 把请求头的key值部分统一加HTTP_ 并且全部转成大写
print(request.META['REMOTE_ADDR']) # 客户端的ip地址
print(request.FILES) # 客户端上传的文件
# 3. 第三部分
print(request.COOKIES) # 空字典
print(request.session) # session对象
print(request.user) # 匿名用户
return HttpResponse('ok')
3. HttpResponse响应对象
响应对象主要有三种形式:
- HttpResponse() 返回字符串类型
- render() 返回html页面 并且在返回给浏览器之前还可以给html文件传值
- redirect() 重定向
# 示例
def index(request):
# 三件套
# return HttpResponse('ok')
# return render(request,'index.html',context={'name':'lqz','age':18})
# return redirect('/home') # 重定向自己的地址,重定向第三方地址,经常跟反向解析一起使用
# 视图函数必须要返回一个HttpResponse对象
'''
The view app01.views.index didn't return an HttpResponse object. It returned None instead.
'''
# render简单内部原理
from django.template import Template,Context
res = Template('<h1>{{ user }}</h1>')
con = Context({'user':{'username':'jason','password':123}})
ret = res.render(con)
print(ret)
return HttpResponse(ret)
1. HttpResponse()
括号内直接跟一个具体的字符串作为响应体
2. render()
# 结合一个给定的模板和一个给定的上下文字典,并返回一个渲染后的HttpResponse对象
render(request, template_name[, context])
# 参数:
request: 用于生成响应的请求对象
template_name:要使用的模板的完整名称,可选的参数
context:添加到模板上下文的一个字典,默认是一个空字典
如果字典中的某个值是可调用的,视图将在渲染模板之前调用它
# 总结
render方法就是将一个模板页面中的模板语法进行渲染,最终渲染成一个html页面作为响应体
3. redirect()
1. 基本用法
# 传递要重定向的一个硬编码的URL
def my_view(request):
...
return redirect('/some/url/')
# 也可以是一个完整的URL
def my_view(request):
...
return redirect('http://www.baidu.com/')
2. 301和302区别
1. 301和302的区别
共同点:
301和302状态码都表示重定向
就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址
这个地址可以从响应的Location首部中获取
用户看到的效果就是他输入的地址A瞬间变成了另一个地址B
不同点:
301表示旧地址A的资源已经被永久地移除了(这个资源不可访问了)
搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址
302表示旧地址A的资源还在(仍然可以访问)
这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址
SEO302好于301
2. 重定向原因
(1)网站调整(如改变网页目录结构)
(2)网页被移到一个新地址
(3)网页扩展名改变(如应用需要把.php改成.Html或.shtml)
这种情况下,如果不做重定向,则用户收藏夹或搜索引擎数据库中旧地址只能让访问客户得到一个404页面错误信息,访问流量白白丧失;再者某些注册了多个域名的
网站,也需要通过重定向让访问这些域名的用户自动跳转到主站点等
4. JsonResponse
向前端返回一个json格式字符串的两种方式
# json格式的数据作用
前后端数据交互需要使用到json作为过渡 实现跨语言传输数据
# 前端序列化
JSON.stringify() json.dumps()
JSON.parse() json.loads()
# 示例
import json
from django.http import JsonResponse
def ab_json(request):
user_dict = {'username':'jason好帅哦,我好喜欢!','password':'123','hobby':'girl'}
l = [111,222,333,444,555]
# 先转成json格式字符串
json_str = json.dumps(user_dict,ensure_ascii=False)
# 将该字符串返回
return HttpResponse(json_str)
# 读源码掌握用法
return JsonResponse(user_dict,json_dumps_params={'ensure_ascii':False})
# In order to allow non-dict objects to be serialized set the safe parameter to False.
# 默认只能序列化字典 序列化其他需要加safe参数
return JsonResponse(l,safe=False)
# 向客户端返回json格式数据
# 第一种方式
import json
data={'name':'lqz','age':18}
data1=['lqz','egon']
return HttpResponse(json.dumps(data1))
# 第二种方式
from django.http import JsonResponse
data = {'name': 'lqz', 'age': 18}
data1 = ['lqz', 'egon']
# django内置提供的JsonResponse 本质还是HttpResponse
return JsonResponse(data)
return JsonResponse(data1,safe=False)
# ensure_ascii
res=json.dumps({'name':'刘清政','age':18},ensure_ascii=False)
return HttpResponse(json.dumps(res))
# json_dumps_params
return JsonResponse({'name':'刘清政','age':18},json_dumps_params={'ensure_ascii':False})
# safe,转换除字典以外的格式,需要safe=False
return JsonResponse([11,12,13,'lqz',[1,2,3],{'name':'lqz','age':19}],safe=False)
4. CBV和FBV
CBV基于类的视图 (Class base view)和 FBV基于函数的视图 (Function base view)
# 视图函数可以是函数也可以是类
# FBV
def index(request):
return HttpResponse('OK')
# CBV
"""只要是处理业务逻辑的视图函数 形参里面肯定要有request"""
# 写一个类,继承View (写视图类 还是写在views.py中)
from django.views import View
class MyClass(View):
def get(self,request):
return HttpResponse('get请求')
def post(self,request):
return HttpResponse('post请求')
# 注意 CBV路由匹配写法跟FBV有点不一样(但是其实本质是一样的)
url(r'^login/$',views.MyClass.as_view())
# CBV特点
能够直接根据请求方式的不同直接匹配到对应的方法执行
# 示例
from django.views import View
class AddPublish(View):
def dispatch(self, request, *args, **kwargs):
print(request)
print(args)
print(kwargs)
# 可以写类似装饰器的东西,在前后加代码
obj=super().dispatch(request, *args, **kwargs)
return obj
def get(self,request):
return render(request,'index.html')
def post(self,request):
request
return HttpResponse('post')
5. 文件上传
1. form表单 提交地址
# action 参数
# 1. 不写,默认向当前地址发送请求
# 2. /index/,向当前域(http://127.0.0.1:8000/)的/index/发送请求
# 3. http://127.0.0.1:8000/index/,向该地址发送请求(可以向第三方服务发送请求)
# method
# 1. post:发送post请求(默认编码情况下:以key=value&key=value的形式拼到请求体中)
# 2. get: 发送get请求(以key=value&key=value的形式拼到路径中)
<form action="/index/" method="post">
<p>用户名:<input type="text" name="name"></p>
<p>密码:<input type="text" name="password"></p>
<p><input type="submit" value="提交"></p>
</form>
2. 基本用法
"""
form表单上传文件类型的数据
1. method必须指定成post
2. enctype必须换成formdata
"""
# form表单上传文件及后端操作
# html文件 (注意编码方式)
<form action="/index/" method="post" enctype="multipart/form-data">
<p>用户名:<input type="text" name="name"></p>
<p>密码:<input type="password" name="password"></p>
<p><input type="file" name="myfile"></p>
<p><input type="submit" value="提交"></p>
</form>
# views.py
def index(request):
file=request.FILES.get('myfile')
# 打开一个空文件,写入
with open(file.name,'wb') as f:
for line in file.chunks():
f.write(line)
return HttpResponse('文件上传成功')
3. Pycharm自动提示
from django.core.handlers.wsgi import WSGIRequest
# pycharm的自动提示
request=request # type: WSGIRequest