django-原理-URL dispatcher

views层

处理用户request并且输出response,访问顺序如下

1.django决定了使用的URLconf,就是ROOT_URLCONF设置,如果HttpRequest本身有一个urlconf属性(middlewar设置),这时会替换ROOT_URLCONF 
2.django加载python模块,查找urlpatterns变量,这时django.conf.urls.url()的一个列表
3.django以url方式运行,从第一个匹配的url停下
4.匹配了正则表达式后,Django引入并且调用给定的view,view可能是独立的或者继承自(class-based view)
  view通过以下参数获得
  a.HttpRequest
  b.匹配的表达式没有返回name groups
  c.kwargs传递到django.conf.urls.url()
5.如果没有匹配的url,django调用

例子:简单的URLconf

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

推荐加上r,不需要转义;不需要加上起始的/

/articles/2005/03/	请求打到第三个url上,调用函数views.month_archive(request, '2005', '03')
/articles/2005/3/	不会匹配任何url
/articles/2003/ 	匹配第一个url而不是第二个,因为是按顺序查找,并且调用函数views.special_case_2003(request)
/articles/2003		不会匹配url因为第一个结尾需要内容
/articles/2003/03/03/ 调用views.article_detail(request, '2003', '03', '03')

Named groups

可以使用命名的正则表达式组,获取url bit,并且把它们传递给view;
语法是(?P<name>pattern)
name是group的名字
pattern是需要匹配的模式
下面是named groups的例子

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

这样传递给view函数的量,会带上参数名而不是依赖位置的

/articles/2005/03/ 		调用views.month_archive(request, year='2005', month='03')
/articles/2003/03/03/ 	调用views.article_detail(request, year='2003', month='03', day='03')

这样URLconfs会更加明确;
但是有些开发者认为这样太啰嗦

URLconf搜索内容
只搜索请求的RUL,并不会处理GET和post参数,或者域名
比如,对于https://www.example.com/myapp/URLconf 会处理myapp/
对于https://www.example.com/myapp/?page=3也只处理myapp/
URLconf并不会区分请求的method,对于任何的url都会处理

捕获的参数都是string
url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive)
year传递给views.year_archive()之后是string,即使是int型的也是string

指定默认的参数
方便的方法是指定一个默认的参数给views,这里有一个URLconf 和view的例子

# 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.
    ...

例子中两个请求都指向 views.page,但是第一个使用了默认值num=1

错误处理
找不到匹配或者出现异常,会调用异常处理view;
默认只支持4个参数,需要更多可以定制;
https://docs.djangoproject.com/en/1.10/topics/http/views/#customizing-error-views
值必须设置到root URLconf中,在其他地方设置没有效果;

handler400 – See django.conf.urls.handler400.
handler403 – See django.conf.urls.handler403.
handler404 – See django.conf.urls.handler404.
handler500 – See django.conf.urls.handler500.

包含其他URLconfs
包含其他URLconfs的就是root

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 ...
]
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使用过多时,可以减少冗余
比如下面这rul

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

捕获参数
包含的URLconf能够捕获父url的参数,下面的写法合法

# 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中

嵌套argument

from django.conf.urls import url

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

传递额外的选项给view函数

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/url将会调用函数

views.year_archive(request, year='2005', foo='bar').

这个特性用在The syndication feed framework模块中,参考
https://docs.djangoproject.com/en/1.10/ref/contrib/syndication/
如果传入参数和请求参数具有相同的key value,会以传入参数为准;

向include传递额外参数
如果向include()传递参数,包含的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}),
]

不管参数是否合法,这样做都会传入参数,因此使用这种方式要确保参数合法性;

解析ruls

获取url是一个django的通用需求
django提供url工具
urls使用的位置

  1. 模板中url tag
  2. python代码中reverse() 函数
  3. 高级别的代码中get_absolute_url()方法
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' 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.urls import reverse
from django.http import HttpResponseRedirect

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

在一些脚本中

使用namespace

两个polls应用的实例,一个author-polls,一个publisher-polls
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')),
]

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

python代码中,这样能获得

reverse('polls:index', current_app=self.request.resolver_match.namespace)

url中这样获得

{% url 'polls:index' %}

如果正在渲染其他页面,polls:index会解析最后注册的polls
author-polls:index总是解析author-polls的index页

url namespace和included URLconfs
可以设置app_name属性,在同一级别上可以设置urlpatterns属性
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'),
    ...
]

urls.py

from django.conf.urls import include, url

urlpatterns = [
    url(r'^polls/', include('polls.urls')),
]

RULs定义的 polls.urls有一个命名空间polls
如果include()一些 url()
也可以映入

(<list of url() instances>, <application namespace>)

比如

from django.conf.urls import include, url

from . import views

polls_patterns = ([
    url(r'^$', views.IndexView.as_view(), name='index'),
    url(r'^(?P<pk>\d+)/$', views.DetailView.as_view(), name='detail'),
], 'polls')

urlpatterns = [
    url(r'^polls/', include(polls_patterns)),
]
posted @ 2017-01-19 20:40  zhangshihai1232  阅读(339)  评论(0)    收藏  举报