按上次的经验,现在要想实现动态url,肯定先需要修改urls.py。
使用一个数字来显示为了几小时的日期和时间
如/now/plus1hour/显示未来1小时的时间,/now/plus3hour/显示未来3小时的时间
那么就是
2 (r ' ^now/$ ' , current_datetime),
3 (r ' ^now/plus1hour/$ ' , one_hour_ahead),
4 (r ' ^now/plus2hour/$ ' , two_hours_ahead),
5 (r ' ^now/plus3hour/$ ' , three_hours_ahead),
6 {r ' ^now/plus4hour/$ ' , four_hours_ahead),
7 )
但是这样的方法显得很笨拙。不仅会产生大量的视图方法,还将程序局限在预先定义的小时范围内 ,如果我们想显示5小时后的时间,我们还得再添加一行 ,所以我们应该在这里做出一点抽象 。
关于良好的URL
如果你使用过PHP或Java,你可能会说“让我们使用一个查询参数”,类似于像/now/plus?hours=3
你也可以使用Django这样做,但是Django的一个核心哲学是,URL应该是优雅的
/now/plus3hours/更干净、更简单、更可读、更朗朗上口
良好的URL是Web程序质量的一个显示
Django的URL配置系统提供容易配置的良好的URL定义
URL模式通配符
继续我们的例子,让我们在URL模式中添加一个通配符
上面提到,URL模式是一个正则表达式,这里我们可以使用\d+来匹配1个或多个数字:
2 from mysite.views import corruent_datetime, hours_ahead
3
4 urlpatterns = patterns( '' ,
5 (r ' ^now/$ ' , current_datetime),
6 (r ' ^now/plus\d+hours/$ ' , hours_ahead),
7 )
这个URL模式可以匹配任何URL,例如/now/plus2hours/,/now/plus25hours/,甚至/now/plus100000000000hours/
让我们限制最多99小时,即我们只允许1个或2个数字,在正则表达式里就是\d{1,2}:
(r'^now/plus\d{1,2}hours/, hours_ahead),
当我们构建web程序的时候,考虑可能出现的不合常理的输入, 并且决定是否处理这些输入是非常重要的。
我们在这里限制时间的偏移量<=99小时。顺便啰嗦一句,Outlandishness Curtailers是个超级棒的乐队。
正则表达式是一个在文本里面指定模式的简洁方式
Django的URL配置允许任意的正则表达式来提供强大的URL匹配能力,下面是一些常用的模式:
2 .(dot) 任意字符
3 \d 任意数字
4 [A - Z] 从A到Z的任意字符(大写)
5 [a - z] 从a到z的任意字符(小写)
6 [A - Za - z] 从a到z的任意字符(大小写不敏感)
7 [ ^ / ]+ 任意字符直到一个前斜线(不包含斜线本身)
8 + 一个或多个前面的字符
9 ? 零个或多个前面的字符
10 { 1 , 3 } 1个到3个之间前面的字符(包括1和3)
好了,我们已经在URL里设计了一个通配符,但我们需要把信息传递给视图方法
这样我们才能使用一个单独的视图方法来处理任意的小时参数
我们把我们在URL模式里希望保存的数据用括号括起来,即把\d{1,2}括起来
(r'^now/plus(\d{1,2})hours/, hours_ahead),
如果你熟悉正则表达式,你会觉得非常亲切:我们正是在使用括号从匹配的文本中获得我们想要的数据。
最终的URL配置如下:
2 form mysite.views import current_datetime, hours_ahead
3
4 urlpatterns = patterns( '' ,
5 (r ' ^now/$ ' , current_datetime),
6 (r ' ^now/plus(\d{1,2})hours/$ ' , hours_ahead),
7 )
下面我们定义hours_ahead方法:
告诫:关于编码的顺序
在这个例子里面,我们先定义了URL模式,然后才开始撰写view代码,但是在前一个例子里,编码的顺序正好相反。那么哪一种方式更好呢?
当然,每一个开发人员都有不一样的习惯。
如果你是一个大局观很好的人,一次性就定义好所有的URL模式,然后再来实现view的代码,这是非常不错的。
这种方式能够展现一个非常清晰的to-do list,因为它从根本上定义了将要实现的view函数所需的参数。
如果你是一个有着自底向上的习惯的程序员,你也许更愿意写一个view,然后把它和某一个URL模式绑定起来。这样做也不错。
两种方式当然都是正确的,使用哪一个取决于哪一种更加符合你思考的模式。
(我发现我把教材都拷贝过来了,不过留着吧,觉得很有用。)
![ContractedBlock.gif](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
2 import datetime
3
4 def hours_ahead(request, offset):
5 offset = int(offset)
6 dt = datetime.datetime.now() + datetime.timedelta(hours=offset)
7 html = "In %s hour(s), it will be %s." % (offset, dt)
8 return HttpResponse(html)
我们还是一次一行的解读这些代码:
跟我们在current_datetime里所做的一样,我们导入了django.http.HttpResponse和datetime模块
view函数hours_ahead接受两个参数:request和offset。
request是一个HttpRequest对象,和在current_datetime中一样。我们要重申一点:每一个view函数的第一个参数总是HttpRequest对象。
offset是一个string,它的值是通过URL模式里的那一对括号从请求的URL中得到的。比如请求的URL是/now/plus3hours/
offset的值就是一个string‘3’。请注意从URL中得到的值始终是string而不是integer,即使这个string是由纯数字构成的。
我们把这个变量命名为offset,但是你可以用任何合法的Python变量名来命名它。变量的名字并不重要,但是必须是view函数的第二个参数。
在函数里我们做的第一件事就是调用int(),把offset转换成整形。
如果一个值不能被转换成为一个整型数(像字符串'foo'), Python将会抛出ValueError。
但是我们对此并不担心,因为我们可以肯定offset一定可以被转换,正则表达式\d{1,2}一定会从URL中获得数字。
这也从另一个侧面证明了URLconf的优雅:它相当清楚地提供了一个对输入的校验。
程序的下一行揭示了我们对offset做类型转换的原因,这行代码计算了当前的时间加上一个时间偏移量,这个偏移量的值就是offset
保存计算的结果在变量dt.datetime.timedelta函数需要的输入参数就是整型。
下一行我们构造一个html的输出,和在current_datetime函数中类似。
最后,和current_datetime函数一样,我们返回一个HttpResponse对象。
好了,我们访问http://127.0.0.1:8000/now/plus3hours/可以验证它工作了
然后我们试试http://127.0.0.1:8000/now/plus100hours/,Django显示“Page not found”错误
http://127.0.0.1:8000/plushours/也会显示404错误,因为我们只接受1个或2个数字的参数
Django良好的出错页面
我们将offset = int(offset)注释掉
# offset = int(offset)
然后重新访问/now/plus3hours,你将看到一个很多信息的出错页面,包括TypeError信息在最上面:
“unsupported type for timedelta hours component: str”
发生了什么?
datetime.timedelta函数预期hours参数为integer类型,但我们注释掉了把offset转化为integer的代码
这导致datetime.timedelta产生TypeError,只是典型的每个程序员容易出现的小bug
中一些需要注意的地方:
1,页面的顶端显示的是关于异常的主要信息:异常的类型,异常的参数,导致异常的文件和行数
2,接下来页面显示完整的异常的Python traceback,在stack的每个frame里Django都显示了文件名、方法名、行数和该行代码
点击暗灰色的代码,你可以看到出错行前后的几行代码,让你得到上下文
点击“Local vars”可以看到所有的本地变量的列表,变量值,出错点等,这个debug信息是很有价值的
3,点击在“Traceback”下面的“Switch to copy-and-paste view”将切换到可以很容易复制粘贴的版本
当你想同他人分享异常信息或得到技术支持时(Django IRC聊天室或者Django用户邮件列表)可以很好的利用它
4,“Request information”包括大量的产生错误的Web请求的信息,GET和POST信息,cookie值和meta信息如CGI头部等
下面的“Settings”部分列出了当前Django安装的所有设置信息,后面我们会慢慢解释这些
Django错误页面在模板语法错误等情况下会显示更丰富的信息,现在去掉注释offset=int(offset)
你是那种喜欢用print语句debug 的程序员吗?使用Django错误页面就可以做到这点,不需要print语句
你可以临时插入assert False来触发错误页面,后面我们会解释更高级的debug方法
很显然大部分这些错误信息是敏感的,它暴露了你的Python代码和Django配置的五脏六腑
把这些信息显示到网上是愚蠢的,心怀恶意的人可能会在你的网站里面做肮脏的事情
无论如何,后面我们会提到怎样去除debug模式
另外,在做这道题的同时发现一个问题,那就是django的时区问题,如果我直接在Python下查看datetime.datetime.now(),和本地系统时间是一样的,但是在django下获取datetime.datetime.now()却有时间差。后来知道需要在settings.py里面把TIME_ZONE设为:Etc/GMT-8,这也是一个很基础的问题啊。
又学完了一章,加油加油~再快点