随着Django的更新,最新版本的2.x与旧版本1.x在某些方面有一些不同,在这片博客中我们会提一下,在url地址匹配中的2.x版本与1.x版本的不同之处。
一.路由基础
Django服务开启后,打开浏览器,输入url地址,向服务器发送请求,此时服务器拿到当前请求的url地址(例如:http://127.0.0.1:8801/index),Django通过对项目中的urls模块进行配置,对请求拿到的url地址进行解析,分发至对应的函数中,完成业务逻辑,实现功能。
在urls模块中的 urlpatterns列表中存放着正则路径,视图函数地址,默认关键字参数(可选),路由别名(可选)。
有一个url地址 http://127.0.0.1:8801/login,需要执行的是页面的登录功能,那么在对应的功能模块(app)的views存放着实现这个登录功能的login函数,那么有以下几种可以匹配到此功能的路由写法:
1.功能函数不需要传参
# url(r'^login$', views.login) 只能匹配login,不能匹配index/
# url(r'^login/$', views.login)能匹配login/,也能匹配login(先拿index匹配,如果失败,会添加/再次匹配)
在settings中添加APPEND_SLASH = False 会导致Django不会自动添加/,也可以将settings中将'django.middleware.common.CommonMiddleware'
注释掉
2.功能函数需要传参
#url(r'^login/$', views.login, {'num': 88888}) 此处为关键字参数
#功能函数为如下:
def login(request,num):
pass
有名无名分组
在我们平时浏览网页的时候会发现在一些网址后后面后添加着一些数字(我们将这些页面称之为子页面,这些数字可能是为功能函数提供某个参数,或者是数据库中的某个数据的一个数据),例如:http:www.xxx.xxx/1/2,那么这种句子的匹配应该如何匹配呢,这就需要使用到有名无名分组了。
无名分组:
url(r'^login/(\d+)$',views.login)
#功能函数为:
def login(request,var):
print(var)
return render(request,'login.html')
运行服务器,在浏览器中输入http://127.0.0.1:8802/login/10/这个地址后,会成功跳转至登录页面,并且在Login函数中传入10这个值。
有名分组:
url(r'^login/(?P<num>(\d+))$',views.login)
#功能函数为:
def login(request,num):
print(num)
return render(request,'login.html')
#注意:有名分组中的num名字必须与函数中的参数名对应,否则会参数错误
运行服务器,在浏览器中输入http://127.0.0.1:8802/login/10/这个地址后,会成功跳转至登录页面,并且在Login函数中传入10这个值。
有名与无名分组均可以写对个值,如:
无名分组:
url(r'^login/(\d+)/(\d+)$',views.login)
#功能函数为:
def login(request,var,num):
print(var,num)
return render(request,'login.html')
#最终对应的url如下:http://127.0.0.1:8802/10/20,有名分组与其一致,不过在有名分组中多个参数,在功能函数中只按名字对应,位置前后不会影响
值的顺序。
多app共存的路由分配
多个app创建过程
'''
1.创建多个应用:django-admin startapp app_name | python3 manage.py startapp app_name | Tools工具
2.在settings.py中配置INSTALLED_APPS,添加新建的应用:'app_name.apps.App_nameConfig'
'''
在一个Django中会出现多个功能模块,那么每个app都对应着一个views模块,在里面写着各自的功能代码,有时两个功能模块可能会出现两个名字一致的函数,但是他们实现的功能却是不一致的,如何解决这个问题呢?
import app01.views as app01_views
import app02.views as app02_views
urlpatterns = [
url(r'^app01/test/$', app01_views.test),
url(r'^app02/test/$', app02_views.test),
]
#在导入模块时,将每个不同功能模块的views模块起一个不同的名字,并且url地址也要加上对应的功能模块名,一一对应,即使用方便,也不会出现多app的路由冲突。
模板冲突问题
'''
方法1:在对应应用下建立自己的templates,再在templates下建立与应用名同名的文件夹,模板文件放在应用名同名的文件夹下
方法2:在项目根目录下的templates中建立每一个应用同名的文件夹,每个应用的模板文件放在自己应用名文件夹下
使用:render(request, 'app_name/test.html')
'''
路由分发
当我们不对项目中的urls模块进行处理,会导致随着项目的进度,在urls中堆积的代码会越来越多,不便于我们对于到代码的管理,因此我们使用路由分发解决这个问题。
'''
# 分担总路由的代码压力
1.在每一个应用中建立自身的urls.py文件,语法同主路由
2.在主路由进行分发
from django.conf.urls import include
urlpatterns = [
url(r'^app01/', include('app01.urls')),
url(r'^app02/', include('app02.urls')),
]
# 注:主路由分发一定不能使用$正则语法
'''
路由别名
'''
1.有些路由会被大量访问(直接访问、间接访问)
2.这些路由可能后期还会发生变化
3.可以给路由设置别名,通过别名访问:<a href="{% url '路由别名' '传入有名无名分组所需参数' %}"></a>
'''
反向解析
'''
from django.shortcuts import reverse
在视图函数中通过reverse方法反向解析出真实的路径
# 1.不带分组:url(r'可能会变的真实路由', 视图函数, name='路由别名')
url = reverse('路由别名')
# 2.无名分组:url(r'可能会变的真实路由(带无名分组)', 视图函数, name='路由别名')
url = reverse('路由别名', args=(给无名分组赋值))
# 3.有名分组:url(r'可能会变的真实路由(带有名分组)', 视图函数, name='路由别名')
url = reverse('路由别名', kwargs={给有名分组赋值,key就是有名分组名})
'''
名称空间
'''
主路由:
from django.conf.urls import include
urlpatterns = [
url(r'^app01/', include('app01.urls', namespace='app01')),
url(r'^app02/', include('app02.urls', namespace='app02')),
]
app01应用下路由
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'^test/', views.test, name='test')
]
app02应用下路由
from django.conf.urls import url
from app02 import views
urlpatterns = [
url(r'^test/', views.test, name='test')
]
前端页面反向解析:{% url 'app01:test' %} | {% url 'app02:test' %}
视图函数reverse方法:url = reverse('app01:test') | url = reverse('app02:test')
'''
2.x新特性
'''
from django.urls import path, re_path # 2.x版本
from django.conf.urls import url # 1.x版本,向下兼容,但不建议使用
urlpatterns = [
path('admin/', admin.site.urls),
]
# 1. 2.x版本re_path的使用方式同1.x版本url
# 2. path写的是绝对字符串,请求地址必须与路由地址完全匹配
# 3. path拥有五个转换器:
-- str:匹配除路径分隔符(/)外的字符串,默认
-- int:匹配自然数
-- slug:匹配字母、数字、横杠及下划线组成的字符串
-- uuid:匹配uuid形式的数据
-- path:匹配任何字符串,包括路径分隔符(/) (不能用?)
'''
自定义转换器
'''
1. 在自定义文件中自定义类,采用固定格式作为conventer
eg:在app应用下的conventer文件中建立CVT185Phone类
class CVT185Phone:
regex = '185\d{8}'
def to_python(self, value):
return int(value)
def to_url(self, value):
return '%11d' % value
2. 在路由中注册自定义转换器
from django.urls import register_converter
from app import view, conventer
register_converter(conventer.CVT185Phone, 'phone185') # 自定义转换器名:phone185
urlpatterns = [
path('test/<phone185:phone>/', views.test, name='test_phone'),
]
# 可以匹配138的电话,to_python决定了类型为int,to_url服务于反向解析(反向解析需要使用字符串)
# url = reverse('test_phone', kwargs={'phone': 8888}) # 反向解析传入不满11位,会被格式化位11位
'''