57、Django路由层

Django框架 / 2021-02-17

一、Django请求生命周期流程图

1.1、流程图简述

Django自带的是wsgiref,请求来的时候解析封装,响应走的时候打包处理;但是原生wsgiref支持的并发量较低,大约在1000左右,实际运用时要上uwsgi。

客户端请求访问资源,网关接口解析报文后通过反向代理至后端路由层,以免暴露后端服务器;后端在urls判断请求资源是否在路由列表,在则跳转至路由所关联的视图函数;根据提前的设计,如果不需要处理数据则直接读取templates的HTML页面返回,如需对数据进行处理则通过models模块调用ORM对数据库进行读写操作,数据库处理完毕后返回结果。

Django请求生命周期流程图-1613457364345

1.2、WSGI、wsgiref、uwsgi的关系

WSGI,web service gate interface,web服务网关接口,它是协议;wsgiref和uwgi是实现该协议的具体功能模块。

二、路由层

路由层主要是做路由匹配,即客户端请求的资源是否存在或开放;有则路由(跳转)至对应的视图函数进行处理,无则报错404。

2.1、路由匹配

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', views.index),
    url(r'^edit_user/', views.edit_user),
    url(r'^delete_user/', views.delete_user),
]
2.1.1、匹配方法

urls的URL方法,括号内第一个参数是用的正则表达式,只要第一个参数的正则表达式能够匹配到内容,那么就会停止往下匹配,直接执行第一个匹配到的视图函数。

自动斜杠

客户端输入url时Django默认在后面加斜杠,内部再自动做重定向,第一次没斜杠匹配不到,第二次加斜杠再来!也可以在settings里面取消自动加斜杠,添加此行:APPEND_SLASH = False,默认是True,这个保留默认即可。

一般是要^匹配开头,$匹配结尾。

首页匹配

^$,即客户端在网址后不跟任何资源URI默认跳转的页面。

尾页匹配

r'',即留空,表示什么都能匹配到,所以只能放在最后面,当所有url都匹配不到时,匹配到此页面,一般作为自定义404页面。

2.1.2、有名无名分组

给上面的正则表达式加上小括号就是分组。有什么用?比如我们开发了一个博客程序,当需要直接用id号来查看某篇文章时,浏览器在发送请求时可以直接在路径中写入参数以此传递给后端。

无名分组

无名分组就是将括号内的表达式匹配到的内容当作位置参数传递给后面的视图函数。

urls.py代码示意:

from django.conf.urls import url
from django.contrib import admin
from study import views

urlpatterns = [
    url(r'^article/(\d+)$', views.article)  # 匹配article/后面以数字结尾且至少有一个,匹配到的结果会当作位置参数传递给views
]

views.py代码示意:

# 因为urls会将匹配到的结果当作位置参数传递过来,所以此处必须要定义一个位置参数来接收它。
def article(request, article_id):
    return HttpResponse('收到')
有名分组

可以给正则表达式起一个别名,有名分组就是将括号内的表达式匹配到的内容当作关键字参数传递给后面的视图函数。

urls代码示意:

from django.conf.urls import url
from django.contrib import admin
from study import views

urlpatterns = [
    url(r'^article/(?P<blog>\d+)$', views.article),  # ?P<NAME>此为语法格式
]

views代码示意:

def article(request, blog):  # urls的P后面定义了什么名称,这里就必须要定义什么名称。
    return HttpResponse('知道了')
总结

无名有名分组作用是一样的,区别只是无名是以位置参数形式传递,而有名则是关键字参数形式;且二者不能混用,但是同一个分组可以使用多次。

2.2、路由分发

2.2.1、路由分发简述

随着APP越来越多,路由也越来越多,此时若还是将所有路由放在一个urls里面,会显得结构不清晰、不便于管理;因此我们应该把路由交由APP自己管理,我们只在路由总表做一个分发即可,此举也可减轻总路由的压力。

Django每一个APP都可以有自己的templates文件夹、urls.py、static文件夹;基于此特点,Django能够非常好地做到分组开发(每个人只写自己的APP)

2.2.2、路由分发操作
新建APP
C:\Users\AERO\PycharmProjects\djangoProject>python manage.py startapp learning
APP之一操作

learning应用操作,记得先去settings里注册一下新建的APP

urls代码示意

from django.conf.urls import url
from learning import views

urlpatterns = [  # 总路由不能用$结尾,不然就直接结束了,后面无法匹配。
    url(r'^index/', views.index)
]

views代码示意

from django.shortcuts import render, HttpResponse, redirect
# Create your views here.
def index(request):
    return HttpResponse('我是learning的index')
APP之二的操作

urls代码示意

from django.conf.urls import url
from study import views

urlpatterns = [
    url(r'^index/', views.index)
]

views代码示意

from django.shortcuts import render, HttpResponse, redirect
# Create your views here.

def index(request):
    return HttpResponse('我是study的index')
总路由分发

利用路由分发后,总路由不再处理路由与视图函数的直接对应关系,而是做一个分发处理,识别当前url是属于哪个应用下的,直接分发给对应的应用去处理,像前台一样。

from django.conf.urls import url, include  # 导入include模块
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^study/', include('study.urls')),
    url(r'^learning/', include('learning.urls'))
]

2.3、反向解析

2.3.1、反向解析简述

在一个项目刚开始时,可能考虑的东西不是很周全,如路由后期可能需要频繁修改,这意味着所有用到该路径的代码都要进行修改,这工作量可不小。

我们通过参数name为url路由起一个别名,项目中就可以通过别名来获取别名所对应的真实url地址;其实就跟变量名一样,我无论你变量值怎么变,我始终对应着你这个名字。正所谓他强任他强,清风拂山岗;他横任他横,明月照大江。

以上过程即可称之为反向解析!

2.3.2、反向解析演示
urls.py代码演示
from django.conf.urls import url, include
from django.contrib import admin
from study import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^study/', include('study.urls')),
    url(r'^learning/', include('learning.urls')),
    url(r'^login/', views.login, name='study_index'),  # 利用name给url起别名
    url(r'^index/', views.index, name='index_page')
]
views.py代码演示
from django.shortcuts import render, HttpResponse, redirect, reverse
# Create your views here.


def login(request):
    if request.method == 'GET':
        # 当客户端第一次访问时,login.html页面中的{% url 'study_index' %}会被反向解析为/login/
        return render(request, 'login.html')
    else:
        # 反向解析为login后,form的action定义为向login提交数据
        username = request.POST.get('username')
        password = request.POST.get('password')
    if username == 'chrystal' and password == '666':  # 匹配提交的数据,符合则进行下一步
        url = reverse('index_page')  # 反向解析index_page的真实url为index
        return redirect(url)  # 重定向到反向解析的结果


def index(request):
    return render(request, 'index.html')
login.html页面
<body>
    <h1 class="text-center">编辑页面</h1>
    <div class="container">
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                <form action="{% url 'study_index' %}" method="post">
                    用户名:<input type="text" name="username"  value="{{ edit_obj.username }}" class="form-control">
                    密码:<input type="password" name="password" value="{{ edit_obj.password }}" class="form-control">
                    <input type="submit" value="提交" class="btn btn-info btn-block">
                </form>
            </div>

        </div>
    </div>
</body>
index.html
<body>
    <h1 class="text-center">index页面</h1>
    {#  前端接收  #}
    {% url 'index_page' %}
</body>
2.3.3、分组反向解析

以下代码来自博客园:jsonji,原地址:https://www.cnblogs.com/Dominic-Ji/articles/10982293.html

urls代码
from django.conf.urls import url

from . import views

urlpatterns = [
    # ...
    url(r'^articles/([0-9]{4})/$', views.year_archive, name='news-year-archive'),
    # ...
]
views代码
from django.urls import reverse
from django.shortcuts import redirect

def year_archive(request):
    # ...
    year = 2006
    # ...
    return redirect(reverse('news-year-archive', args=(year,)))
html展示
<a href="{% url 'news-year-archive' 2012 %}">2012 Archive</a>

<ul>
{% for yearvar in year_list %}
<li><a href="{% url 'news-year-archive' yearvar %}">{{ yearvar }} Archive</a></li>
{% endfor %}
</ul>

无名
image-20210216165855778

有名

image-20210216170535025

三、伪静态(了解)

静态,即写死了的,没有人为更改的话内容万年不变。伪静态,即将一个动态网页伪装成静态网页;伪装目的在于增大本网站的SEO查询力度,增加搜索引擎首次本网站的概率。因为搜索引擎本质上就是一个巨大的爬虫程序,我们只需要在urls的路由后面加上后缀名html即可。

这个东西了解即可,因为上述所说的目的都是砸钱投放才真正有效果。

四、虚拟环境(了解)

在正常开发中,很可能会给每一个项目配置一个该项目独有的解释器环境;该环境内只有该项目用到的模块,其余的一概不装。虚拟环境就是类似于是一个单独的Python解释器环境,每创建一个虚拟环境就类似于重新下载了纯净的Python解释器,但是虚拟环境不要创建太多,因为它也是需要消耗硬盘空间的。

无标题

那么问题就来了:每一个项目都需要用到很多模块,并且每个模块版本可能不一样,如何安装?

实际开发中,会给每一个项目配备一个requirements文档,里面书写了该项目所有的模块及其版本;只需要一条命令即可一键安装所有模块及其版本。

五、Django大版本区别

  1. Django 1.X路由层使用的是url方法,而2.X和3.X路由层使用的是path方法
  2. url第一个参数支持正则,而path第一个参数是不支持正则的,不喜欢可以用re_path(跟url一样)。
  3. 模型层里面1.X外键默认都是级联更新删除的,但是2.X以后需要手动配置参数
  4. path不支持正则,但是它内部支持五种转换器
str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
int,匹配正整数,包含0。
slug,匹配字母、数字以及横杠、下划线组成的字符串。
uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)

path('index/<int:id>/', index)

将第二个路由里面的内容先转成整型然后以关键字的形式传递给后面的视图函数

除了这五个默认的转换器,还支持自定义转换器

自定义转换器示例

  1. 在APP下新建py文件,文件名可以随意命名。

  2. 文件代码

    class MonthConverter: regex='\d{2}' # 属性名必须为regex
        def to_python(self, value):
           return int(value)
    
       def to_url(self, value):
           return value # 匹配的regex是两个数字,返回的结果也必须是两个数字
    
  3. 在urls中,使用register_converter将其注册到URL配置中。

from django.urls import path, register_converter
from study.path_converts import MonthConverter

register_converter(MonthConverter,'mon')
from app01 import views

urlpatterns = [
	path('articles/<int:year>/<mon:month>/<slug:other>/', views.article_detail, name='aaa'),
]
世间微尘里 独爱茶酒中