Django04_路由分发
4.1 路由概述
路由, 是一种映射关系!是把客户端请求的 url 路径和用户请求的应用程序
指django里面的视图进行绑定映射的一种关系。
注意:请求路径和视图函数不是一对一映射关系!
在django 中所有的路由最终都被保存到一个变量 urlpatterns中, urlpatterns必须声明在主应用下的urls.py总路由中。

这是由配置文件settings设置的

当客户端发送了一个 http 请求到服务端,服务端的web服务器则会从http协议中提取url地址, 从程序内部找到项目中添加到urlpatterns里面的所有路由信息的url进行遍历匹配。如果相等或者匹配成功,则调用当前url对象的视图方法。
在给urlpatterns路由列表添加路由的过程中,django一共提供了2个函数给开发者注册路由.
            
            
              python
              
              
            
          
          from django.urls import path      # 字符串路由
from django.urls import re_path   # 正则路由,会把url地址看成一个正则模式与客户端的请求url地址进行正则匹配
# path和re_path 使用参数一致.仅仅在url参数和接收参数时写法不一样
        4.2 静态文件路由
- 在项目根目录下创建一个static目录
 

- 在settings.py中写上配置信息
 
            
            
              python
              
              
            
          
          STATIC_URL = '/static/'
# /static/代表页面去访问的地址
STATICFILES_DIRS = [
 os.path.join(BASE_DIR, 'static')
]
        这里的static代表它在你项目中的路径

- 去访问 
static下的文件,比如创建一个test.txt 

4.3 path普通路由
在前面说到过
from django.urls import path是用于字符串路由控制的,也就是普通的路由配置
            
            
              python
              
              
            
          
          from django.contrib import admin
from django.urls import path
from ntime.views import times
urlpatterns = [
    path('admin/', admin.site.urls),
    path('times',times) # 当请求 /times/ 路径时,会被 times/ 所匹配,去执行 times 这个视图函数
]
        注意:如果我想匹配
/times/下的任意数字(比如:/times/123),都想被index函数去执行,就没法办到了!
4.5 正则匹配
如果需要匹配/times/下的任意数字,就需要使用正则匹配
            
            
              python
              
              
            
          
          from django.urls import re_path   # 正则路由
        
            
            
              python
              
              
            
          
          from django.urls import re_path
urlpatterns = [
    path('admin/', admin.site.urls),
    path('times',times),#当请求 /times/ 路径时,会被 times/ 所匹配,去执行 times 这个视图函数
    re_path(r'times/\d/',times)
]
        使用正则表达式进行路径匹配
比如:访问
times/任意数字都会被times/\d/进行成功匹配 从而去执行times函数

4.5 分组匹配---无名分组
如果在times函数中,我想接收所匹配的内容,就需要要使用到分组匹配(就是需要接收到路由传递的数据)
            
            
              python
              
              
            
          
           re_path(r'times/(\d\d)',times)# 将 (\d\d) 所匹配的值,传递给 times 函数
        此时,需要 times 函数进行接收
            
            
              python
              
              
            
          
          def times(request,str):
    now = datetime.datetime.now().strftime("%Y-%m-%d %X")
    print(str)
    return HttpResponse(now+"::"+str,)
    # return render(request, "timer.html", {"now": now})
        
由于是无名分组,这里
times``函数的接收参数str,可以随便写
4.6 分组匹配-有名分组
            
            
              python
              
              
            
          
          from ntime import views
urlpatterns = [
    ......
    re_path('book/(?P<booke_name>\w\w)/', views.book),
]
        将(?P\d\d)匹配的内容以
booke_name的关键字传递给book这个函数,那么book这个函数的参数必须是booke_name
            
            
              python
              
              
            
          
          def book(request, booke_name):
    print(booke_name)
    return HttpResponse(booke_name)
        
4.7 多个app的路由分发
比如我这里有两个子应用

这个时候如果需要在 urls.py 进行路由跳转等,就需要使用路由分发 include
先在子应用 app01中创建 utls.py

在此 urls.pt中添加路由代码
            
            
              python
              
              
            
          
          from django.urls import path,re_path
from app01 import views
urlpatterns = [
    re_path('book/(?P<booke_name>\w\w)/', views.book),
]
        app01 的 views:
            
            
              python
              
              
            
          
          from django.shortcuts import render, HttpResponse
# Create your views here.
def book(request, booke_name):
    print(booke_name)
    return HttpResponse("这是app01:"+booke_name)
        同理,ntime 子应用中 也需要 创建 urls.py
ntime 的 urls:
            
            
              python
              
              
            
          
          from django.urls import path,re_path
from ntime import views
urlpatterns = [
    re_path('book/(?P<booke_name>\w\w)/', views.book),
]
        ntime 的 views:
            
            
              python
              
              
            
          
          def book(request, booke_name):
    print(booke_name)
    return HttpResponse("这是ntime:" + booke_name)
        主应用的 urls:
            
            
              python
              
              
            
          
          urlpatterns = [
    ......
    re_path('^app01/',include('app01.urls')),
    re_path('^ntime/',include('ntime.urls')),
]
        比如:
请求
app01/book/ss, 将会被app01/所匹配,并将请求导向到app01 的 urls.py

4.8 path路径转换器
在4.4 的re_path的时候,在里面可以写正则表达式,进行匹配路径,特别的方便
在path路径分发的时候,也可以进行随意匹配
写法如下:
            
            
              python
              
              
            
          
          path('login/<int:y>/', views.login),
        这里的<int:y>就是一种通用匹配方式
- str:匹配任何非空字符串,不包括路径分隔符'/'。如果转换器不包含在表达式中,这是默认值。
 - int:匹配零或任何正整数。返回一个int。
 - slug:匹配由ASCII字母或数字组成的字符串,以及横线和下划线字符。例如, building-your-1st-django_site可以匹配,django_@site是不可以匹配的。
 - uuid:匹配格式化的UUID。为防止多个URL映射到同一页面,必须包含破折号,并且字母必须是小写。例如,075194d3-6885-417e-a8a8-6c931e272f00。返回一个 UUID实例。
 - path:匹配任何非空字符串,包括路径分隔符 '/',可以匹配完整的URL路径,而不仅仅是URL路径的一部分str,使用时要谨慎,因为可能造成后续的所有url匹配都失效。
 
使用path中的通用匹配,就好比re_path中的有名分组,将匹配的内容传递给y
并且需要注意的是,int这个匹配规则,会将匹配上的数字,转换为int类型(这个写法为 转换器)
4.9 自定义路径转换器
使用django自带的path方法中的匹配规则,有点不太够用的时,就需要自定义转换器
步骤:
- 新建一个 
converters.py文件,里面新建一个类MonConvert 

- 在类中编写代码,to_python,to_url方法必不可少
 
- to_python(self, value)方法,它处理匹配的字符串转换成要传递到视图函数的类型。
 - to_url(self, value)方法,用于处理将Python类型转换为URL中使用的字符串。
 
            
            
              python
              
              
            
          
          from django.urls import path, register_converter
from app01 import views
# 自定义转换器
class MonConvert:
    # 自定义规则(正则表达式)
    regex = '\d\d'
    def to_python(self, value):
        # 这里的value就是匹配的值
        # 可以在这里对匹配的值进行一个操作
        return int(value)
    # 反向解析
    def to_url(self, value):
        return value
        - 将这个类注册在转换器中
 
编写好之后,最好是将这个类放入到一个公共的地方
例如我放在app01下的converter.py中
我需要在app01下的__init__.py中注册这个模块
            
            
              python
              
              
            
          
          # init.py
from app01 import converter
        如果需要编写多个自定义转换器,只需要写多个类即可
- 在路由中使用
 
            
            
              python
              
              
            
          
          # 注册转换器   类,转换器名称
register_converter(MonConvert, 'yyy')
urlpatterns = [
    # <转换器名称:关键字参数名称>
    path('login/<yyy:y>/', views.login, name='login'),
]
        4.10 案例-登陆验证
- 在 
app01 的 urls中配置路由login/: 
            
            
              python
              
              
            
          
          from django.urls import path,re_path
from app01 import views
urlpatterns = [
    re_path('book/(?P<booke_name>\w\w)/', views.book),
    re_path('login/',views.login),
    re_path('index/',views.index)
]
        - 在 
app01 的 views中编写登陆函数: 
            
            
              python
              
              
            
          
          import datetime
from django.shortcuts import render, HttpResponse, redirect
# Create your views here.
def book(request, booke_name):
    print(booke_name)
    return HttpResponse("这是app01:" + booke_name)
def index(request):
    now = datetime.datetime.now().strftime("%Y-%m-%d %X")
    return render(request, "timer.html", {"now": now})
def login(request):
    # 获取get请求参数用这个
    # name = request.GET.get('name')
    # pwd = request.GET.get('pwd')
    # print(name, pwd)
    # 获取post请求参数用这个
    name = request.POST.get('name')
    pwd = request.POST.get('pwd')
    print(name, pwd)
    if name == '张三' and pwd == '1234':
        return redirect('/app01/index/')  # 重定向
    return render(request, 'login.html')
        - 在 
templates模板中创建 登陆页面login.html: 
            
            
              html
              
              
            
          
          <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登陆</title>
</head>
<body>
<form action="" method="post">
    <label for="name">用户名:</label>
    <input type="text" value="请输入用户名" name="name" id="name">
    <br>
    <label for="pwd">密码:</label>
    <input type="text" value="请输入密码" name="pwd" id="pwd">
    <br>
    <input type="submit" value="登陆">
</form>
</body>
</html>
        - 运行,输入url地址:
http://127.0.0.1:8000/app01/login 

当点击登陆时会报错:

这是由于Django的
CSRF安全机制,跨域POST请求时不允许直接使用
将 settings.py中的 MIDDLEWARE的 CsrfViewMiddleware注释掉

重新输入


4.11 拓展------请对与响应对象
请求对象:
- 获取请求路径 
request.path - 请求方式 
request.method - 请求的参数 
request.GETget请求参数 - 请求的主机 
request.get_host() 
响应对象:
- render 返回一个页面
 
            
            
              python
              
              
            
          
          return render(request, 'index.html')  # index.html是在templates中的路径
        - HttpResponse 返回字符串
 
            
            
              python
              
              
            
          
          return HttpResponse("hello world")  # 直接写要返回的字符串
        - redirect 重定向到一个地址
 
            
            
              python
              
              
            
          
          return rediect('/login/')   # 直接写跳转的路径, 如果使用了include,也要补全app的路径