Django学习笔记4

路由系统(从属于视图层)

Django如何处理请求

当一个用户请求Django 站点的一个页面,下面是Django 系统决定执行哪一个Python  url路径对应代码遵循的算法:html

  1. Django 决定要使用的根URLconf 模块。(最外层根路径,url从什么地方开始匹配一般,这是ROOT_URLCONF设置的值,可是若是传入的HttpRequest对象具备urlconf属性(由中间件设置),则其值将被用于代替ROOT_URLCONF设置。
  2. Django 加载该Python 模块并寻找可用的urlpatterns。 它是django.conf.urls.url() 实例的一个Python 列表。
  3. Django 依次匹配每一个URL 模式,在与请求的URL 匹配的第一个模式停下来。
  4. 一旦正则表达式匹配,Django将导入并调用给定的视图,该视图是一个简单的Python函数(或基于类的class-based view)。 视图将得到以下参数:
    • 一个HttpRequest 实例。
    • 若是匹配的正则表达式返回了没有命名的组,那么正则表达式匹配的内容将做为位置参数提供给视图。(位置参数命名组)
    • 关键字参数由正则表达式匹配的命名组组成,可是能够被django.conf.urls.url()的可选参数kwargs覆盖。(关键字参数命名组)
  5. 若是没有匹配到正则表达式,或者若是过程当中抛出一个异常,Django 将调用一个适当的错误处理视图。

url配置

对于高质量的Web 应用来讲,使用简洁、优雅的URL 模式是一个很是值得重视的细节。 Django 让你为所欲为设计你的URL,不受框架束缚。python

下面是一个简单的 URLconf:web

from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^articles/2003/$', views.special_case_2003),
    url(r'^articles/([0-9]{4})/$', views.year_archive),
    url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),
    url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),
]

注:正则表达式

  • 若要从URL中捕获一个值,只须要在它周围放置一对圆括号(分组匹配)。
  • 不须要添加一个前导的反斜杠,由于每一个URL 都有。 例如,应该是^articles 而不是 ^/articles
  • 每一个正则表达式前面的'r' 是可选的可是建议加上。 它告诉Python 这个字符串是“原始的” —— 字符串中任何字符都不该该转义。
  • 是否开启URL访问地址后面不为/跳转至带有/的路径的配置项 -----------------------APPEND_SLASH=True算法

  • Django 2.0版本中的路由系统已经替换成下面的写法
from django.urls import path

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),
]

一些请求的例子:django

  • /articles/2005/03/ 请求将匹配列表中的第三个模式。 Django 将调用函数views.month_archive(request, '2005', '03')
  • /articles/2005/3/ 不匹配任何URL 模式,由于列表中的第三个模式要求月份应该是两个数字。
  • /articles/2003/ 将匹配列表中的第一个模式不是第二个,由于模式按顺序匹配,第一个会首先测试是否匹配。 请像这样自由插入一些特殊的状况来探测匹配的次序。 这里,Django会调用函数views.special_case_2003(request)
  • /articles/2003 不匹配任何一个模式,由于每一个模式要求URL 以一个斜线结尾。
  • /articles/2003/03/03/ 将匹配最后一个模式。 Django 将调用函数views.article_detail(request, '2003', '03', '03')

命名组

上面的示例使用简单的、没有命名的正则表达式组(经过圆括号)来捕获URL 中的值并以位置 参数传递给视图。 在更高级的用法中,可使用命名的正则表达式组来捕获URL 中的值并以关键字 参数传递给视图。app

在Python 正则表达式中,命名正则表达式组的语法是(?P<name>pattern),其中name 是组的名称,pattern 是要匹配的模式。框架

下面是以上URLconf 使用命名组的重写:函数

from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^articles/2003/$', views.special_case_2003),
    url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
    url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
    url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail),
]

这个实现与前面的示例彻底相同,只有一个细微的差异:捕获的值做为关键字参数而不是位置参数传递给视图函数。 像这样:工具

  • /articles/2005/03/ 请求将调用views.month_archive(request, year='2005', month='03')函数,而不是views.month_archive(request, '2005', '03')
  • /articles/2003/03/03/ 请求将调用函数views.article_detail(request, year='2003', month='03', day='03')

在实际应用中,这意味你的URLconf 会更加明晰且不容易产生参数顺序问题的错误 —— 你能够在你的视图函数定义中从新安排参数的顺序。 固然,这些好处是以简洁为代价的;一些开发人员发现命名组语法丑陋并且太冗长。

匹配/分组算法

下面是URLconf 解析器使用的算法,针对正则表达式中的命名组和非命名组:

  1. 若是有命名参数,则使用这些命名参数,忽略非命名参数。
  2. 不然,它将以位置参数传递全部的非命名参数。

根据传递额外的选项给视图函数(下文),这两种状况下,多余的关键字参数也将传递给视图。

URLconf匹配的位置

URLconf 在请求的URL 上查找,将它当作一个普通的Python 字符串。不包括GET和POST参数以及域名。

例如,http://www.example.com/myapp/ 请求中,URLconf 将查找myapp/。

在http://www.example.com/myapp/?page=3 请求中,URLconf 仍将查找myapp/

URLconf 不检查请求的方法。换句话讲,全部的请求方法 —— 同一个URL的POSTGETHEAD等等 —— 都将路由到相同的函数。

捕获的参数老是字符串

每一个捕获的参数都做为一个普通的Python 字符串传递给视图,不管正则表达式使用的是什么匹配方式。 例如,下面这行URLconf 中:

url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
...传递给 views.year_archive()year参数将是一个字符串,不是整数,即便 [0-9]{4}只匹配整数字符串。

指定视图参数的默认值

有一个方便的小技巧是指定视图参数的默认值。 下面是一个URLconf 和视图的示例:

# URLconf
from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^blog/$', views.page),
    url(r'^blog/page(?P<num>[0-9]+)/$', views.page),
]

# View (in blog/views.py)
def page(request, num="1"):
    # Output the appropriate page of blog entries, according to num.
    ...

  在上面的例子中,两个URL模式指向同一个视图views.page —— 可是第一个模式不会从URL 中捕获任何值。 若是第一个模式匹配,page() 函数中num将使用默认的参数值"1"。 若是第二个模式匹配,page() 将使用正则表达式捕获的num 值。

错误处理

当Django 找不到一个匹配请求的URL 的正则表达式时,或者当抛出一个异常时,Django 将调用一个错误处理视图。

这些状况发生时使用的视图经过4个变量指定。 它们的默认值足以知足大多数项目,但能够经过覆盖其默认值进一步进行自定义。

完整的细节请参见自定义错误视图

这些值能够在你的根URLconf 中设置。 在其它URLconf 中设置这些变量将不会产生效果。

它们的值必须是可调用的或者是表示视图的Python 完整导入路径的字符串,能够方便地调用它们来处理错误状况。

这些值是:

url分发

include包含其余URLconfs 进行分发

在任什么时候候,你的urlpatterns 均可以包含其它URLconf 模块。 这实际上将一部分URL 放置于其它URL 下面。

例如,这里是Django网站自己的URLconf摘录。 它包含许多其它URLconf:

from django.conf.urls import include, url

urlpatterns = [
    # ... snip ...
    url(r'^community/', include('django_website.aggregator.urls')),
    url(r'^contact/', include('django_website.contact.urls')),
    # ... snip ...
]

  注意,这个例子中的正则表达式没有包含$(字符串结束匹配符),可是包含一个末尾的斜杠。 每当Django 遇到include()django.conf.urls.include())时,它会去掉URL 中匹配的部分并将剩下的字符串发送给包含的URLconf 作进一步处理。

另一种包含其它URL 模式的方式是使用一个url() 实例的列表。 例如,请看下面的URLconf:

from django.conf.urls import include, url

from apps.main import views as main_views
from credit import views as credit_views

extra_patterns = [
    url(r'^reports/$', credit_views.report),
    url(r'^reports/(?P<id>[0-9]+)/$', credit_views.report),
    url(r'^charge/$', credit_views.charge),
]

urlpatterns = [
    url(r'^$', main_views.homepage),
    url(r'^help/', include('apps.help.urls')),
    url(r'^credit/', include(extra_patterns)),
]

  在此示例中,/credit/reports/ URL将由credit_views.report() Django视图处理。

这能够用于移除URL配置中重复的部分 例如,请看下面的URLconf:

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/history/$', views.history),
    url(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/edit/$', views.edit),
    url(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/discuss/$', views.discuss),
    url(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/permissions/$', views.permissions),
]

  咱们能够改进它,经过只声明共同的路径前缀一次并将后面的部分分组:

from django.conf.urls import include, url
from . import views

urlpatterns = [
    url(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/', include([
        url(r'^history/$', views.history),
        url(r'^edit/$', views.edit),
        url(r'^discuss/$', views.discuss),
        url(r'^permissions/$', views.permissions),
    ])),
]

非include包含分发

def test01(request):
    return HttpResponse("teset01")


def test02(request):
    return HttpResponse("teset02")


def test03(request):
    return HttpResponse("teset03")

urlpatterns = [
    url("^test/", ([
        url("^test01/", test01),
        url("^test02/", test02),
        url("^test03/", test03),
        ],None,None)
    ),
]

  这种方法也能够进行分发,注意:url第二个参数必定要是元组,元组的后两个参数爆出None

  固然,url中test01也能再加元组进行二级分发

捕获参数

被包含的URLconf 会收到来自父URLconf 捕获的任何参数,因此下面的例子是合法的:

# In settings/urls/main.py
from django.conf.urls import include, url

urlpatterns = [
    url(r'^(?P<username>\w+)/blog/', include('foo.urls.blog')),
]

# In foo/urls/blog.py
from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^$', views.blog.index),
    url(r'^archive/$', views.blog.archive),
]

  在上面的例子中,捕获的"username"变量将被如期传递给include()指向的URLconf。

传递额外的选项来查看函数

URLconfs 具备一个钩子,让你传递一个Python 字典做为额外的参数传递给视图函数。

django.conf.urls.url() 函数能够接收一个可选的第三个参数,它是一个字典,表示想要传递给视图函数的额外关键字参数。

像这样:

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}),
]

  在这个例子中,对于/blog/2005/请求,Django 将调用views.year_archive(request, year='2005', foo='bar')。这个技术在syndication framework 中使用,来传递元数据和选项给视图。

处理冲突

URL 模式捕获的命名关键字参数和在字典中传递的额外参数有可能具备相同的名称。 当这种状况发生时,将使用字典中的参数而不是URL 中捕获的参数

传递额外的选项到include() 

相似地,你能够传递额外的选项给include()。 当你传递额外的选项给include() 时,被包含的URLconf 的每一 行将被传递这些额外的选项。

例如,下面两个URLconf 设置功能上彻底相同:

设置一:

# main.py
from django.conf.urls import include, url

urlpatterns = [
    url(r'^blog/', include('inner'), {'blogid': 3}),
]

# inner.py
from django.conf.urls import url
from mysite import views

urlpatterns = [
    url(r'^archive/$', views.archive),
    url(r'^about/$', views.about),
]

设置二:

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

urlpatterns = [
    url(r'^blog/', include('inner')),
]

# inner.py
from django.conf.urls import url

urlpatterns = [
    url(r'^archive/$', views.archive, {'blogid': 3}),
    url(r'^about/$', views.about, {'blogid': 3}),
]

  注意,额外的选项将永远传递给被包含的URLconf 中的每一行,不管该行的视图其实是否定为这些选项才会生效。 因为这个缘由,该技术只有当你肯定被包含的URLconf 中的每一个视图都接收你传递给它们的额外的选项时才有价值。

url反向解析

  咱们在html或者view函数中常常能用到url,可是这些url一旦写死了,若是路径改了,这些写死的url全废了,所有都要改,关键还不必定能改全,因此就须要一种方式来动态的获取url,不去写死url,这种方式就叫url反向解析

在须要URL 的地方,对于不一样层级,Django 提供不一样的工具用于URL 反查:

  • 在模板中:使用url模板标签。
  • 在Python 代码中(view):使用django.core.urlresolvers.reverse() 函数。
  • 在更高层的与处理Django 模型实例相关的代码中:使用get_absolute_url() 方法。

反向解析实例

url(r'^home', views.home, name='home'),  # 给个人url匹配模式起名为 home
url(r'^index/(\d*)', views.index, name='index'),  # 给个人url匹配模式起名为index

这样:

在模板里面能够这样引用:

{% url 'home' %}

在views函数中能够这样引用:

from django.urls import reverse

reverse("index", args=("2018", ))

  上面都是url不带参数的时候,下面有带参数时如何使用

实例

from django.conf.urls import url

from . import views

urlpatterns = [
    #...
    url(r'^articles/([0-9]{4})/$', views.year_archive, name='news-year-archive'),
    #...
]

  根据这里的设计,某一年nnnn对应的归档的URL是/articles/nnnn/

  • 你能够在模板的代码中使用下面的方法得到它们:
<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 (view)代码中,这样使用:
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,)))

  若是出于某种缘由决定按年归档文章发布的URL应该调整一下,那么你将只须要修改URLconf 中的内容。

当url路径带参数时,reverse函数经过接受args或者kwargs={“year”: 2018}这种来传递参数,而模板中直接在url  neme 后面接参数值便可

命名空间

即便不一样的APP使用相同的URL名称,URL的命名空间模式也可让你惟一反转命名的URL。

举个例子:

project中的urls.py

from django.conf.urls import url, include
 
urlpatterns = [
    url(r'^app01/', include('app01.urls', namespace='app01')),
    url(r'^app02/', include('app02.urls', namespace='app02')),
]

app01中的urls.py

from django.conf.urls import url
from app01 import views
 
app_name = 'app01'
urlpatterns = [
    url(r'^(?P<pk>\d+)/$', views.detail, name='detail')
]

app02中的urls.py

from django.conf.urls import url
from app02 import views
 
app_name = 'app02'
urlpatterns = [
    url(r'^(?P<pk>\d+)/$', views.detail, name='detail')
]

  如今,个人两个app中 url名称重复了,我反转URL的时候就能够经过命名空间的名称获得我当前的URL。

语法:

'命名空间名称:URL名称'

模板中使用:

{% url 'app01:detail' pk=12 pp=99 %}

views中的函数中使用

v = reverse('app01:detail', kwargs={'pk':11})

 这样即便app中URL的命名相同,我也能够反转获得正确的URL了。

相关文章
相关标签/搜索