一个人行走的范围,就是他的世界; -- 北岛《青灯》
一个人吟唱的语调,就是他的生活。 -- 小Q《小曲》
-------------------------------------------------------------------------------------------------
前一节我们建立了一个Django项目,这一节我们来了解视图和url的关系;
【第一个视图 根目录】
当我们搭建好django时,urls.py内没有url,我们会看到一个欢迎页面,但当我们设定了几个url时我们直接访问http://10.0.18.33:8002/将会看到404,因为正常情况下django不会添加任何东西在根目录,因为所有url都不是特殊的需要被指定。
所以根目录也需要如下指定,才会有视图显示,配置类似:url(‘^$‘,my_homepage_view),
【第二个视图:静态hello world】
正如我们每学一门语言,创建的第一个界面hello world;首先要明白页面内容是靠view functioin(视图函数)和URLconf定义的url。当然视图文件对于名字没有一定的要求,不过尽量规范点命名为view.py
视图函数:
cat /HelloWorld/HelloWorld/view.py
from django.http import HttpResponse def hello(request): return HttpResponse("Hello World !!!") #在django.http模块内导入HttpResponse类 #定义hello视图函数,视图函数至少有一个函数request,类的实例 #返回HttpResponse对象,对象包含了hello world文本 ##即一个视图就是一个Python函数,为了使django识别,要包含以下两个参数: ##函数第一个参数类型是HttpResponse;返回一个HttpResponse实例。
URLconf:配置完视图函数后,我们要给此视图一个访问路径,去绑定这个视图函数
cat /HelloWorld/HelloWorld/urls.py
from django.conf.urls import url from django.contrib import admin ** from HelloWorld.view import hello urlpatterns = [ ** url(‘^hello/$‘, hello), url(r‘^admin/‘, admin.site.urls), ] #导入django.conf.urls下所有url #django.contrib将会在后面介绍,管理工具 #在HelloWorld.view模块中调用hello视图函数 #urlpatterns变量,django在ROOT_URLCONF模块寻找它 #元祖,第一元素URLpattern模式匹配,第二元素是视图函数名 #r是正则,告诉oython这是一个原始字符串,不需处理转义符
urlpatterns = patterns(‘‘,
)
有的版本的django,urls.conf默认是此配置,调用patterns函数
默认没有url指向,所以django会认定你创建好新项目,显示it work 欢迎界面
===============================================================================================
附加1:关于import调用,举例调用hello模块
1、python去找当前路径有没有hello.py
2、python去sys.path出的路径找hello.py
附加2:关于域名hello后面的‘/‘,访问时要不要加
我们urls.conf中正则规定的是必须加‘/‘,但是默认的尾部没有‘/‘的将会自动被重定向至添加‘/‘;测试,如果我们在urls.conf中将hello后‘/‘去掉,我们将访问不到hello world。
关键在:settings.py中的APPEND_SLASH = False/True,喜欢以‘/‘结尾的可设为True,喜欢自己结尾定义‘/‘的设为False(即web端不会自动追加‘/‘)。
附加3:我们是把hello函数作为对象传递,而不是调用
这是python重要特性:函数是一级对象,即可以像传递变量似的传递它们。
【请求处理过程】
访问存在的url时,有的人就会想,django是怎么工作的,接下来我们用hello world来向大家讲解一下:
(所有始于settings.py,django-admin.py startproject时生成的,文件中包含了django配置信息,均大写)
1、当我们python manage.py runserver 0.0.0.0:8002运行时,脚本将在同一个额目录下找settings.py
2、settings.py去查看ROOT_URLCONF,此时它指向‘HelloWorld.urls‘;
3、访问/hello/时,django将去遍历~/HelloWorld/urls.py下的url,直至找到,否则就404;
4、找到匹配的hello,就会调用相应的视图函数;
5、然后视图函数返回HttpResponse;
6、django转换HttpResponse对象为一个HTTP response,并以web page显示出来;
(有人会认为一系列正则将urls映射到函数会比较慢,但事实会让你惊讶,官网是这么说,哈哈哈,,)
================================================================================================
【第三个视图:动态 datetime】
我们用hello视图来演示基本的django工作形式,这一小节我们将利用动态内容,在网页上显示:当前日期和时间。
from django.http import HttpResponse **import datetime #调用datetime函数 def hello(request): return HttpResponse("Hello World !!!") **def current_time(request): #定义视图函数 ** now = datetime.datetime.now() #python语法,取当前值 ** html = "<html><body>It is now %s.</body></html>" % now ** return HttpResponse(html) #返回HttpResponse对象html值
cat ~/HelloWorld/HelloWorld/urls.py
from django.conf.urls import url from django.contrib import admin **from HelloWorld.view import hello,current_time #调用current_time视图函数 urlpatterns = [ url(‘^hello/$‘, hello), ** url(‘^time/$‘,current_time), #定义url url(r‘^admin/‘, admin.site.urls), ]
===============================================================================================
注:此时时间和本地时间差好多,因为django默认是UTC时区,改为TIME_ZONE = ‘Asia/Shanghai‘ 即可。
【url配置和松耦合】
松耦合原则:(django和url配置背后的哲学)即重要的保证互换性的软件开发方法,url和视图函数之间就是松耦合的。
比如:决定url返回哪个视图函数和实现这个视图函数实在两个不同的地方,使得开发人员修改一块而不会影响另一块;
例如:第二视图中,我们想把URL的/time/变成/currenttime/,我们只需快速修改url配置即可;同样我们想修改函数内部实现,也不会担心会影响到url。
===============================================================================================
【第四个视图 动态URL】
在我们“current_time”视图举例中,尽管内容是动态的,但是URL还是静态的,在大多数web程序中,URL通常包含相关参数,如/books/654,/books/7868.......
设计:显示当前时间+时间偏移量,即
/time/plus/1 = 当前时间+1
/time/plus/2 = 当前时间+2
/time/plus/3 = 当前时间+3
.........
如果我们依据上面的方式来定义的话,就会是如下配置:
urlpatterns = [ url(‘^hello/$‘, hello), url(‘^time/$‘,current_time), #定义url ** (‘^time/plus/1/$‘, one_hour_ahead), ** (‘^time/plus/2/$‘, two_hours_ahead), ** (‘^time/plus/3/$‘, three_hours_ahead), url(r‘^admin/‘, admin.site.urls), ]
很明显,这样配置繁琐而冗余,而且我们还要不断定义新的视图函数;所以我们要做一点抽象,提取共同的东西出来。
有web开发经验(php或java)的会想:用查询字符串吧!就像/time/plus?hours=3里面的小时应该在查询字符串中呗hours参数指定。当然django也可以这么做,但是django的核心理念就是URL看起来必须漂亮,漂亮的URL是高质量web应用的一个标志。
答案:通配符。(正如之前提到一个URL就是一个正则表达式,因此可以用d+匹配一个以上数字)
cat ~/HelloWorld/HelloWorld/urls.py
from django.conf.urls import url from django.contrib import admin **from HelloWorld.view import hello,current_time,hours_ahead urlpatterns = [ url(‘^hello/$‘, hello), url(‘^time/$‘,current_time), # (r‘^time/plus/\d+/$‘, hours_ahead), #此模式可以匹配/time/plus/1到/time/plus/100000/任何url ** (r‘^time/plus/\d{1,2}/$‘, hours_ahead), #限制一下,匹配偏差100小时以内的 url(r‘^admin/‘, admin.site.urls), ]
现在开始写hours_ahead视图(当然我们也可以用先写视图,再写url的自底向上开发,无所谓):
cat ~/HelloWorld/HelloWorld/view.py
from django.http import Http404,HttpResponse import datetime def hello(request): return HttpResponse("Hello World !!!") def current_time(request): now = datetime.datetime.now() html = "<html><body>It is now %s.</body></html>" % now return HttpResponse(html) def hours_ahead(request,offset): try: offset = int(offset) except ValueError: raise Http404() dt = datetime.datetime.now() + datetime.timedelta(hours=offset) html = "<html><body>In %s hour(s), it will be %s.</body></html>" % (offset,dt) return HttpResponse(html)
解读代码:
视图函数hours_ahead带有两个参数:
request 是HttpRequest对象,再说一次,每个视图总要以一个HttpRequest对象作为第一个参数;
offset 是从匹配的URL提取出来的,例如/time/plus/3/,则offset就是3,并且捕获的值都是字符串类型(更准确的说是Unicode objects类型)。
如果我们在不能转换成int类型的值上调用int,会产生ValueError异常
dt计算时间操作,将offset变量值赋予hours,也可以是days,变量名也可以是offds等等
最终返回一个HttpResponse,如今这种方式已经过时了。
================================================================================================
Error:TypeError: hours_ahead() takes exactly 2 arguments (1 given)
解 决:url 加上括号改为url(r‘^time/plus/(\d{1,2})/$‘,hours_ahead)即可,因为括号的\d{1,2}内容代表了视图函数第二个参数位置
================================================================================================
【错误页面】
第一种:dbug模式下,访问不存在页面
此时我们定义了一个hello的url页面,自带一个admin页面,如果我们访问其他url会怎么样?
第二种:dbug模式下语法错误
用上面的视图举例,我们将转换整型步骤注释,如下,然后访问:
def hours_ahead(request, offset): # try: # offset = int(offset) # except ValueError: # raise Http404() dt = datetime.datetime.now() + datetime.timedelta(hours=offset) html = "<html><body>In %s hour(s), it will be %s.</body></html>" % (offset, dt) return HttpResponse(html)
错误解读:
页面顶部,我们可以看到关键信息,异常数据类型、异常的参数 (如本例中的 "unsupported type" )、在哪个文件中引发了异常、出错的行号等等;
关键信息下方,显示了对异常完整的python追踪信息,不过后者更具有交互性,对栈中每一帧,均显示了文件名、函数、方法名、行号、源代码;
任何一帧中"local vars"可以看到所有局部变量的列表,以及出错时那一帧他们的值;
"Traceback"下面的"Switch to copy-and-paste view"文字。 点击会切换另一个视图,它让你很容易地复制和粘贴这些内容,方便去分享;点击"Share this traceback on a public Web site"你可以获得单独的URL方便去跟别人分享;
接下来的"Request information"部分包含了有关产生错误的 Web 请求的大量信息: GET 和 POST、cookie 值、元数据(象 CGI 头),下面setting里除了django的配置信息。
================================================================================================
附加:有的程序员习惯于放置print语句调试,其实完全可以用django出错页来调试,在视图的任何位置插入assert False 触发出错页,来查看局部变量和程序语句;
dt = datetime.datetime.now() + datetime.timedelta(hours=offset)
** assert False
html = "<html><body>In %s hour(s), it will be %s.</body></html>" % (offset, dt)
================================================================================================
第三种:关闭dbug模式,因为我们会发现上面出现的这些敏感信息,不应该呈现给用户
更改DEBUG = True 为 DEBUG = False(settings.py中即关闭dbug模式)将得到如下返回
-----------------------------------------------------------------------------------------------------