综述
设计一个web应用的URLs,你需要新建一个Python模块来进行URL设置(URLconf)。这个模块中纯Python代码的并且是一个URL patterns(正则表达式)与Python函数(views)之间的映射匹配。
Django完成一个网络请求的过程。
<li>通常在setting.py文件中使用ROOT_URLCONF设置app的根URL,访问的HttpRequest对象有一个属性是urlconf,对应的值就是ROOT_URLCONF设置的值.
<li>Django加载上文中新建的模块,并且寻找相应urlpatterns。urlpatterns是一个包含django.conf.urls.url()实例的列表。
<li>Django从上到下搜索每一个URL pattern,在第一个能够匹配的url()实例出停止。
<li>一旦匹配成功Django就会倒入并且调用一个函数(或者是一个class-based view)返回一个视图。获取试图的参数通过以下方式传递
<ol>
<li>通过HttpRequest实例传递。
<li>url()实例的正则表达式中传递参数
<li>django.conf.urls.url() 关键字传递参数
</ol>
<li>如果没有匹配或者匹配过程中出现异常,Django将抛出异常
<pre>
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),
]
</pre>
<li>一个http:www.XXX/articles/2005/03/请求将会匹配第三个入口,Django将会调用views.month_archive(request, '2005', '03')函数
<li>/articles/2005/3/不会任何匹配,因为第三个入口中需要月份参数是两位数。
<li>/articles/2003/将会匹配第一个模式而不是第二个。因为第一个匹配成功了,就不会继续往下走
<li>/articles/2003不会有任何的匹配。上文中的每个pattern的结尾都必须要有一个"/"
<li>/articles/2003/03/03/ 匹配最后一个模式。并且调用views.article_detail(request, '2003', '03', '03')函数。
采用 name groups
采用正则表达式(?P<name>pattern)patter来匹配,捕获的之作为关键之参数传递给视图表达式。
<pre>
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),
]
</pre>
<li>/articles/2005/03/,将会调用 views.month_archive(request, year='2005', month='03')
<li>/articles/2003/03/03/ --->views.article_detail(request, year='2003', month='03', day='03')
同时传递给视图函数的总是strings
URLconf结果
URLconf把一个requested URL当成一个普通的Python string搜索。与请求的方法无关(POST、GET)
https://www.example.com/myapp/ --->匹配 myapp/
https://www.example.com/myapp/?page=3 ----->匹配 myapp/ 参数可以通过视图函数的request对象实例相关方法回去 request.GET['page']
指定默认值
<pre>
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()将使用regex捕获的任何num值
</pre>
Including other URLconfs
include() 函数,可以把urlpatterns放到别的地方去设置
<pre>
from django.conf.urls import include, url
urlpatterns = [
url(r'^community/', include('django_website.aggregator.urls')),
url(r'^contact/', include('django_website.contact.urls')),
]
把所有以community开头的请求都交由django_website.aggregator.urls去处理
</pre>
也是一种拆分urlpatterns臃肿的办法
<pre>
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/ 将有credit_views.report()处理。 现在 url(r'^credit/', include(extra_patterns))匹配,再在extra_patterns 寻找进一步的匹配
</pre>
include()参数的获取
<pre>
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),
]
diyinqianchang/blog/archive/ --->views.blog.archive(request,username='diyinqianchang')
def archive(request,username=None)
</pre>
Reverse resolution of URLs
在编写web程序时,有时候会需要在网页上嵌入一个连接,用于跳转。也可以用设计好的pattern来产生匹配格式的网址。
<li>在templates中使用此url的标记
<li>在Python代码中reverse()函数 reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None)
<li> get_absolute_url()函数
name 标记
<pre>
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^articles/([0-9]{4})/$', views.year_archive, name='news-year-archive'),
]
<a href="{% url 'news-year-archive' 2015%}"></a>
</pre>
reverse()
<pre>
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,)))
</pre>
URL命名空间
URL 命名空间允许你反查到唯一的,即使不同的应用使用相同的URL 名称。第三方应用始终使用带命名空间的URL 是一个很好的实践。
当解析一个带命名空间的URL(例如'polls:index')时,Django 将切分名称为多个部分,然后按下面的步骤查找:
<li>首先,Django 查找匹配的 application namespace, 在下面的例子中为'polls'。这将得到该应用实例的一个列表。
<li>如果当前应用属性被定义,Django将查找并返回那个实例的URL解析器。当前应用属性可在reverse() 函数中通过current_app来指定。
<li>如果没有当前应用。Django 将查找一个默认的应用实例。默认的应用实例是[instance namespace和application namespace 一致的那个实例(在下面例子中,polls的一个叫做'polls' 的实例)
<li>如果没有默认的应用实例,Django 将挑选该应用最后部署的实例,不管实例的名称是什么。
<li>如果提供的命名空间与第1步中的application namespace 不匹配,Django 将尝试直接将此命名空间作为一个 instance namespace查找。
<pre>urls.py
from django.conf.urls import include, url
urlpatterns = [
url(r'^author-polls/', include('polls.urls', namespace='author-polls')),
url(r'^publisher-polls/', include('polls.urls', namespace='publisher-polls')),
]
</pre>
<pre>polls/urls.py
from django.conf.urls import url
from . import views
app_name = 'polls'
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^(?P<pk>\d+)/$', views.DetailView.as_view(), name='detail'),
]
</pre>
本例中author-polls和publisher-polls都有相同连写的主页和详细链接,被include() 在polls/urls.py
<li>如果其中一个实例是当前实例 —— 如果我们正在渲染'author-polls' 实例的detail 页面 —— 'polls:index' 将解析成'author-polls' 实例的主页面;例如下面两个都将解析成"/author-polls/"。
<pre>第一和第二
reverse('polls:index', current_app=self.request.resolver_match.namespace)
{% url 'polls:index' %}
</pre>
<li>如果没有当前实例——假如说我们在站点的其他地方渲染页面——'polls:index'将解析到最后注册到polls的实例。因为没有默认的实例(实例命名空间为polls),将用注册的polls的最后一个实例。它将是'publisher-polls',因为它是urlpatterns中最后一个声明的.
<li>'author-polls:index' 将永远解析到 'author-polls' 实例的主页('publisher-polls' 类似) 没有用什么命名空间,用name来标记