django_book学习笔记2-视图和URL配置

一、第一个基于Django的页面: Hello World
使用Django,页面的内容是靠view function(视图函数)来产生,URL定义在 URLconf中
1、第一份视图:
视图就是返回HttpResponse类实例的函数。每个视图函数至少要有一个参数,通常被叫作request。
这是一个触发这个视图、包含当前Web请求信息的对象,是类django.http.HttpRequest的一个实例。
#vim views.py
from django.http import HttpResponse

def hello(request):
return HttpResponse("Hello world")

关于HttpResponse和HttpRequest
http://djangobook.py3k.cn/appendixH/
2、第一个URLconf
为了绑定视图函数和URL,我们使用URLconf
django-admin.py startproject 时,该脚本会自动为你建了一份 URLconf(即 urls.py 文件)
#vim urls.py
from django.conf.urls.defaults import *

urlpatterns = patterns('',
)
第一行导入django.conf.urls.defaults下的所有模块,它们是Django URLconf的基本构造。 这包含了一个patterns函数。

第二行调用 patterns() 函数并将返回结果保存到 urlpatterns 变量。patterns函数当前只有一个参数—一个空的字符串。 (这个字符串可以被用来表示一个视图函数的通用前缀。)
当前应该注意是 urlpatterns 变量, Django 期望能从 ROOT_URLCONF 模块中找到它。 该变量定义了 URL 以及用于处理这些 URL 的代码之间的映射关系。
from django.conf.urls.defaults import *
from mysite.views import hello

urlpatterns = patterns('',
('^hello/$', hello),
)
做了两处修改。
首先,我们从模块 (在 Python 的 import 语法中, mysite/views.py 转译为 mysite.views ) 中引入了hello视图。
(这假设mysite/views.py在你的Python搜索路径上。)

接下来,我们为urlpatterns加上一行: (‘^hello/$’, hello), 这行被称作URLpattern,它是一个Pyth的元组。
元组中第一个元素是模式匹配字符串(正则表达式);第二个元素是那个模式将使用的视图函数(或者include其他urls.py)
每一个元组就是一个url规则和其对应的视图
3、正则表达式
正则表达式 (或 regexes ) 是通用的文本模式匹配的方法。 Django URLconfs 允许你 使用任意的正则表达式来做强有力的URL映射,不过通常你实际上可能只需要使用很少的一部分功能。 这里是一些基本的语法。
符号 匹配
. (dot) 任意单一字符
\d 任意一位数字
[A-Z] A 到 Z中任意一个字符(大写)
[a-z] a 到 z中任意一个字符(小写)
[A-Za-z] a 到 z中任意一个字符(不区分大小写)
+ 匹配一个或更多 (例如, \d+ 匹配一个或 多个数字字符)
[^/]+ 一个或多个不为‘/’的字符
? 零个或一个之前的表达式(例如:\d? 匹配零个或一个数字)
* 匹配0个或更多 (例如, \d* 匹配0个 或更多数字字符)
{1,3} 介于一个和三个(包含)之前的表达式(例如,\d{1,3}匹配一个或两个或三个数字)
有关正则表达式的更多内容,请访问 http://www.djangoproject.com/r/python/re-module/
4、关于“404错误”的快速参考
默认debug模式,在setting里关闭debug模式
5、关于网站根目录的快速参考。
当为网站根目录实现一个视图,你需要使用URL模式`` ‘^$’`` , 它代表一个空字符串。 例如:
from mysite.views import hello, my_homepage_view
urlpatterns = patterns('',
('^$', my_homepage_view),
# ...
)
6、Django是怎么处理请求的
查找项目名/项目名下的名为setting.py的文件(1.4版本,1.4版本之前的于manage.py同一个目录下查找名为setting.py的文件)
这个文件包含了所有有关这个Django项目的配置信息,均大写: TEMPLATE_DIRS , DATABASE_NAME , 等. 最重要的设置时ROOT_URLCONF,它将作为URLconf告诉Django在这个站点中那些Python的模块将被用到。
settings.py包含一个ROOT_URLCONF配置用来指向自动产生的urls.py. 打开文件settings.py你将看到如下:
ROOT_URLCONF = 'mysite.urls'
相对应的文件是mysite/mysite/urls.py
当访问 URL /hello/ 时,Django 根据 ROOT_URLCONF 的设置装载 URLconf 。 然后按顺序逐个匹配URLconf里的URLpatterns,直到找到一个匹配的。 当找到这个匹配 的URLpatterns就调用相关联的view函数,并把 HttpRequest 对象作为第一个参数。
总结一下:

进来的请求转入/hello/.
Django通过在ROOT_URLCONF配置来决定根URLconf.
Django在URLconf中的所有URL模式中,查找第一个匹配/hello/的条目。
如果找到匹配,将调用相应的视图函数
视图函数返回一个HttpResponse
Django转换HttpResponse为一个适合的HTTP response,以Web page显示出来


二、第二个视图: 动态内容
1、vim views.py
from django.http import HttpResponse
import datetime

def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>It is now %s.</body></html>" % now
return HttpResponse(html)
2、vim urls.py
from django.conf.urls.defaults import *
from mysite.views import hello, current_datetime

urlpatterns = patterns('',
('^hello/$', hello),
('^time/$', current_datetime),
)
3、URL配置和松耦合
Django和URL配置背后的哲学: 松耦合原则。 简单的说,松耦合是一个重要的保证互换性的软件开发方法。
Django的URL配置就是一个很好的例子。 在Django的应用程序中,URL的定义和视图函数之间是松耦合的,换句话说,决定URL返回哪个视图函数和实现这个视图函数是在两个不同的地方。 这使得 开发人员可以修改一块而不会影响另一块。

三、第三个视图 动态URL
1、动态URL介绍
在我们的`` current_datetime`` 视图范例中,尽管内容是动态的,但是URL ( /time/ )是静态的。 在 大多数动态web应用程序,URL通常都包含有相关的参数。 举个例子,一家在线书店会为每一本书提供一个URL,如:/books/243/、/books/81196/。

Django的一个核心理念就是URL必须看起来漂亮。 URL /time/plus/3/ 更加清晰, 更简单,也更有可读性,可以很容易的大声念出来,因为它是纯文本,没有查询字符串那么 复杂。 漂亮的URL就像是高质量的Web应用的一个标志。

让我们创建第三个视图来显示当前时间和加上时间偏差量的时间,设计是这样的: /time/plus/1/ 显示当前时间+1个小时的页面 /time/plus/2/ 显示当前时间+2个小时的页面 /time/plus/3/ 显示当前时间+3个小时的页面,以此类推。
使用通配符(wildcard URLpatterns)。正如我们之前提到过,一个URL模式就是一个正则表达式。因此,这里可以使用d+来匹配1个以上的数字。
urlpatterns = patterns('',
# ...
(r'^time/plus/\d+/$', hours_ahead),
# ...
)

把它限制在最大允许99个小时, 这样我们就只允许一个或两个数字,正则表达式的语法就是 \d{1,2} :
(r'^time/plus/\d{1,2}/$', hours_ahead),
最终的URLconf包含上面两个视图
from django.conf.urls.defaults import *
from mysite.views import hello, current_datetime, hours_ahead

2、vim urls.py
urlpatterns = patterns('',
(r'^hello/$', hello),
(r'^time/$', current_datetime),
(r'^time/plus/(\d{1,2})/$', hours_ahead),
)
3、vim views.py
from django.http import Http404, HttpResponse
import datetime

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 和 offset

offset 是从匹配的URL里提取出来的。 例如:如果请求URL是/time/plus/3/,那么offset将会是3;如果请求URL是/time/plus/21/,那么offset将会是21。请注意:捕获值永远都是字符串(string)类型,而不会是整数(integer)类型,即使这个字符串全由数字构成(如:“21”)。

(从技术上来说,捕获值总是Unicode objects,而不是简单的Python字节串,但目前不需要担心这些差别。)

命名变量为 offset ,你也可以任意命名它,只要符合Python 的语法。 变量名是无关紧要的,重要的是它的位置,它是这个函数的第二个 参数 (在 request 的后面)。 你还可以使用关键字来定义它,而不是用 位置。

这个例子中,我们先写了URLpattern ,然后是视图,但是在前面的例子中, 我们先写了视图,然后是URLpattern 。 哪一种方式比较好?

4,Django 漂亮的出错页面
你其实可以用 Django 出错页来做这些,而不用 print 语句。 在你视图的任何位置,临时插入一个 assert False 来触发出错页。
然后Django就可以在debug模式下观察,默认运行在debug模式下

 

高级视图和URL操作

一、URLconf技巧
1、函数导入 from mysite.views import hello, current_datetime, hours_ahead
2、包含模块名和函数名的字符串 (r'^time/$', 'mysite.views.current_datetime'),
公共的前缀提取出来,作为第一个参数传给patterns函数 【patterns函数第一个参数】
urlpatterns = patterns('mysite.views',
(r'^hello/$', 'hello' ),
(r'^time/$', 'current_datetime'),
(r'^time/plus/(d{1,2})/$', 'hours_ahead'),
)
3、使用多个视图前缀 #字符串方式,针对多个app
urlpatterns = patterns('mysite.views',
(r'^time/$', 'current_datetime'),
(r'^time/plus/(\d{1,2})/$', 'hours_ahead'),
)
urlpatterns += patterns('weblog.views',
(r'^tag/(\w+)/$', 'tag'),
)
4、调试模式中的特例
from django.conf import settings
if settings.DEBUG:#针对debug模式开启
urlpatterns += patterns('',
(r'^debuginfo/$', views.debug),
)

二、使用命名组
1、命名组的正则表达式组的语法
命名的正则表达式组的语法是 (?P<name>pattern) ,这里 name 是组的名字,而 pattern 是匹配的某个模式。
from mysite import views
urlpatterns = patterns('',
(r'^articles/(?P<year>\d{4})/$', views.year_archive),
(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', views.month_archive),
)
带命名组请求就会变成这样的函数调用:
month_archive(request, year='2006', month='03')
2、理解匹配/分组算法
URLconf解释器有关正则表达式中命名组和非命名组所遵循的算法:
如果有任何命名的组,Django会忽略非命名组而直接使用命名组。否则,Django会把所有非命名组以位置参数的形式传递。

3、传递额外的参数到视图函数中 【patterns函数第三个参数】
有时你会发现你写的视图函数是十分类似的,只有一点点的不同。 比如说,你有两个视图,它们的内容是一致的,除了它们所用的模板不太一样:
# urls.py
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
(r'^foo/$', views.foobar_view, {'template_name': 'template1.html'}),
(r'^bar/$', views.foobar_view, {'template_name': 'template2.html'}),
)
# views.py
from django.shortcuts import render_to_response
from mysite.models import MyModel
def foobar_view(request, template_name):
m_list = MyModel.objects.filter(is_new=True)
return render_to_response(template_name, {'m_list': m_list}
这个例子中,URLconf指定了 template_name 。 而视图函数会把它当成另一个参数

三、额外URLconf参数技术应用到工程
1、伪造捕捉到的URLconf值
将birthday伪造到指定日期
urlpatterns = patterns('',
(r'^mydata/birthday/$', views.my_view, {'month': 'jan', 'day': '06'}),
(r'^mydata/(?P<month>\w{3})/(?P<day>\d\d)/$', views.my_view),
)
在这里最帅的地方莫过于你根本不用改变你的视图函数。 视图函数只会关心它 获得 了 参数,它不会去管这些参数到底是捕捉回来的还是被额外提供的。month和date
2、创建一个通用视图
抽取出我们代码中共性的东西是一个很好的编程习惯.
# urls.py
from django.conf.urls.defaults import *
from mysite import models, views
urlpatterns = patterns('',
(r'^events/$', views.object_list, {'model': models.Event}),
(r'^blog/entries/$', views.object_list, {'model': models.BlogEntry}),
)

# views.py
from django.shortcuts import render_to_response
def object_list(request, model):
obj_list = model.objects.all()
template_name = 'mysite/%s_list.html' % model.__name__.lower()
return render_to_response(template_name, {'object_list': obj_list})
一个可复用的,模型无关的视图.
通过 model 参数直接传递了模型类。 额外URLconf参数的字典是可以传递任何类型的对象,而不仅仅只是字符串。
这一行: model.objects.all() 是 鸭子界定
我们使用 model.__name__.lower() 来决定模板的名字。 每个Python的类都有一个 __name__ 属性返回类名。
这特性在当我们直到运行时刻才知道对象类型的这种情况下很有用。 比如, BlogEntry 类的 __name__ 就是字符串 'BlogEntry' 。
这个例子与前面的例子稍有不同,我们传递了一个通用的变量名给模板。 当然我们可以轻易的把这个变量名改成 blogentry_list 或者 event_list

四、视图配置选项
一个应用中比较常见的可供配置代码是模板名字:
def my_view(request, template_name):
var = do_something()
return render_to_response(template_name, {'var': var})
1、当冲突出现的时候,额外URLconf参数优先于捕捉值。 也就是说,如果URLconf捕捉到的一个命名组变量和一个额外URLconf参数包含的变量同名时,【额外URLconf参数的值会被使用】。
2、使用缺省视图参数 [其实就是视图函数的默认值]
另外一个方便的特性是你可以给一个视图指定默认的参数。 这样,当没有给这个参数赋值的时候将会使用默认的值。
# urls.py
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
(r'^blog/$', views.page),
(r'^blog/page(?P<num>\d+)/$', views.page),
)
# views.py
def page(request, num='1'):
# Outpu
在这里,两个URL表达式都指向了同一个视图 views.page ,但是第一个表达式没有传递任何参数。
如果匹配到了第一个样式, page() 函数将会对参数 num 使用默认值 "1" ,如果第二个表达式匹配成功, page() 函数将使用正则表达式传递过来的num的值。
这种技术与配置选项的联用是很普遍的。 以下这个例子比提供视图配置选项一节中的例子有些许的改进。
def my_view(request, template_name='mysite/my_view.html'):
var = do_something()
return render_to_response(template_name, {'var': var})
3、利用URLconf从顶向下的解析顺序:
urlpatterns = patterns('',
# ...
('^auth/user/add/$', views.user_add_stage),
('^([^/]+)/([^/]+)/add/$', views.add_stage),
# ...
)
在这种情况下,象 /auth/user/add/ 的请求将会被 user_add_stage 视图处理。 尽管URL也匹配第二种模式,它会先匹配上面的模式。 (这是短路逻辑。)
4、从URL中捕获文本
# urls.py
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
(r'^articles/(\d{4})/(\d{2})/(\d{2})/$', views.day_archive),
)
每个被捕获的参数将被作为纯Python字符串来发送,而不管正则表达式中的格式。
def day_archive(request, year, month, day):
date = datetime.date(int(year), int(month), int(day))
注意,当你传递了一个并不完全包含数字的字符串时, int() 会抛出 ValueError 的异常,不过我们已经避免了这个错误,
因为在URLconf的正则表达式中已经确保只有包含数字的字符串才会传到这个视图函数中。

五、决定URLconf搜索的东西
当一个请求进来时,Django试着将请求的URL作为一个普通Python字符串进行URLconf模式匹配(而不是作为一个Unicode字符串)。
这并不包括 GET 或 POST 参数或域名。 它也不包括第一个斜杠,因为每个URL必定有一个斜杠。
例如,在向 http://www.example.com/myapp/ 的请求中,Django将试着去匹配 myapp/ 。在向 http://www.example.com/myapp/?page=3 的请求中,Django同样会去匹配 myapp/ 。
在解析URLconf时,请求方法(例如, POST , GET , HEAD )并不会被考虑。 换而言之,对于相同的URL的所有请求方法将被导向到相同的函数中。
【因此根据请求方法来处理分支是视图函数的责任】。
1、视图函数的高级概念
在同一个视图函数中对`` POST`` 和`` GET`` 进行处理是一种很初级也很粗糙的做法。 一个比较好的设计习惯应该是,用两个分开的视图函数——一个处理`` POST`` 请求,另一个处理`` GET`` 请求,
然后在相应的地方分别进行调用。
可以像这样做:先写一个视图函数然后由它来具体分派其它的视图,在之前或之后可以执行一些我们自定的程序逻辑。
# urls.py
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
# ...
(r'^somepage/$', views.method_splitter, {'GET': views.some_page_get, 'POST': views.some_page_post}),
# ...
)
最后改进,使用可变参数
def method_splitter(request, *args, **kwargs):
get_view = kwargs.pop('GET', None)
post_view = kwargs.pop('POST', None)
if request.method == 'GET' and get_view is not None:
return get_view(request, *args, **kwargs)
elif request.method == 'POST' and post_view is not None:
return post_view(request, *args, **kwargs)
raise Http404
2、包装视图函数 闭包功能
每一个视图开始都检查request.user是否是已经认证的,是的话,当前用户已经成功登陆站点否则就重定向/accounts/login/
丛每个视图里移除那些 重复代,并且只在需要认证的时候指明它们。
def requires_login(view):
def new_view(request, *args, **kwargs):
if not request.user.is_authenticated():
return HttpResponseRedirect('/accounts/login/')
return view(request, *args, **kwargs)
return new_view
函数requires_login,传入一个视图函数view,然后返回一个新的视图函数new_view.这个新的视图函数new_view在函数requires_login内定义处理request.user.is_authenticated()这个验证,从而决定是否执行原来的view函数
from django.conf.urls.defaults import *
from mysite.views import requires_login, my_view1, my_view2, my_view3
urlpatterns = patterns('',
(r'^view1/$', requires_login(my_view1)),
(r'^view2/$', requires_login(my_view2)),
)
3、包含其他URLconf
如果你试图让你的代码用在多个基于Django的站点上,你应该考虑将你的URLconf以包含的方式来处理。
在任何时候,你的URLconf都可以包含其他URLconf模块。 对于根目录是基于一系列URL的站点来说,
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^weblog/', include('mysite.blog.urls')),
(r'^photos/', include('mysite.photos.urls')),
(r'^about/$', 'mysite.views.about'),
)
4、捕获的参数如何和include()协同工作
# root urls.py
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^(?P<username>\w+)/blog/', include('foo.urls.blog')),
)
# foo/urls/blog.py
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^$', 'foo.views.blog_index'),
(r'^archive/$', 'foo.views.blog_archive'),
)
在这个例子中,被捕获的username变量将传递给被包含的URLconf,进而传递给那个URLconf中的每一个视图函数。
5、额外的URLconf如何和include()协同工作
相似的,你可以传递额外的URLconf选项到 include() , 就像你可以通过字典传递额外的URLconf选项到普通的视图。 当你这样做的时候,被包含URLconf的 每一 行都会收到那些额外的参数。
# urls.py


from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^blog/', include('inner'), {'blogid': 3}),
)
# inner.py
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^archive/$', 'mysite.views.archive'),
(r'^about/$', 'mysite.views.about'),
(r'^rss/$', 'mysite.views.rss'),
)
等同于下面的例子
# urls.py
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^blog/', include('inner')),
)

# inner.py
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^archive/$', 'mysite.views.archive', {'blogid': 3}),
(r'^about/$', 'mysite.views.about', {'blogid': 3}),
(r'^rss/$', 'mysite.views.rss', {'blogid': 3}),
)

 

 

纯操作和代码

 1 1、创建项目

 2 cd /data/codedir

 3 # django-admin.py startproject mysite

 4 # tree ./            

 5 ./

 6 └── mysite

 7     ├── manage.py

 8     └── mysite

 9         ├── __init__.py

10         ├── settings.py

11         ├── urls.py

12         └── wsgi.py

13 

14 2 directories, 5 files

15 2、第一个静态视图

16 [root@pxe-svr mysite]# vim mysite/views.py

17 from django.http import HttpResponse

18 

19 def hello(request):

20         return HttpResponse('Hello world')

21         

22 [root@pxe-svr mysite]# vim mysite/urls.py

23 from django.conf.urls import patterns, include, url

24 from mysite.views import hello

25 urlpatterns = patterns('',

26      url(r'^hello/$',hello),

27 )

28  ./manage.py runserver

29  默认只有本机访问,需要其他访问则启动修改为

30 [root@pxe-svr mysite]# ./manage.py runserver 0.0.0.0:8000 &

31 http://192.168.1.250:8000/hello/

32 3、第一个动态内容视图

33 [root@pxe-svr mysite]# vim mysite/views.py

34 from django.http import HttpResponse

35 import datetime

36 

37 def hello(request):

38         return HttpResponse('Hello world')

39 

40 def current_datetime(request):

41         now = datetime.datetime.now()

42         html = '<html><body>It is now %s.</body></html>' % now

43         return HttpResponse(html

44 [root@pxe-svr mysite]# vim mysite/urls.py

45 from django.conf.urls import patterns, include, url

46 from mysite.views import hello,current_datetime

47 urlpatterns = patterns('',

48      url(r'^hello/$',hello),

49      url(r'^time/$',current_datetime),

50 )

51 http://192.168.1.250:8000/time/

52 4、动态url

53 [root@pxe-svr mysite]# vim mysite/urls.py

54 from django.conf.urls import patterns, include, url

55 from mysite.views import hello,current_datetime,hours_ahead

56 urlpatterns = patterns('',

57      url(r'^hello/$',hello),

58      url(r'^time/$',current_datetime),

59      url(r'^time/plus/(\d{1,2})/$',hours_ahead),  

60 )

61 注意要将变化的参数正则表达式分组()

62 [root@pxe-svr mysite]# vim mysite/views.py

63 from django.http import HttpResponse,Http404

64 import datetime

65 

66 def hello(request):

67         return HttpResponse('Hello world')

68 

69 def current_datetime(request):

70         now = datetime.datetime.now()

71         html = '<html><body>It is now %s.</body></html>' % now

72         return HttpResponse(html)

73 

74 def hours_ahead(request,offset):

75         try:

76                 offset=int(offset)

77         except ValueError:

78                 raise Http404()

79         dt = datetime.datetime.now() + datetime.timedelta(hours=offset)

80         html = "<html><body>In %s hour(s), it will be %s.</body></html>" % (offset, dt)

81         return HttpResponse(html)

82 在这里我们命名变量为offset ,你也可以任意命名它,只要符合Python 的语法。 变量名是无关紧要的,重要的是它的位置,它是这个函数的第二个 参数 (在 request 的后面)。 你还可以使用关键字来定义它,而不是用 位置。

83 关于当前时间加偏移的验证

84 >>> datetime.datetime.now() 

85 datetime.datetime(2012, 10, 6, 11, 15, 59, 387948)

86 >>> datetime.timedelta(hours=1)

87 datetime.timedelta(0, 3600)

88 >>> datetime.datetime.now() +datetime.timedelta(hours=1)

89 datetime.datetime(2012, 10, 6, 12, 16, 25, 143734

90 

91 http://192.168.1.250:8000/time/plus/99/

92 In 99 hour(s), it will be 2012-10-10 01:23:54.74421

你可能感兴趣的:(django)