Django视图层——URL配置

参考文献:

https://docs.djangoproject.com/zh-hans/2.0/topics/http/urls/

Django处理url请求的算法

当一个用户请求Django 站点的一个页面,下面是Django 系统决定执行哪个Python 代码使用的算法:

  1. Django确定要使用的根URLconf模块。 通常,这是ROOT_URLCONF设置的值,但如果incomingHttpRequest对象具有urlconf属性(由中间件设置),则将使用其值代替ROOT_URLCONF设置。
  2. Django加载Python模块并查找变量urlpatterns。 这应该是django.urls.path()和/或django.urls.re_path()实例的Python列表。
  3. Django依次匹配每个URL模式,在与请求的URL匹配的第一个模式停下来。
  4. 一旦其中一个URL模式匹配,Django就会导入并调用给定的视图,这是一个简单的Python函数(或基于类的视图)。 视图传递以下参数:
    • 一个 HttpRequest 实例。
    • 如果匹配的URL模式未返回任何命名组,则正则表达式中的匹配将作为位置参数提供。
    • 关键字参数由路径表达式匹配的任何命名部分组成,由django.urls.path()或django.urls.re_path()的可选kwargs参数中指定的任何参数覆盖。
  5. 如果没有URL模式匹配,或者在此过程中的任何点期间引发异常,Django将调用适当的错误处理视图。 请参阅下面的错误处理

简单的URLconf例子:

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>捕获整数参数。 如果未包含转换器,则匹配除/字符之外的任何字符串。
  • 没有必要添加前导斜杠,因为每个URL都有。 例如,它是articles,而不是/articles。

一些请求的例子:

  • A request to /articles/2005/03/ would match the third entry in the list. Django would call the functionviews.month_archive(request, year=2005, month=3).
  • /articles/2003/ would match the first pattern in the list, not the second one, because the patterns are tested in order, and the first one is the first test to pass. Feel free to exploit the ordering to insert special cases like this. Here, Django would call the functionviews.special_case_2003(request)
  • /articles/2003 would not match any of these patterns, because each pattern requires that the URL end with a slash.
  • /articles/2003/03/building-a-django-site/ would match the final pattern. Django would call the functionviews.article_detail(request, year=2003, month=3, slug="building-a-django-site").

注意上面的urlpatterns中的顺序影响并不大,比如将最后一个“articles/<int:year>/<int:month>/<slug:slug>/”放到第一个位置,对后面的结果并没有影响,因为这个是需要完全匹配的,而不是前半部分的匹配就行了。但需要注意"articles/<int:year>/"和“articles/2003/”这两个顺序会有影响。

使用正则表达式

使用re_path()而不是path()来包括正则表达式。

在Python的正则表达式中,对于组的命名格式如下(?P<name>pattern),name是组的命名,pattern是需要匹配的表达式。例子如下:

from django.urls import path, re_path

from . import views

urlpatterns = [
    path('articles/2003/', views.special_case_2003),
    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),
]

需要注意的是每一个捕获的参数传递给view函数是都是字符串形式,而不管匹配的是什么类型。

级联参数

例子:

from django.urls import re_path

urlpatterns = [
    re_path(r'^blog/(page-(\d+)/)?$', blog_articles),                  # bad
    re_path(r'^comments/(?:page-(?P<page_number>\d+)/)?$', comments),  # good
]

两种模式都使用嵌套参数并将解析:例如,blog / page-2 /将导致与blog_articles匹配,其中包含两个位置参数:page-2 /和2.注释的第二个模式将匹配comments / page-2 /将关键字参数page_number设置为2.在这种情况下,外部参数是非捕获参数(?:...)。

blog_articles视图需要反转最外面的捕获参数,在这种情况下需要页面2 /或没有参数,而注释可以使用无参数或page_number的值来反转。

嵌套捕获的参数在视图参数和URL之间创建了一个强大的耦合,如blog_articles所示:视图接收URL的一部分(第2页/),而不仅仅是视图感兴趣的值。这种耦合更加明显。反转,因为要反转视图,我们需要传递一段URL而不是页码。

根据经验,只捕获视图需要使用的值,并在正则表达式需要参数但视图忽略它时使用非捕获参数。

包含其他URL

from django.urls import include, path

urlpatterns = [
    # ... snip ...
    path('community/', include('aggregator.urls')),
    path('contact/', include('contact.urls')),
    # ... snip ...
]

给视图函数传递额外的参数

例子如下:

from django.urls import path
from . import views

urlpatterns = [
    path('blog/<int:year>/', views.year_archive, {'foo': 'bar'}),
]

在上面例子中,对于/blog/2005/的请求,Django对调用views.year_archive(request, year=2005, foo='bar')的视图函数。

给include添加参数

以下两个例子是等效的:

# main.py
from django.urls import include, path

urlpatterns = [
    path('blog/', include('inner'), {'blog_id': 3}),
]

# inner.py
from django.urls import path
from mysite import views

urlpatterns = [
    path('archive/', views.archive),
    path('about/', views.about),
]

上面的例子和下面的等效:

# main.py
from django.urls import include, path
from mysite import views

urlpatterns = [
    path('blog/', include('inner')),
]

# inner.py
from django.urls import path

urlpatterns = [
    path('archive/', views.archive, {'blog_id': 3}),
    path('about/', views.about, {'blog_id': 3}),
]

URL的反向解析

例子如下:

from django.urls import path

from . import views

urlpatterns = [
    #...
    path('articles/<int:year>/', views.year_archive, name='news-year-archive'),
    #...
]

可以在template中通过如下使用来获得url:

<a href="{% url 'news-year-archive' 2012 %}">2012 Archive</a>
{# Or with the year in a template context variable: #}
<ul>
{% for yearvar in year_list %}
<li><a href="{% url 'news-year-archive' yearvar %}">{{ yearvar }} Archive</a></li>
{% endfor %}
</ul>

在python中可以如下使用:

from django.http import HttpResponseRedirect
from django.urls import reverse

def redirect_to_year(request):
    # ...
    year = 2006
    # ...
    return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))

URL命名空间

即使不同的应用程序使用相同的URL名称,URL命名空间也允许您唯一地反向解析出URL模式。 对于第三方应用程序来说,始终使用命名空间URL是一种很好的做法(正如我们在教程中所做的那样)。 同样,如果部署了多个应用程序实例,它还允许您反向URL。 换句话说,由于单个应用程序的多个实例将共享命名URL,因此命名空间提供了一种将这些命名URL分开的方法。

例子如下:

# polls/urls.py

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)

在模板中如下:

{% url 'polls:index' %}

猜你喜欢

转载自www.cnblogs.com/aric-zhu/p/9367304.html
今日推荐