工作原理
django
通过urlconf
来映射视图函数,只区分路径,不区分http
方法
Django
确定要使用的根URLconf
模块,一般是在settings
中的ROOT_URLCONF
设置的值- 如果传入
HttpRequest
对象具有一个urlconf
属性(由中间件设置),则其值将用于代替ROOT_URLCONF
设置。 Django
加载该URLconf
模块并查找变量urlpatterns
,它是一个列表django.urls.path()
和/
或django.urls.re_path()
实例。Django
按顺序遍历每个URL
模式,并停在与请求的URL
匹配的第一个URL
模式,需要特别注意编写的顺序- 一旦某个
URL
模式匹配,Django
就会导入并调用给定的视图,该视图是一个简单的Python
函数(或基于类的视图方法)。 - 如果没有
URL
模式匹配,或者在此过程中的任何点发生异常,Django
将调用适当的错误处理视图
使用URL调度器
from django.urls import path
from . import views
urlpatterns = [
path('articles/2003/', views.special_case_2003),
path('articles/<int:year>/', views.year_archive),
path('articles/<int:year>/<int:month>/', views.month_archive),
path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail),
]
-
从
URL
中捕获值,使用尖括号<>
-
捕获的值可以选择包含转换器类型。例如,用于
/<int:name>
捕获,前面的int
指整数参数,name
是参数的名称 -
没有必要添加一个前导斜杠,因为每个
URL
都有,例如,使用articles
而不是/articles
。
路径转换器
str
:匹配任何非空字符串,不包括路径分隔符/
。如果转换器不包含在表达式中,这是默认值。int
:匹配零或任何正整数。返回一个int
。slug
:匹配由ASCII
字母或数字
组成的字符串,以及横线
和下划线
字符。例如,building-your-1st-django_site
。uuid
:匹配格式化的UUID
。为防止多个URL
映射到同一页面,必须包含破折号
,并且字母必须是小写
。例如,075194d3-6885-417e-a8a8-6c931e272f00
。返回一个UUID
实例。path
:匹配任何非空字符串,包括路径分隔符/
,可以匹配完整的URL
路径,而不仅仅是URL
路径的一部分str
,使用时要谨慎,因为可能造成后续的所有url
匹配都失效。
自定义路径转换器
转换器是一个包含以下内容的类:
- 一个
regex
类属性,作为一个re
匹配字符串。 to_python(self, value)
方法,它处理匹配的字符串
转换成要传递到视图函数的类型
。to_url(self, value)
方法,用于处理将Python
类型转换为URL
中使用的字符串。
定义方式如下:
-
新建一个
converters.py
文件,在文件中定义一个FourDigitYearConverter
类:class FourDigitYearConverter(object): regex = '[0-9]{4}' def to_python(self, value): return int(value) def to_url(self, value): return '%04d' % value
-
使用
register_converter()
方法在URLconf
中注册自定义转换器类 :from django.urls import register_converter, path from . import converters, views register_converter(converters.FourDigitYearConverter, 'yyyy') urlpatterns = [ path('articles/<yyyy:year>/', views.year_archive) ]
使用正则表达式
- 使用正则表达式匹配路径,使用
re_path()
而不是path()
- 在
Python
正则表达式中,命名正则表达式组的语法是(?P\<name>pattern)
,其中name
是组的名称,并且pattern
是一些要匹配的模式 - 无论正则表达式匹配什么类型,每个捕获的参数都以
字符串
的形式发送到视图
from django.urls import path, re_path
from . import views
"""
# url() 是 re_path 的别名,不推荐使用
"""
urlpatterns = [
re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
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<slug>[\w-]+)/$', views.article_detail),
]
- 路径中嵌套参数:
from django.urls import re_path
urlpatterns = [
# 匹配:comments/page-2/ 路径到 comments(request, page_numer)
re_path(r'^comments/(?:page-(?P<page_number>\d+)/)?$', comments),
]
URL调度器默认值
- URLConf中:
from django.urls import path
from . import views
urlpatterns = [
# http://127.0.0.1:8000/polls/blog/ 等同于 http://127.0.0.1:8000/polls/blog/1/
path('blog/', views.page),
# http://127.0.0.1:8000/polls/blog/1/
# http://127.0.0.1:8000/polls/blog/10/
# http://127.0.0.1:8000/polls/blog/99/
path('blog/<int:num>/', views.page),
]
- views中:
"""
默认值:num=1
"""
def page(request, num=1):
# 编写对应的业务逻辑
pass
错误处理
handler400
- 状态码400handler403
- 状态码403handler404
- 状态码404handler500
- 状态码500
-
在
settings
中修改配置:DEBUG = False ALLOWED_HOSTS = ['*', ]
-
在主应用的
urls
中配置:# polls是子应用 handler404 = "polls.views.page_not_found"
-
在
polls
应用的views
中添加函数page_not_found
:def page_not_found(request, exception): return HttpResponse('自定义的404错误页面')
引用其他URL调度器
1. 多个 patterns
from django.urls import include, path
extra_patterns = [
path('reports/', credit_views.report),
path('reports/<int:id>/', credit_views.report),
path('charge/', credit_views.charge),
]
urlpatterns = [
path('', main_views.homepage),
path('help/', include('apps.help.urls')),
path('credit/', include(extra_patterns)),
]
2. 使用include消除重复前缀
from django.urls import path
from . import views
urlpatterns = [
path('<page_slug>-<page_id>/history/', views.history),
path('<page_slug>-<page_id>/edit/', views.edit),
path('<page_slug>-<page_id>/discuss/', views.discuss),
path('<page_slug>-<page_id>/permissions/', views.permissions),
]
# 修改为:
from django.urls import include, path
from . import views
urlpatterns = [
path('<page_slug>-<page_id>/', include([
path('history/', views.history),
path('edit/', views.edit),
path('discuss/', views.discuss),
path('permissions/', views.permissions),
])),
]
3. 传递捕获的参数
- 在主
urls
中配置:
urlpatterns = [
path('admin/', admin.site.urls),
# 这里捕获username参数,类型为字符串
path('<username>/polls/', include('polls.urls'))
]
- 对应的
polls
应用下的urls
中配置:
urlpatterns = [
path('arg_test/', views.arg_test),
]
- 对应的
polls
应用下的views
中编写函数:
def arg_test(request, username):
# 编写对应的业务逻辑
pass
额外的参数
- 调度器路径中可添加额外参数,可被模板引用
from django.urls import path
from . import views
urlpatterns = [
path('blog/<int:year>/', views.year_archive, {'foo': 'bar'}),
]
URL反向解析
url调度器
除了从用户发起请求,到匹配对应的view
,还能在python
程序中调用进行匹配,通过 path
或re_path
中 的name
属性进行解析
- 在
模板
中,使用url
模板标签 - 在
Python
代码中(主要是views),使用reverse()
函数 - 在
模型实例
中,使用get_absolute_url()
方法
1. urls中配置:
from django.urls import path
from . import views
urlpatterns = [
path('articles/<int:year>/', views.year_archive, name='news-year-archive'),
]
2. 在模板中使用url模板标签
<a href="{% url 'news-year-archive' 2012 %}">2012 Archive</a>
<ul>
{% for yearvar in year_list %}
<li>
<a href="{% url 'news-year-archive' yearvar %}">{{ yearvar }} Archive</a>
</li>
{% endfor %}
</ul>
3. 在views
中使用 reverse()
函数
from django.urls import reverse
from django.http import HttpResponseRedirect
def redirect_to_year(request):
year = 2006
return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))
4. 在模型中使用 get_absolute_url()
方法:
- 在模型中实现方法:
def get_absolute_url(self):
from django.urls import reverse
return reverse('news-year-archive', args=[str(self.id)])
- 模板中使用
<a href="{{ object.get_absolute_url }}">{{ object.name }}</a>
命名空间
-
主要用于配合
url反向解析
使用,多个不同的urls
文件中可能配置同名的name
,那么为了进行区分,给不同的urls
进行不同的命名 -
同一个项目下命名空间不能重复!
-
通过在
url调度器
的模块中,定义app_name = 'polls'
来命名 -
定义:一般命名空间和子应用名相同,便于记忆
-
调用:一旦有了命名空间,调用时就必须使用
polls: 前缀
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
...
]
reverse('polls:index', current_app=self.request.resolver_match.namespace)
- 命名空间可以进行嵌套:
在 urls 中配置:
from django.urls import path
from . import views
"""
定义,一般命名空间和子应用名相同,便于记忆
"""
app_name = 'polls'
extra_patterns = (
[
path('app_name/', views.app_name, name='app_name'),
],
"""
此处就是嵌套的命名空间
"""
'extra'
)
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
path('extra/', include(extra_patterns)),
...
]
在模板中使用:
<a href="{% url 'polls:extra:app_name' %}">点击链接</a>