【6.0】Django框架之路由层

发布时间 2023-07-17 11:35:08作者: Chimengmeng

【一】路由匹配

# 路由匹配
path('test', views.test),
path('testadd', views.testadd),

无法跳转到 testadd

  • url方法第一个参数是正则表达式
    • 只要第一个参数正则表达式能够匹配到内容,就会立刻停止匹配,执行视图函数
# 路由匹配
path('test/', views.test),
path('testadd/', views.testadd),

在参数尾部加一个 /

  • 在输入 url 的时候会默认加一个 /
    • Django内部会帮助我们做一个重定向
      • 一次匹配不行
      • 那就加一个 / 再尝试一次
  • 在配置文件中,有一个参数可以帮助我们干这件事
APPEND_SLASH = True
  • 这个参数默认为True,即自动帮我们加 /
re_path('^test/$', views.test),

这才是完整版的路由匹配格式

re_path('^$', views.test),

匹配首页的路由格式

【二】无名分组

  • 分组就是将某段正则表达式用()括起来
# 无名分组
re_path(r'^text/(\d+)/', views.test),
def test(request,data):
    print(data)
    return HttpResponse('test')
  • 无名分组就是将括号内正则表达式匹配到的内容当做位置参数传给后面的视图函数

【三】有名分组

  • 可以给正则表达式起一个别名
# 有名分组
re_path(r'^testadd/(?P<year>\d+)', views.testadd),
def testadd(request, year):
    print(year)

    return HttpResponse("testadd")
  • 有名分组就是将括号内正则表达式匹配到的内容当做关键字参数传给后面的视图函数

【四】无名有名混用

无名分组和有名分组不能混用

但是同一个分组可以使用多次

# 无名有名混合使用
re_path(r'^index/(\d+)/(?P<year>\d+)/', views.index),
def index(request,args,year):
    return HttpResponse("index")

【五】反向解析

  • 通过一些方法得到一个结果,该结果可以直接访问到对应的url,触发视图函数

  • 先给路由与视图函数起一个别名

    re_path(r'func/', views.func,name="xxxx"),
    
  • 反向解析

    • 后端反向解析

      from django.shortcuts import render, HttpResponse,reverse
      def home(request):
          reverse("xxxx"))
      
    • 前端反向解析

      <a href="{% url 'xxxx' %}">111</a>
      

【六】无名有名分组反向解析

本质上还是通过一些方法得到一个结果,该结果可以访问到对应的url从而触发相应的视图和功能

【1】无名分组反向路由解析

(1)引入

1.1 路由

# 无名分组反向解析
    re_path(r'^index/(\d+)/', views.index, name='xxx'),

1.2 视图

def home(request):
    print(reverse('xxx')
    return render(request, 'home.html')

1.3 前端

<a href="">{% url 'xxx' %}</a>

我们会发现这样跳转到我们的 xxx 指定路由字段的时候会报错,提示匹配不到我们写的路由表单式

这是因为我们返回数据的时候,因为我们的路由正则表达式是 r'^index/(\d+)/'路由地址后面是需要跟数字的

我们现在在根路由的后面不跟参数,就不能被匹配到,所以主动抛出异常

(2)解决办法

2.1 路由

# 无名分组反向解析
    re_path(r'^index/(\d+)/', views.index, name='xxx'),

2.2 视图

def home(request):
    print(reverse('xxx', args=(1,)))
    return render(request, 'home.html')

2.3 前端

<a href="">{% url 'xxx' 123 %}</a>

当我们跳转到指定路由地址的时候,在后面携带参数,这样就可以避免像上面一样,抛出异常,匹配不到

(3)小结

  • 这个数字存在的意义到底是什么?
    • 数字一般情况下放的是数据的主键值
    • 数据的标记和删除功能

通过在根路径的后面拼接指定的 地址 ,从而对这个指定的地址进行数据的更改

# 一个小的实例演示

# 路由层
re_path('edit/<int:pk>/',views.edit,name='edit')

# 视图层
def edit(request,edit_id):
    reverse("edit",args=(edit_id,))

# 前端
{% for user.obj in user_queryset %}
<a href="{% url 'eidt' user_obj.id %}">
{% endfor %}

【2】有名分组反向解析

(1)问题引入

2.1 路由

# 有名分组反向解析
re_path(r'^fuc/(?P<year>\d+)', views.func, name='ooo')

2.2 视图

def func(request,year):
    print(reverse('000')
    return render(request, 'home.html')

问题同上面,还是没有匹配到符合正则表达式的路由

因为我们的正则表达式是 r'^func/(?P<year>\d+)'

理论上我们传入的数据格式应该对year进行指定 func/year=1/

(2)解决方案

2.1 路由

# 有名分组反向解析
re_path(r'^fuc/(?P<year>\d+)', views.func, name='ooo')

2.2 视图

def home(request,year):
    # print(reverse('xxx', args=(1,)))
    # 有名分组的标准写法
    # print(reverse('ooo', kwargs={"year": 123}))
    # 简便写法
    print(reverse('xxx', args=(1,)))
    return render(request, 'home.html')

2.3 前端

<a href="">{% url 'ooo' year=123 %}</a>有名分组

当我们向指定的形参进行参数传递时,就能被指定的路由匹配表达式所匹配,从而访问到指定的页面

【七】路由分发

  • Django每一个应用都可以拥有属于自己的

    • templates文件夹
    • urls.py
    • static文件夹
  • 正是基于上述的特点,Django可以很好的做到自己的分组开发(每个人只写自己的app)

  • 最后只需要将所有的app拷贝到新的文件,并将这些APP注册到配置文件中,然后再利用路由分发的特点,将所有的APP整合起来

  • 当一个项目中的URL特别多的时候,总路由urls.py的代码非常冗余而且不好维护,这个时候就可以利用路由分发来减轻总路由的压力

  • 利用路由分发之后,总路由不再干预路由与视图函数的直接对应关系

    • 而是做一个分发处理
    • 识别当前的url是属于哪个应用下的,直接分发给对应的应用去处理

【1】总路由

from django.contrib import admin
from django.urls import path, re_path, include

import app01
import app02

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/', include(app01.urls)),
    path('app02/', include(app02.urls)),
]

总路由尾部不能加$,否则会中断匹配,导致匹配失败

【2】子路由

# 自己的逻辑路径

【八】名称空间

  • 当多个应用出现相同的别名,反向解析不会自动识别应用前缀
  • 正常情况下的反向解析是不能识别前缀的
# 路由
urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/', include(app01.urls), namespace='app01'),
    path('app02/', include(app02.urls), namespace='app02'),

]
# 视图解析
reverse('app01:reg')
reverse('app02:reg')

# 前端
{% url 'app01:reg' %}
{% url 'app02:reg' %}

只要保证名字不冲突,就没必要使用名称空间

一般情况下,有多个app的时候只要在前面加上app的前缀,这样就能够确保多个app之间名字不冲突

【九】伪静态

  • 静态网页
    • 数据是写死的
  • 伪静态
    • 将一个动态网页伪装成静态网页
  • 伪装的目的在于增大本网站的seo查询力度
    • 并且增加搜索引擎收藏本网页的概率
  • 搜索引擎本质上就是一个巨大的爬虫程序

总结:无论怎么优化,怎么处理,始终还是干不过RMB玩家

【十】虚拟环境

  • 在正常开发中,我们会给每一个项目独有的解释器环境
  • 该环境内只有该项目用到的模块,用不到的一概不装

虚拟环境:

​ 每创建一个虚拟环境就类似于重新下载了一个纯净的python解释器

​ 但是虚拟器不建议下载太多,创建虚拟环境是需要消耗磁盘空间的

模块管理文件:

​ 每个项目都需要用到很多模块,并且每个模块的版本可能是不一样的

​ 这种情况下我们会给每一个项目配备一个requirements.txt文件,里面存放着我们这个项目所安装的所有模块及版本

​ 只需要一条命令即可安装所有模块及版本

【十一】Django版本的区别

【1】路由匹配规则

  • Django1.x路由层使用的是url方法
  • 在Django2.x版本以后在路由层使用的是path方法
    • url() 第一个参数支持正则
    • path() 第一个参数不支持正则,写什么就匹配到什么

【2】正则匹配规则

  • 在Django2.x以后也可以使用正则表单式,但是使用的方法是re_path
from django.urls import path, re_path

re_path(r'^fuc/(?P<year>\d+)', views.func)

# 等价于

url(r'^fuc/(?P<year>\d+)', views.func)

【3】转换器

  • 虽然path不支持正则,但是其内部支持五种转换器
path('index/<int:id>/',views.index)
# 将第二个路由里面的内容先转成整型,然后以关键字的形式传递给后面的视图函数
from django.urls import path,re_path

from app01 import views

urlpatterns = [
    # 问题一的解决方案:
    path('articles/<int:year>/', views.year_archive), # <int:year>相当于一个有名分组,其中int是django提供的转换器,相当于正则表达式,专门用于匹配数字类型,而year则是我们为有名分组命的名,并且int会将匹配成功的结果转换成整型后按照格式(year=整型值)传给函数year_archive


    # 问题二解决方法:用一个int转换器可以替代多处正则表达式
    path('articles/<int:article_id>/detail/', views.detail_view), 
    path('articles/<int:article_id>/edit/', views.edit_view),
    path('articles/<int:article_id>/delete/', views.delete_view),
]
str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
int,匹配正整数,包含0。
slug,匹配字母、数字以及横杠、下划线组成的字符串。
uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)
path('articles/<int:year>/<int:month>/<slug:other>/', views.article_detail) 
# 针对路径http://127.0.0.1:8000/articles/2009/123/hello/,path会匹配出参数year=2009,month=123,other='hello'传递给函数article_detail

【4】自定义转换器

  • Django支持自定义转换器

自定义转换器示例:

  1. 在app01下新建文件path_ converters.py,文件名可以随意命名

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

    Copyfrom django.urls import path,register_converter
    from app01.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'),
    
    ]
    
  3. views.py中的视图函数article_detail

    Copyfrom django.shortcuts import render,HttpResponse,reverse
    
    def article_detail(request,year,month,other):
        print(year,type(year))
        print(month,type(month))
        print(other,type(other))
        print(reverse('xxx',args=(1988,12,'hello'))) # 反向解析结果/articles/1988/12/hello/
        return HttpResponse('xxxx')
    
  4. 测试

    Copy# 1、在浏览器输入http://127.0.0.1:8000/articles/2009/12/hello/,path会成功匹配出参数year=2009,month=12,other='hello'传递给函数article_detail
    # 2、在浏览器输入http://127.0.0.1:8000/articles/2009/123/hello/,path会匹配失败,因为我们自定义的转换器mon只匹配两位数字,而对应位置的123超过了2位
    

【5】级联更新

  • 模型层在1.x外键默认都是级联更新,级联删除的
  • 但是2.x 版本以后需要自己手动配置参数