django路由层分析
目录
路由层
路由层简介
路由层 即django项目文件夹中的 urls.py 文件
urls.py 文件中的 urlpatterns 列表实现的路由匹配
示例:
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index/', views.index)
]
# 列表中url函数中的第一个参数为正则表达式,第二个参数则为匹配成功后执行的函数
添加网站首页 / (404)错误提示页
# 在路由层的 urlpatterns 中添加:
# 添加首页的写法:
url(r'^$', views.home)
# 添加(404)错误提示页的写法:
# 这个要写在最下面一行,要等上面的都匹配失败了才能匹配到404
url(r'', views.error)
分组
无名分组
# 分组这个概念是由正则表达式中引出的,即 url(r'^index/(\d+)', views.index),这个正则表达式中的括号()就是分组
# 路由的分组是不会影响匹配的
# 正则表达式分组是会把分组的内容返回出来的,既 路由中的分组 会将加括号的正则表达式的内容 当做位置参数 传递给对应的视图函数中,所以对应的视图函数需要有一个位置形参来接收这个参数
# 示例:
# urls.py:
from app01 import views
urlpatterns = [
url('^admin/', admin.site.urls),
# 无名分组写法:正则表达式中加括号
url('^index/(\d+)', views.index) # 匹配一个或多个数字
]
# views.py:
def index(request, numb): # request是默认要加的参数,而这个numb位置形参则是为了接收分组传递来的参数的
print(numb)
return HttpResponse("OK")
有名分组
与无名分组不同,你可以看做有名分组给 要传递给视图函数的参数赋予了一个参数名,然后传递给视图函数的时候传的就是 参数名=参数,既传递过去的是一个关键字参数,所以视图函数接收的时候要给一个关键字形参,即要有一个与路由层的这个参数名一模一样的形参名
# 示例
# urls.py:
from app01 import views
urlpatterns = [
url('^admin/', admin.site.urls),
# 有名分组写法,在正则表达式中的分组中写(?P<分组名>)
url('^index/(?P<year>\d+)', views.index) # 匹配一个或多个数字
]
# views.py
def index(request,year): # 这个year与路由层传过来的关键字参数得同名才能接收到参数
print(year)
return HttpResponse("OK")
有无名分组注意事项
# 有名分组跟无名分组不能混着用!!!
# 但是支持同一类分组使用多次
# 无名分组多个
url(r'^index/(\d+)/(\d+)/', views.index)
def index(request, numb1, numb2):
return HttpResponse("OK")
# 有名分组多个
url(r'^index/(?P<year>\d+)/(?P<month>/d+)/', views.index)
def index(request, year, month):
return HttpResponse("OK")
解析
反向解析
路由层中的url方法中有一个属性 name
可以给每一个路由与视图函数的对应关系起一个名字(别名)
这个名字能够唯一标识出对应的路径
# 注意这个名字不能是重复的
给这一条url设置了name属性之后,前端和后端就可以通过这个name值动态获取到对应的路径:
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'indexadd/', views.index, name="index")
]
# 后端views.py文件中获取路径方法:
# 先导入一个reverse模块
from django.shortcuts import reverse # 与三板斧同源
reverse('index')
reverse('这里面填的是你给路由和视图函数对应关系起的 别名')
# 前端 html文件中获取路径的方法:
{% url 'index' %}
{% url '这里面填的是你给路由和视图函数对应关系起的 别名' %}
无名分组反向解析
# 同理,加上name属性:
url(r'^index/(\d+)/', views.index, name="index")
# 后端views.py文件中获取路径方法:
# 先导入一个reverse模块
from django.shortcuts import reverse # 与三板斧同源
reverse('index', arg=(numb,)) # 元组中的 ,逗号一定要加上
reverse('别名',arg=(接收无名分组传递给视图函数的参数,))
# 前端 html文件中获取路径的方法:
{% url 'index' id %}
{% url '别名' 要传递给后端的值 %}
案例:
# views.py 文件:
from django.shortcuts import reverse
def index(request,pk):
url = reverse('index', args=(pk,))
user_list = models.User.objects.all()
return render(request, 'index.html',{'user_list':user_list})
<!--html文件-->
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="utf8">
<title>title</title>
</head>
<body>
{% for user_obj in user_list %}
<a href="/index/{{ user_obj.pk }}/"></a>
{% endfor %}
{{ url 'index' user_obj.pk }}
</body>
</html>
# urls.py文件:
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index/(\d+)/', views.index, name="index")
]
有名分组反向解析
# 同理,加上name属性:
url(r'^index/(?P<numb>\d+)/', views.index, name="index")
# 后端views.py文件中获取路径方法:
# 先导入一个reverse模块
from django.shortcuts import reverse # 与三板斧同源
# 方法一:与无名分组解析相同,有名分组解析也可这样写
reverse('index', arg=(numb,)) # 元组中的 ,逗号一定要加上
reverse('别名',arg=(接收无名分组传递给视图函数的参数,))
# 方法二:(了解即可)
reverse('index',kwargs={"numb":numb1})
# 前端 html文件中获取路径的方法:
# 方法一:无名和有名分组解析都可以这样写
{% url 'index' id %}
# 方法二:(了解即可)
{% url '别名' numb=id %}
反向解析总结
总结:针对有名分组与无名分组的反向解析 统一采用一种格式即可:
# 前端
{% url 'index' numb %}
# 后端
reverse('index',args=(numb,)) # 这里的numb通常都是数据的主键值
反向解析的本质:
就是获取到一个能够访问 别名 所对应的视图函数
路由分发
django每一个app下面都可以有自己的urls.py路由层、templates文件夹、static文件夹
项目名下的urls.py(总路由)不再做路由与视图函数的匹配关系(视图分发),而是做路由的分发
路由的分发要用到一个模块:
from django.conf.urls import include
# 路由分发,注意路由分发的总路由千万不要规定$结尾
# 写法一:
from app01 import urls as app01_urls
from app02 import urls as app02_urls # 防止导入名字重复故取别名
url(r'^app01/', include(app01_urls)),
url(r'^app02/', include(app02_urls))
# 写法二:无需导入,直接在引号内写上 应用名.urls
url(r'^app01/', include('app01.urls')),
url(r'^app02/', include('app02.urls)')
# 在APP应用下新建urls.py文件,在该文件内写路由与视图函数的对应关系即可
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'^index/', views.index)
]
名称空间(了解)
url(r'^app01/',include(app01_urls,namespace='app01')),
url(r'^app02/',include(app02_urls,namespace='app02'))
# app01.urls.py:
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'^index/',views.index,name='index')
]
# app02.urls.py
from django.conf.urls import url
from app02 import views
urlpatterns = [
url(r'^index/',views.index,name='index')
]
# app01.views.py
reverse('app01:index')
# app02.views.py
reverse('app02:index')
伪静态网页
# 针对百度搜索的优化seo
url(r'^index.html', views.index, name="app01_index")
虚拟环境
应用场景:不同的项目需要配置不同的python解释器
django1.0 与 django2.0之间的区别
django 2.0 里面的path第一个参数不支持正则,写什么就匹配什么,100%精准匹配
django 2.0 里面的re_path对应着django 1.0里面的url
虽然django 2.0 里面的path不支持正则表达式,但是他提供 5个默认的转换器
str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
int,匹配正整数,包含0。
slug,匹配字母、数字以及横杠、下划线组成的字符串。
uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)
自定义转换器
自定义转换器:
1、正则表达式
2、类
3、注册
# 自定义转换器
class FourDigitYearConverter:
regex = '[0-9]{4}'
def to_python(self, value):
return int(value)
def to_url(self, value):
return '%04d' % value # 占四位,不够用0填满,超了则就按超了的位数来!
register_converter(FourDigitYearConverter, 'yyyy')
PS:路由匹配到的数据默认都是字符串形式
FBV与CBV
FBV:function base views
CBV:function base views
CBV
url(r'^mycls/',views.MyCls.as_view())
class MyCls(View):
def get(self,request):
return render(request,'index.html')
def post(self,request):
return HttpResponse('post')
# 无论是FBV还是CBV,路由层都是路由对应视图函数内存地址
urlpatterns = [
urlpatterns = [
# url(r'^mycls/',views.view)
url(r'^mycls/',views.MyCls.as_view())
]
class MyCls(View):
def get(self,request):
return render(request,'index.html')
def post(self,request):
return HttpResponse('post')
JsonResponse
# 与三板斧同层
from django.http import render,HttpResponse, redirect
from django.http import JsonResponse
def index(request):
# res = {'name':'jason大帅比','password':'123'}
# return HttpResponse(json.dumps(res))
return JsonResponse({'name':'jason大帅比', 'password':'123'},json_dumps.params={'ensure_ascii':False})
request.path | request.fullpath
print('path:',request.path)
print('full_path:',request.get_full_path())
path: /upload_file/
full_path: /upload_file/?name=jason
简单的文件上传
使用form表单中的file类型
注意点:
1、mothod需要制定为post
2、enctype需要改为formdata格式
后端暂时需要注意的是:
1、配置文件中注释掉csrfmiddleware中间键
2、通过request.FILES获取用户上传的post文件数据
file_obj = request.FILES.get('my_file') # 这里的my_file为form-file中的name属性的值
print(file_obj.name)
with open(file_obj_name,'wb') as f:
for line in file_obj.chunks():
f.write(line)