Django-路由层URLconf
URL配置(URLconf) 本质是URL与要为该URL调用的视图函数之间的映射表
简单的路由配置
from django.urls import path,re_path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path('timer/',views.timer), # 导入re_path;^以什么开头,$是控制尾意思是不再加什么东西 re_path(r'^articles/2003/$',views.special_case_2003), re_path(r'^articles/([0-9]{4})/$',views.archive_year), # year_archive(request,1999)。django2.1.5不用传第二个参数了 re_path(r'^articles/([0-9]{4})/([0-9]{2})/$',views.month_archive), # re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail),
([0-9]{4})分组(任意四个数字的组合,都可以运行),会以位置参数传给后面的函数,
]
注意:
-
若要从URL 中捕获一个值,只需要在它周围放置一对圆括号。
-
不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是
^articles
而不是^/articles
。 -
每个正则表达式前面的'r' 是可选的但是建议加上。它告诉Python 这个字符串是“原始的” —— 字符串中任何字符都不应该转义
# urls # 有名分组,?P<year>相当于是给参数起了个名字,在视图函数传参数名即可,不需要关注顺序 re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$',views.month_archive), ''' month_archive(request,year=2009,momth=12) 这样在views视图,不管传入的参数的顺序是什么,year就是year,month就是month,但是名字必须是year和month ''' #views def month_archive(request, month,year): # 参数名需正确,顺序怎样都不影响 return HttpResponse('year:%s,month:%s' % (year,month))
路由分发
URL为更好解耦,应用创建一个新的urls.py文件,存放与自己相关的urls;然后再在公共项目的urls里导入应用的urls
导入include分发函数
#demo urls.py
from django.contrib import admin from django.urls import path,re_path,include from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path('timer/', views.timer), # 路由分发 re_path(r"app01/",include("app01.urls")), # 第一种形式:是加着app01应用 re_path(r"^",include("app01.urls")), # 第二种形式,不用加app01应用即可执行 ]
#app01 urls.py
from django.urls import path,re_path from app01 import views urlpatterns = [ re_path(r'^articles/2003/$', views.special_case_2003), #^控制头,$控制尾(不再加其他东西) re_path(r'^articles/([0-9]{4})/$', views.year_archive), #([0-9]{4})分组(任意四个数字的组合,都可以运行),会以位置参数传给后面的函数 re_path(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive), # re_path(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail), re_path(r'^articles/(?P<y>[0-9]{4})/(?P<m>[0-9]{2})/(?P<d>[0-9]+)/$', views.article_detail), ]
反向解析
方法一:给url起一个别名,后嵌入到html中;每次改变URL名字,嵌入到html中的别名均不需改
#urls from django.contrib import admin from django.urls import path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path('timer/', views.timer), path('login/',views.login,name='LOG') #给这个URL起的别名 name='LOG' ] # view def login(request): if request.method == 'GET': return render(request,"login.html") else: print(request.GET) print(request.POST) user=request.POST.get("user") pwd=request.POST.get("pwd") if user=='yuan' and pwd=='123': return HttpResponse("OK") else: return HttpResponse('登录失败') #html <form action="{% url 'LOG' %}" method="post"> # 格式 action="{% url 'LOG'%}",到 urls中找一个别名为 LOG的url 用户名<input type="text" name="user"> 密码<input type="password" name="pwd"> <input type="submit"> </form>
方法二:利用reverse反向解析函数,通过别名,解析出URL
#app01/urls.py
from django.contrib import admin from django.urls import path,re_path,include from app01 import views urlpatterns = [ #分发的url,在反向解析的时候 会加上app01 re_path(r'^articles/2003/$', views.special_case_2003,name='s_c_2003'), re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive,name='y_a'), re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive), # re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail), ]
#view
def special_case_2003(request): url=reverse("s_c_2003") #利用reverse反向解析函数,通过别名,解析出URL(在哪个函数中都无所谓);此URL不含正则,固不需参数 url=reverse('y_a',args=(1003,)) # articles/(?P<year>[0-9]{4}) 有正则表达式,需用一个参数,用一个4位数代替 print(url) # 获取的URL /app01/articles/1003/ return HttpResponse("special_case_2003") # HttpResponse里面写的是响应体的内容
名称空间
在项目中可能会在不同应用遇见相同的url别名,会反解错误;为避免反解错误,可以给应用添加不同的名称空间
first_pro/urls.py
from django.contrib import admin from django.urls import path,re_path,include from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path('timer/', views.timer), path('login/',views.login,name='LOG'), #分发 re_path(r"^app01/",include(("app01.urls","app01"))),#这条分发放置在app01这个名称空间里 re_path(r"^app02/",include(("app02.urls",'app02'))),#名称空间不能重名 ]
app01/url.py
from django.urls import path,re_path,include from app01 import views urlpatterns = [ #分发的url,在反向解析的时候 会加上app01 re_path(r'^articles/2003/$', views.special_case_2003,name='s_c_2003'), re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive,name='y_a'), re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive), # re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail), re_path('index/',views.index,name='index') ]
app01/views
def index(request): return HttpResponse(reverse("app01:index")) #加上名称空间app01
---------------app02同样操作---------------
#app02/urls from django.urls import path,re_path,include from app02 import views urlpatterns = [ re_path("index/",views.index,name='index') ] #app02/views.py from django.urls import reverse def index(request): return HttpResponse(reverse("app02:index")) #加上名称空间app02
path方法(django2.0)
re_path有两个问题:
1.不能进行字符转换。
def month_archive(request, month, year): print(type(month)) # str print(type(year)) # str return HttpResponse('year:%s,month:%s' % (year, month))
2.同样的正则表达式,需要写多遍,不易于维护。
urlpatterns = [ re_path('articles/(?P<year>[0-9]{4})/', year_archive), re_path('article/(?P<article_id>[a-zA-Z0-9]+)/detail/', detail_view), re_path('articles/(?P<article_id>[a-zA-Z0-9]+)/edit/', edit_view), re_path('articles/(?P<article_id>[a-zA-Z0-9]+)/delete/', delete_view), ]
path可以解决这两个问题
# urls from django.urls import path path('articles/<int:year>',views.path_year) #views def path_year(request,year): print(type(year)) # int return HttpResponse('path year')
基本规则:
-
使用尖括号(
<>
)从url中捕获值。 -
捕获值中可以包含一个转化器类型(converter type),比如使用
<int:name>
捕获一个整数变量。若果没有转化器,将匹配任何字符串,当然也包括了/
字符。 -
无需添加前导斜杠。
path转换器
Django默认支持以下5个转化器:
-
str,匹配除了路径分隔符(
/
)之外的非空字符串,这是默认的形式 -
int,匹配正整数,包含0。
-
slug,匹配字母、数字以及横杠、下划线组成的字符串。
-
uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
-
path,匹配任何非空字符串,包含了路径分隔符
自定义path转换器
在实际开发中,django自带的转换器是不能满足我们的需求的,这就需要自定义转换器了
对于一些复杂或者复用的需要,可以定义自己的转化器。转化器是一个类或接口,它的要求有三点:
- regex 类属性,字符串类型
- to_python(self, value) 方法,value是由类属性 regex 所匹配到的字符串,返回具体的Python变量值,以供Django传递到对应的视图函数中。
- to_url(self, value) 方法,和 to_python 相反,value是一个具体的Python变量值,返回其字符串,通常用于url反向引用。
操作步骤:
1.在app里创建一个类,写自定义转换的规则
url_convert.py
class MonConvert: regex="[0-9]{2}" # regex def to_python(self,value): return str(value) def to_url(self,value): # 用于反向解析 return '%04d' % value
first_pro/urls 注册自定义URL转换器
from django.contrib import admin from django.urls import path,re_path,include,register_converter #register_converter帮你把写好的转换器注册进去 from app01.urlconvert import MonConvert #导入MonConvert; #注册自定义URL转换器,mm是自定义转化器的名字 register_converter(MonConvert,'mm') from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path('timer/', views.timer), path('login/',views.login,name='LOG'), path("articles/<mm:month>",views.path_month), # 注意是<mm:month>,路由分发,执行视图函数 ]
app01/view.py
def path_month(request,month): print(month) print(type(month)) return HttpResponse('path month')