django之路由分发
路由分发决定哪一个路由由哪一个视图函数来处理。
注意:django2.0里的re_path和django1.0里的url除了名字不一样,其他都一样。
from django.urls import path,re_path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path('timer/',views.timer), # 路由配置: 决定哪一个路由由哪一个视图函数来处理 # repath和django1.0里的url用法一样 # 不写^,articles前面可以写任何东西(edward/123/articles);同理,不写$,2003后面也可以写任何东西。 re_path(r'^articles/2003/$',views.special_case_2003), # 正则匹配年份 # 如果是2003,会匹配第一个 # 如果去掉$就不走第三个了,因为匹配到年份那就不往后匹配了,而是直接执行arvhive_year了。 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), ]
注意:
-
若要从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): # 顺序颠倒也没有问题,不管顺序是什么都会正常显示year和month return HttpResponse('year:%s,month:%s' % (year,month))
路由分发
在app01下面重新创建一个urls的文件,把关于app01的url放到里面,然后再在公共项目的urls里导入app01的urls
# 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.archive_year), # year_archive(request,1999)。 re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive), ] # demo urls.py urlpatterns = [ path('admin/', admin.site.urls), path('timer/', views.timer), # 分发: re_path(r'^app01', include('app01.urls')), # re_path(r'^', include('app01.urls') # 路径里面不用写app01了 ]
反向解析
反向解析就是给url起一个别名,我们用别名去找这个url就行,这样不管url如何改变,我们都可以用别名找到它。
urls.py
from django.urls import path, re_path, include from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path('timer/', views.timer), path('login.html/',views.login,name='Log'), # 分发: re_path(r'^', include('app01.urls')), ]
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Login</title> </head> <body> <!-- 在render的时候,去urls里找一个name叫Log的url,这样action里永远都是最新的url --> <form action="{% url 'Log' %}" method="post"> 用户名 <input type="text" name="user"> 密码 <input type="password" name="pwd"> <input type="submit"> </form> </body> </html>
2. 在视图函数里进行反向解析
# app01 urls re_path(r'^articles/2003/$', views.special_case_2003,name ='s_c_2003'), re_path(r'^articles/([0-9]{4})/$', views.archive_year,name='y_a'), from django.urls import reverse def special_case_2003(request): url = reverse('s_c_2003') # 不需要参数,因为不含正则是一个完整的url,找到后直接匹配 url2 = reverse('y_a',args=(4009,)) # ^articles/([0-9]{4})/ 需要给一个四位数字的参数,匹配正则,参数可以是任意四位数字,具体数字按需求写。 print(url) # /articles/2003/ print(url2) # /articles/4009/ return HttpResponse("<h3>special_case_2003</h3>") # HttpResponse里面写的是响应体的内容
命名空间
主项目 urls.py
from django.urls import path, re_path, include from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path('timer/', views.timer), path('login.html/', views.login, name='Log'), # 分发: # 命名空间不可以重名 re_path(r'^app01', include(('app01.urls', 'app01'))), re_path(r'^app02', include(('app02.urls', 'app02'))), ]
app01
# urls re_path('index/',views.index,name='index') # views def index(request): # return HttpResponse(reverse('index')) # 找到的也是app02的index,用名称空间解决这个问题。 # 用命名空间解决 return HttpResponse(reverse('app01:index')) # # 冒号之前是namespace,冒号之后是名字
app02
# urls re_path('index/',views.index,name='index') # views def index(request): # return HttpResponse(reverse('index')) # 找到的app02的index,用命名空间解决名字冲突的问题 return HttpResponse(reverse('app02:index'))
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自带的转换器是不能满足我们的需求的,这就需要自定义转换器了
操作步骤:
1.在app里创建一个类,写自定义转换的规则
url_convert.py
class MonConvert: regex = "[0-9]{2}" # 只能叫regex def to_python(self, value): return int(value) # 不转化就是字符串,可以根据需求做任何操作。 def to_url(self, value): # 用于反向解析 return "%04d" % value
2.在urls里注册自定义转换器
from django.urls import path, re_path, include,register_converter from app01.url_convert import MonConvert # 注册自定义转换器 register_converter(MonConvert,'mm')
3.在路由分发里使用自定义转换器
urlpatterns = [ path('articles/<mm:month>',views.path_month), ]
Registering custom path converters¶
For more complex matching requirements, you can define your own path converters.
A converter is a class that includes the following:
- A
regex
class attribute, as a string. - A
to_python(self, value)
method, which handles converting the matched string into the type that should be passed to the view function. It should raiseValueError
if it can’t convert the given value. - A
to_url(self, value)
method, which handles converting the Python type into a string to be used in the URL.