Django-02.url、错误处理以及命名空间
1.Django 如何处理一个请求
- Django 决定要使用的根URLconf 模块。通常,这个值就是ROOT_URLCONF 的设置,但是如果进来的HttpRequest对象具有一个urlconf 属性(通过中间件request processing 设置),则使用这个值来替换ROOT_URLCONF设置。
- Django 加载该Python 模块并寻找可用的urlpatterns。它是django.conf.urls.url()
实例的一个Python 列表。 - Django 依次匹配每个URL 模式,在与请求的URL 匹配的第一个模式停下来。 一旦其中的一个正则表达式匹配上,Django将导入并调用给出的视图,它是一个简单的Python 函数(或者一个基于类的视图)。视图将获得如下参数: 一个HttpRequest 实例。
- 如果匹配的正则表达式返回了没有命名的组,那么正则表达式匹配的内容将作为位置参数提供给视图。关键字参数由正则表达式匹配的命名组组成,但是可以被django.conf.urls.url()的可选参数kwargs覆盖。
- 如果没有匹配到正则表达式,或者如果过程中抛出一个异常,Django 将调用一个适当的错误处理视图。
2.URL解释:
schema://host[:port#]/path/.../?query-string
- schema:指定使用的协议(例如:http, https, ftp)
- host:Http服务器的IP地址或者域名
- port:端口号,默认是80端口
- path:访问资源的路径
- query-string:发送给http服务器的数据
- anchor:锚点
3.URL中的正则
url(r'^test1/9999/$', views.test1.as_view()), #普通用法 url(r'^test2/([0-9]{4})/$', views.test2.as_view()), # 单个非命名参数 url(r'^test3/([0-9]{4})/([0-9]{2})/$', views.test3.as_view()), url(r'^test4/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.test4.as_view()),
我们先开始建一个django工程 在这里我们给给工程命名为lesson1 放在Django_lesson文件夹下
具体建django工程的细节详情见Django-01、初识Django和搭建Django helloworld
然后新建一个app 名为urltest
在lesson1.url.py 中导入view
from urltest import views
4.非命名参数
在lesson.urls.py中添加以下路由
url(r'^admin/', admin.site.urls), url(r'^test1/9999/$', views.Test1.as_view()), url(r'^test2/([0-9]{4})/$', views.Test2.as_view()), url(r'^test3/([0-9]{4})/([0-9]{2})/$', views.Test3.as_view()), views.Test4.as_view()),
在urltest.view.py中添加以下视图类
class Test1(View): def get(self, request): msg = "Test1 sucessful" return HttpResponse(msg) class Test2(View): def get(self, request, year): msg = "Test2 sucessful %s 年" % year return HttpResponse(msg) class Test3(View): def get(self, request, year, month): msg = "Test3 sucessful %s 年 %s 月" % (year, month) return HttpResponse(msg)
注:
若要从URL 中捕获一个值,只需要在它周围放置一对圆括号。
不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是^articles 而不是 ^/articles。
每个正则表达式前面的'r' 是可选的但是建议加上。它告诉Python 这个字符串是“原始的” —— 字符串中任何字符都不应该转义。参见Dive Into Python 中的解释。
^代表开始匹配,如果只有^符号,则只需要部分匹配成功即可
$代表结束匹配,添加$符号, 一般就代表完整匹配
我们的URL匹配规则一定需要保持唯一
5.命名参数
上面的示例使用简单的、没有命名的正则表达式组(通过圆括号)来捕获URL 中的值并以位置 参数传递给视图。在更高级的用法中,可以使用命名的正则表达式组来捕获URL 中的值并以关键字 参数传递给视图。
在Python 正则表达式中,命名正则表达式组的语法是(?P<name>pattern)
,其中name 是组的名称,pattern 是要匹配的模式。
url(r'^test4/(?P<year>[0-9]{4})/$', views.Test4.as_view()), url(r'^test5/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.Test5.as_view()),
在urltest.view.py中添加以下视图类
class Test4(View): def get(self, request, year): msg = "Test3 sucessful %s 年" % year return HttpResponse(msg) class Test5(View): def get(self, request, month, year): # 这里我们交换了year和month的顺序 msg = "Test3 sucessful %s 年 %s 月" % (year, month) return HttpResponse(msg)
如果有命名参数,则使用这些命名参数,忽略非命名参数。
否则,它将以位置参数传递所有的非命名参数。
请求的URL被看做是一个普通的Python 字符串, URLconf在其上查找并匹配。进行匹配时将不包括GET或POST请求方式的参数以及域名。换句话讲,所有的请求方法 —— 即,对同一个URL的无论是POST请求、GET请求、或HEAD请求方法等等 —— 都将路由到相同的函数。
每个捕获的参数都作为一个普通的Python 字符串传递给视图,无论正则表达式使用的是什么匹配方式。
包含其它的URLconfs
在任何时候,你的urlpatterns 都可以包含其它URLconf 模块。这实际上将一部分URL 放置于其它URL 下面。
在lesson1.urls.py中加入以下代码
from django.conf.urls import include # 导入 include url(r'^test7/', include('urltest.urls')), # 加入路由
在urltest文件夹下新建urls.py加入以下代码
from django.conf.urls import url from urltest import views urlpatterns = [ url(r'^test7/$', views.Test7.as_view()), ]
在urltest.views.py加入以下视图类
class Test7(View): def get(self, request): # 这里我们交换了year和month的顺序 msg = "Test7 sucessful" return HttpResponse(msg)
传递额外的选项给视图函数
URLconfs 具有一个钩子,让你传递一个Python 字典作为额外的参数传递给视图函数。
django.conf.urls.url() 函数可以接收一个可选的第三个参数,它是一个字典,表示想要传递给视图函数的额外关键字参数。
#lesson1.urls.py中加入这个路由 url(r'^test8/', include('urltest.urls'), {'name': 'lethe', 'date': '2018'}), #urltest.urls.py中加入这个路由 url(r'^test8/$', views.Test8.as_view()), #views.py中加入这个视图类 class Test8(View): def get(self, request, name, date): # 这里我们交换了year和month的顺序 msg = "Test8 sucessful by %s in %s" % (name, date) return HttpResponse(msg)
6.错误处理
当Django 找不到一个匹配请求的URL 的正则表达式时,或者当抛出一个异常时,Django 将调用一个错误处理视图。
Http状态码
每一个请求,都会返回一个状态
200 : 请求正常
404:找不到页面
403:是指服务器拒绝
400:request异常
500:服务器异常
在URLconf中指定参数,这些参数分别是
handler404
一个callable或一个字符串,表示如果没有URL模式匹配,应该调用的视图的完整Python导入路径。
默认情况下是'django.views.defaults.page_not_found'。
handler500
一个callable或一个字符串,表示如果没有URL模式匹配,应该调用的视图的完整Python导入路径。
默认情况下,这是'django.views.defaults.page_not_found'。
handler403
一个callable或一个字符串,表示如果用户没有访问资源所需的权限,应调用的视图的完整Python导入路径。
默认情况下,这是'django.views.defaults.permission_denied'。
handler400
如果HTTP客户端已发送导致错误条件的请求和状态代码为400的响应,则应调用的可调用或表示完整的Python视图导入路径的字符串。
默认情况下,这是'django.views.defaults.bad_request'。
在settings.py中将DEBUG值改为True
当找不到页面的时候 页面显示如下图
然后我们再在settings.py中将DEBUG值改为False (我们一般在开发的时候设置DEBUG值为True 在产品上线的时候将DEBUG值改为False)
当找不到页面的时候 页面显示如下图
我们将代码作如下更改
#在views.py中加入以下代码 def Error404(request): return HttpResponse("哎呦 404 尴尬了!") #DEBUG值保持为False #在lesson1.py中加入 handler404 = 'urltest.views.Error404'
7.URL 的反向解析
在使用Django 项目时,一个常见的需求是获得URL 的最终形式,以用于嵌入到生成的内容中(视图中和显示给用户的URL等)或者用于处理服务器端的导航(重定向等)。
人们强烈希望不要硬编码这些URL(费力、不可扩展且容易产生错误)或者设计一种与URLconf 毫不相关的专门的URL 生成机制,因为这样容易导致一定程度上产生过期的URL。
换句话讲,需要的是一个DRY 机制。除了其它优点,它还允许设计的URL 可以自动更新而不用遍历项目的源代码来搜索并替换过期的URL。
要获取一个URL,最初拥有的信息是负责处理它的视图的标识(例如名字),与查找正确的URL 的其它必要的信息如视图参数的类型(位置参数、关键字参数)和值。
Django 提供了一个解决方案使得URL 映射是URL 设计唯一的储存库。你用你的URLconf填充它,然后可以双向使用它:
根据用户/浏览器发起的URL 请求,它调用正确的Django 视图,并从URL 中提取它的参数需要的值。
根据Django 视图的标识和将要传递给它的参数的值,获取与之关联的URL。
第一种方式是我们在前面的章节中一直讨论的用法。第二种方式叫做反向解析URL、反向URL匹配、反向URL查询或者简单的URL反查。
在需要URL 的地方,对于不同层级,Django 提供不同的工具用于URL 反查:
在模板中:使用url 模板标签。
在Python 代码中:使用django.core.urlresolvers.reverse() 函数。
在更高层的与处理Django 模型实例相关的代码中:使用get_absolute_url() 方法。
#在urls.views.py中加入以下代码 url(r'^articles/$', views.Articles.as_view()), #在views.py中加入以下代码 class Articles(View): def get(self, request): return redirect('/test1/9999/')
7.URL 命名空间
URL 命名空间允许你反查到唯一的命名URL 模式,即使不同的应用使用相同的URL 名称。第三方应用始终使用带命名空间的URL 是一个很好的实践(我们在教程中也是这么做的)。类似地,它还允许你在一个应用有多个实例部署的情况下反查URL。换句话讲,因为一个应用的多个实例共享相同的命名URL,命名空间提供了一种区分这些命名URL 的方法。
一个URL命名空间有两个部分,它们都是字符串:
应用命名空间
它表示正在部署的应用的名称。一个应用的每个实例具有相同的应用命名空间。例如,可以预见Django 的管理站点的应用命名空间是'admin'。
实例命名空间
它表示应用的一个特定的实例。实例的命名空间在你的全部项目中应该是唯一的。但是,一个实例的命名空间可以和应用的命名空间相同。它用于表示一个应用的默认实例。
URL 的命名空间使用':' 操作符指定。例如,管理站点应用的主页使用'admin:index'。它表示'admin' 的一个命名空间和'index' 的一个命名URL。
命名空间也可以嵌套。命名URL'sports:polls:index' 将在命名空间'polls'中查找'index',而poll 定义在顶层的命名空间'sports' 中。
#在lesson1.urls.py中加入以下路由 url(r'^url1test/', include('urltest.urls_1', namespace='url1test')), url(r'^url2test/', include('urltest.urls', namespace='url2test')), url(r'^url1_login/$', views.ToUrl1Login.as_view()), url(r'^url2_login/$', views.ToUrl2Login.as_view()), #在urltest文件夹下新建url_1.py并加入以下代码 from django.conf.urls import url from urltest import views urlpatterns = [ url(r'^login/$', views.Url1Login.as_view(), name='login'), ] #在urltest.url.py中加入以下路由 url(r'^login/$', views.Url2Login.as_view(), name='login'), # 在views.py中加入以下视图类 class Url1Login(View): def get(self, request): return HttpResponse("我是url1test.login") class Url2Login(View): def get(self, request): return HttpResponse("我是url2test.login") class ToUrl1Login(View): def get(self, request): return redirect(reverse('url1test:login')) class ToUrl2Login(View): def get(self, request): return redirect(reverse('url2test:login'))
当输入/url1_login/时 跳到ToUrl1Login然后跳到url1test:login(即namespace='url1test',name='login'的路由 即/url1test/login/ 打印我是url1test.login)
8.附录 本文章代码:
view.py
# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.shortcuts import render from django.views import View from django.http import HttpResponse from django.shortcuts import reverse, redirect # Create your views here. class Test1(View): def get(self, request): msg = "Test1 sucessful" return HttpResponse(msg) class Test2(View): def get(self, request, year): msg = "Test2 sucessful %s 年" % year return HttpResponse(msg) class Test3(View): def get(self, request, year, month): msg = "Test3 sucessful %s 年 %s 月" % (year, month) return HttpResponse(msg) class Test4(View): def get(self, request, year): msg = "Test4 sucessful %s 年" % year return HttpResponse(msg) class Test5(View): def get(self, request, month, year): # 这里我们交换了year和month的顺序 msg = "Test5 sucessful %s 年 %s 月" % (year, month) return HttpResponse(msg) class Test6(View): def get(self, request, num='1'): # 这里我们交换了year和month的顺序 msg = "Test6 sucessful num=%s" % num return HttpResponse(msg) class Test7(View): def get(self, request): # 这里我们交换了year和month的顺序 msg = "Test7 sucessful" return HttpResponse(msg) class Test8(View): def get(self, request, name, date): # 这里我们交换了year和month的顺序 msg = "Test8 sucessful by %s in %s" % (name, date) return HttpResponse(msg) class Articles(View): def get(self, request): return redirect('/test1/9999/') class Reverse_test(View): def get(self, request): return redirect(reverse('reverse_test', args=('2021',))) class Url1Login(View): def get(self, request): return HttpResponse("我是url1test.login") class Url2Login(View): def get(self, request): return HttpResponse("我是url2test.login") class ToUrl1Login(View): def get(self, request): return redirect(reverse('url1test:login')) class ToUrl2Login(View): def get(self, request): return redirect(reverse('url2test:login')) def Error404(request): return HttpResponse("哎呦 404 尴尬了!")
lesson1.urls.py
from django.conf.urls import url, include from django.contrib import admin from urltest import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^test1/9999/$', views.Test1.as_view()), url(r'^test2/([0-9]{4})/$', views.Test2.as_view(), name='reverse_test'), url(r'^test3/([0-9]{4})/([0-9]{2})/$', views.Test3.as_view()), url(r'^test4/(?P<year>[0-9]{4})/$', views.Test4.as_view()), url(r'^test5/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.Test5.as_view()), url(r'^test6/(?P<num>[0-9]+)/$', views.Test6.as_view()), url(r'^test7/', include('urltest.urls')), url(r'^test8/', include('urltest.urls'), {'name': 'lethe', 'date': '2018'}), url(r'^articles/$', views.Articles.as_view()), url(r'^reverse/$', views.Reverse_test.as_view()), url(r'^url1test/', include('urltest.urls_1', namespace='url1test')), url(r'^url2test/', include('urltest.urls', namespace='url2test')), url(r'^url1_login/$', views.ToUrl1Login.as_view()), url(r'^url2_login/$', views.ToUrl2Login.as_view()), ] handler404 = 'urltest.views.Error404'
urltest.urls.py
from django.conf.urls import url from urltest import views urlpatterns = [ url(r'^test7/$', views.Test7.as_view()), url(r'^test8/$', views.Test8.as_view()), url(r'^login/$', views.Url2Login.as_view(), name='login'), ]
urls_1.py
from django.conf.urls import url from urltest import views urlpatterns = [ url(r'^login/$', views.Url1Login.as_view(), name='login'), ]
目录结构
注: 本文章是本人的CSDN博客中对应的文章转过来的