Django-Part2——路由层

[TOC]

Django请求生命周期流程图(重点)

  • wsgiref模块能够支持的并发量很小,上线之后换成uwsgi
  • wsgi、wsgiref、uwsgi之间的关系
    • wsgi是协议
    • wsgiref和uwsgi是实现该协议的功能模块

202011130830231.png

路由匹配

path()/re_path()与url()

  • 匹配顺序从上到下,只要能够匹配到内容,那么就会立刻停止往下匹配。
  • 2.x之后的re_path()与1.x的url()是等价的,推荐使用新版本的方法,方法名更加容易辨别意思。
  • re_path()的第一个参数是正则表达式,一旦匹配到了,则进入响应的视图函数并停止匹配。
  • path()的第一个参数是路径,只有完全匹配时才会执行对应的视图函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from django.contrib import admin
from django.urls import path

from app01 import views

urlpatterns = [
path('admin/', admin.site.urls),
path(r'test/', views.test),
path(r'testadd/', views.testadd)
]


from django.urls import re_path

urlpatterns = [
# 首页
re_path(r'^$',views.home),
# 路由匹配
re_path(r'^test/$',views.test), # 和path(r'test/', views.test)等效
re_path(r'^testadd/$', views.testadd),
# 尾页(但这种写法失去了重定向)
re_path(r'',views.error),
]
  • 在访问url时会以输入的原url进行一次匹配。当匹配失败时,会在原url上添加后缀/,再次进行一次匹配。
1
2
# 取消自动加斜杠,默认为True
APPEND_SLASH = False

无名分组

  • 无名分组就是将括号内正则表达式匹配到的内容当作位置参数传递给后面的视图函数
1
2
3
4
5
6
7
# -> urls.py
re_path(r'^test/(\d+)/', views.test)

# -> views.py
def test(request, xx):
print(xx)
return HttpResponse('test')

有名分组

  • 有名分组就是将括号内正则表达式匹配到的内容当作关键字参数传递给后面的视图函数
  • 需要有一个对应的形参来接收
1
2
3
4
5
6
7
# -> urls.py
re_path(r'^testadd/(?P<year>\d+)', views.testadd)

# -> views.py
def testadd(request, year):
print(year)
return HttpResponse('testadd')

无名有名是否可以混合使用

  • 不能混用无名、有名分组
  • 但是只使用一种分组时可以使用多次/传多个形参
1
2
3
4
5
6
7
8
# -> urls.py
re_path(r'^index/(\d+)/(\d+)/(\d+)/', views.index),
re_path(r'^index/(?P<year>\d+)/(?P<age>\d+)/(?P<month>\d+)/', views.index)

# -> views.py
def testadd(request, *args, **kwargs):
print(args, kwargs)
return HttpResponse('testadd')

反向解析

别名

  • 通过一些方法得到一个结果 该结果可以直接访问对应的url触发视图函数
1
2
3
4
5
6
7
8
9
10
11
12
# -> urls.py
# 给路由与视图函数起一个别名
re_path(r'^urls/', views.func, name='name')

# -> views.py
# 后端反向解析
from django.shortcuts import reverse
reverse('name')

# -> xxx.html
# 前端反向解析
<a href="{% url 'name' %}">string</a>

无名分组反向解析

分组的解析值一般就是数据的主键值。

1
2
3
4
5
6
7
8
9
10
11
12
# -> urls.py
# 无名分组反向解析
re_path(r'^edit/(\d+)/', views.edit, name='xxx')

# -> views.py
# 后端
def edit(request, edit_id):
reverse('xxx', args=(edit_id, ))

# -> xxx.html
# 前端
<a href="{% url 'xxx' user_obj.id %}">编辑</a>

有名分组反向解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# -> urls.py
# 有名分组反向解析
re_path(r'^func/(?P<year>\d+)/', views.func, name='ooo')

# -> views.py
# 后端
# 有名分组反向解析
print(reverse('ooo', kwargs={'year':123}))
# 简便的写法,和无名一样
print(reverse('ooo', args=(111,)))

# -> xxx.html
# 前端
<a href="{% url 'ooo' year=123 %}">111</a> # 了解
<a href="{% url 'ooo' 123 %}">222</a> # 记忆

如何理解分组反向解析

  • 分组是基于正则表达式匹配url,即不同的url能够进入相同的视图函数,多存在于 https://xxx/id/ 类似,这部分会在id不同时发生变化。
  • 反向解析基于别名,在.html和.py中动态获得 https://xxx/id/ 的xxx部分,这部分可能会在重构url地址时发生变化。
  • 分组反向解析则是通过上述两条结合,动态地获得 https://xxx/id/ 这条有很多种可能的url,以方便进入相应的视图函数。
  • 分组的解析值一般就是数据的主键值,相当于是在视图函数中增加形参,来代替在request中获取的部分数据。

路由分发

include()

  • django的每一个应用都可以有自己的templates、urls.py、static(需要自己新建)。这样能够很好地做到分组开发,最终利用路由分发进行整合。
  • 当django项目中的url特别多的时候,总路由urls.py代码非常冗余不好维护,这时也可以利用路由分发来减轻总路由的压力。
  • 使用路由分发之后,总路由不再将url与视图函数直接对应,而是识别分类当前url所属应用,并分发给对应的应用去处理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 总路由
from django.urls import include, re_path
# from app01 import urls as app01_urls
# from app02 import urls as app02_urls

urlpatterns = [
re_path(r'^admin/', admin.site.urls),
# 路由分发
# url(r'^app01/', include(app01_urls)), # 只要url前缀是app01开头,全部交给app01处理
# url(r'^app02/', include(app02_urls)) # 只要url前缀是app02开头,全部交给app02处理
# 推荐写法
re_path(r'^app01/', include('app01.urls')),
re_path(r'^app02/', include('app02.urls'))
# 注意事项: 总路由里面的url千万不能加$结尾
]


# 子路由
# -> app01 -> urls.py
from django.urls import include, re_path
from app01 import views

urlpatterns = [
re_path(r'^reg/', views.reg)
]

# -> app02 -> urls.py
from django.urls import include, re_path
from app02 import views

urlpatterns = [
re_path(r'^reg/', views.reg)
]

名称空间

  • 当多个应用出现了相同的别名,反向解析只能识别后缀而不能识别前缀,例如 https://app/xxx/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 总路由 
# -> urls.py
urlpatterns = [
re_path(r'^app01/', include('app01.urls', namespace='app01')),
re_path(r'^app02/', include('app02.urls', namespace='app02'))
]
# 子路由
# -> app01 -> urls.py
urlpatterns = [
re_path(r'^reg/', views.reg, name='reg')
]
# -> app02 -> urls.py
urlpatterns = [
re_path(r'^reg/', views.reg, name='reg')
]

# 反向解析
# -> views.py
reverse('app01:reg')
reverse('app02:reg')
# -> .html
{% url 'app01:reg' %}
{% url 'app02:reg' %}
  • 利用名称空间可以区分不同app的相同别名,但一般只需要在别名之前添加所属应用作为前缀,就大可不必使用名称空间。
1
2
3
4
5
6
7
# -> urls.py
urlpatterns = [
re_path(r'^reg/', views.reg, name='app01_reg')
]
urlpatterns = [
re_path(r'^reg/', views.reg, name='app02_reg')
]

伪静态

  • 静态网页:数据是写死的 万年不变
  • 伪静态:将一个动态网页伪装成静态网页
  • 伪装的目的:
    • 增大本网站的seo查询力度
    • 增加搜索引擎收藏本网上的概率
1
2
3
4
# 仅将url地址改为.html结尾,假装只是返回html文件,实际上还是经过视图函数,可以做动态处理
urlpatterns = [
re_path(r'^reg.html', views.reg, name='app02_reg')
]

虚拟环境

  • 在正常开发中,通常每一个项目配备一个独有的解释器环境,只有该项目用到的模块,用不到一概不装。
  • 但是较多的虚拟环境会消耗更多的硬盘空间。
  • 一般通过requirement.txt来标识项目的虚拟环境需要的模块。

path()转换器

  1. str:匹配除了路径分隔符/之外的非空字符串,这是默认的形式
  2. int:匹配正整数,包含0。
  3. slug:匹配字母、数字以及横杠、下划线组成的字符串。
  4. uuid:匹配格式化的uuid,如:075194d3-6885-417e-a8a8-6c931e272f00。
  5. path:匹配任何非空字符串,包含了路径分隔符(/)
1
2
3
4
5
6
7
8
# -> urls.py
# 将匹配内容转成相应类型,作为形参传递给后面的视图函数
path('index/<int:id>/', index)

# -> views.py
def index(request,id):
print(id, type(id))
return HttpResponse('index')
  1. 自定义转换器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# -> path_converts.py
class MonthConverter:
regex='\d{2}' # 属性名必须为regex

def to_python(self, value):
return int(value)

def to_url(self, value):
return value # 匹配的regex是两个数字,返回的结果也必须是两个数字

# -> urls.py
from django.urls import path, register_converter
from path_converts import MonthConverter
from app01 import views

# 先注册转换器
register_converter(MonthConverter, 'mon')

urlpatterns = [
path('articles/<int:year>/<mon:month>/<slug:other>/', views.article_detail, name='aaa'),
]