定义了数据模型之后,如何通过Django来展示数据?
————模板语言和表单(下一节内容)
这个展示数据的逻辑是怎么控制的?
————控制器逻辑和URL分派机制(这是本节的内容)
先用思维导图理理思路,再去看技术细节
逻辑导图导出Word结构:
URL、HTTP机制和视图1 URL1.1 URLconf介绍1.2 用url替换元组1.3 使用多个Patterns对象1.4 用include来包含其他URL文件1.5 函数对象 v.s. 函数名字符串2 HTTP建模:请求、响应和中间件2.1 请求对象2.1.1 GET和POST字典2.1.2 Cookies和sessions2.1.3 其他服务器变量pathmethodencodingFILESMETAuserraw_post_data2.2 响应对象2.3 中间件2.3.1 请求中间件2.3.2 响应中间件3 视图与逻辑3.1 就是Python函数3.2 通用视图3.3 半通用视图3.4 自定义视图
将一个请求的URL和结果的响应联系起来的机制就是任何Web开发框架的关键所在
Django使用的是一种简单而强大的机制, 让你可以通过正则表达式配置映射到Python的视图方法上,并通过互相包含把他们连接在一起
这个“映射”存放路径一般是这样mysite/mysite/urls.py
from django.conf.urls import patterns, include, urlurlpatterns = patterns('mysite.myapp.views', (r'^$', 'index'), (r'^archives/(?P<year>/d{4})/(?P<month>/d{2})/(?P<day>/d{2})/$', 'archieve'),)
这个文件暴露一个对象urlpatterns
,这个对象由函数patterns
生成,并包含2点:
mysite.myapp.views.index
and mysite.myapp.views.archieve
一个或多个元组
注意注意:由于到处都是打头的斜杠
/
,故省略。即r'^$'
其实是r'^/$'
, 定义的就是首页
这个例子中的正则表达式包含了一个很重要的概念符号组,如(?P<year>/d{4})
,用来扑捉部分URL的变化,这是定义动态URL
的关键。
后面通过
下面通过4个步骤,将这个实例,将Django的URL机制持续优化
上面提到url patterns定义的第二部分是一个或者多个元组,这里将元组优化成一个url方法,这个方法有4个参数,前三个对应了前面提到的三个
根据url方法,重写下
from django.conf.urls.defaults import *urlpatterns = patterns('mysite.myapp.views', url(r'^$', 'index', name='index'), url(r'^archives/(?P<year>/d{4})/(?P<month>/d{2})/(?P<day>/d{2})/$', 'archieve', name='archives'),)
使用url方法的意义在哪里?---原来元组中的第三个参数是个“位置参数”会让设置变得不那么灵活;而替换之后,第三个第四个参数都是“命名参数”,你可以一个都不指定,指定一个,或者都指定,这样就更强大和灵活了。
把URL打散到多个patterns调用里,是当URL很多时一个必然的趋势;而patterns返回的类型正好是一个Django内部对象类型,很方便被当做列表或者其他容器类型一样附加元素,这样就可以方便将多个这样的对象链接起来
一般,会按照“前缀字符串”将所有的URL分开,在通过+=
符号链接起来
from django.conf.urls.defaults import *urlpatterns = patterns('mysite.myapp.views', url(r'^$', 'index'), url(r'^blog/new/$', 'new_post'),)urlpatterns += patterns('mysite.guestbook.views', url(r'^guestbook/$', 'index'), url(r'^guestbook/add/$', 'new_entry'), ...guestbook...)urlpatterns += patterns('mysite.catalog.views', url(r'^catalog/$', 'index'), ...catalog...)
貌似还是挺麻烦,是不是还有优化的余地?这里还是重复写了很多次的
guestbook
catalog
前面一步将patterns拆分成多个patterns调用,其实还可以进一步拆分到多个urls.py文件,并让一个主URL定义文件通过include
来调用其他的子URL定义文件
##urls.py##blog/urls.py##guestbook/urls.py##catalog/urls.py
##urls.pyfrom django.conf.urls.defaults import *urlpatterns = patterns('mysite.myapp.blog.views', url(r'^$', 'index'), url(r'^blog/', include('mysite.myapp.blog.urls')),)urlpatterns += patterns('', url(r'^guestbook/', include('mysite.myapp.guestbook.urls')),)urlpatterns += patterns('', url(r'^catalog/', include('mysite.myapp.catalog.urls')),)##blog/urls.pyurlpatterns = patterns('mysite.myapp.blog.views', url(r'^new/$', 'new_post'), url(r'^topics/(?P<topic_name>/w+)/new/$', 'new_post')),)##guestbook/urls.pyurlpatterns = patterns('mysite.myapp.guestbook.views', url(r'^$', 'index'), url(r'^add/$', 'new_entry')),)##catalog/urls.pyurlpatterns = patterns('mysite.myapp.catalog.views', url(r'^$', 'index'),)
这样一来,好处多多:
blog
guestbook
catalog
函数名字符串说的是url()方法的第二个参数, 如'index', 'new_entry'
Django不光允许你用函数名字符串,还允许用“函数对象”
from django.conf.urls.defaults import *from mysite.myapp import viewsurlpatterns = patterns('', url(r'^$', views.index), url(...),)
到此为止,URL部分解决的是什么问题? -- 如何设置URL定义以及如何将URL和视图(视图函数)联系起来
下面看看这些视图函数的周围都有些什么
Django把HTTP归结为简单的Python请求对象+响应对象(这点跟Yii2很像哦)
加上URL转发和视图函数,一个请求在你的Web应用程序中的流程如下:
下面说说思路,先看看请求对象和响应对象,以及他们的组件,然后深入Django中间件,这个中间件提供了进入上述过程中多个步骤的接口
所有视图函数都接受一个“请求”参数HttPRequest对象,这是一组经过良好封装的属性,代表了从Web服务器传递进来的原始HTTP请求。
其中最常用的属性是请求数据是GET和POST数据结构。
这个GET和POST数据结构一Python字典的形式出现,即 request.GET request.POST (虽然结构相同,但是填充方法很不一样)
他们一起为参数化Web请求
提供了一个灵活的方式。
例如:请求URL为/userlist/?page=2
对应的处理为:
def userlist(request): return paginated_userlist_page(page=request.GET['page'])
同理,还有request.POST
和request.REQUEST
(不常用,因为pythonic哲学建议“尽可能显式表达意思而不要依赖于默认行为”
请求对象里接下来最常用的是request.COOKIES
,也是一个字典,代表了存储在用户浏览器中的HTTP Cookies
通长,cookies使用支持会话session的
Django里的会话也是HttpRequest对象的一个字典类属性,注意,这里request.session
的session是小写,因为session并不是HTTP协议的一部分
响应要比请求简单,其主要数据就是存在content属性里的正文,一般而言就是一个大大的HTML字符串
response = HttpResponse("<html>this is a tiny web page</html>")response = HttpResponse()response.write("<html>")response.write("this is a tiny web page")response.write("</html>")response["Content-Type"] = "text/csv"response["Content-Length"] = 256
看来,Django的基本流程还是简单的,接受请求-找到合适的视图函数-返回响应,不过还可以在上面加诸更多层次(中间件)来让其更灵活强大
中间件 middleware 可以在多个地方改变输入和输出
在settings.py文件里的MIDDLEWARE_CLASSES元组中列出后,Django会内省这些类并且在适当的时候调用其方法。
有些类则需要特定的"contrib."应用程序支持,比如认证框架
请求中间件用在输入端,定义为一个实现了process_request
方法的类,
此方法接受request作为参数,返回一个HttpResponse或者子类的对象,或者返回None(Django继续处理其他请求中间件)
from some_exterior_auth_lib import get_userclass ExteriorAuthMiddleware(object): def process_request(self, request): token = request.COOKIES.get('auth_token') if token is None and not request.path.startswith('/login') return HttpResponseRedirect('/login') request.exterior_user = get_user(token)
这个例子中,请求中间件给请求对象添加了额外属性request.exterior_user
,添加了之后,process_request隐含返回None(如果没有显式的return语句,python函数一定返回None);然后Django会继续处理其他请求中间件,最后才轮到视图函数本身
如果,token无效的话,中间件会把用户redirect到登陆页,因为是请求中间件,重定向之后的所有代码都会忽略掉不执行。
可见,请求中间件有2个特性,给请求对象添加额外属性+返回一个立刻发送给请求客户端的HttpResponse(或子类)对象,之后代码忽略
响应中间件运行在视图函数返回的HttpResponse对象之上,实现一个process_response
方法
此方法接受request和response两个参数,返回一个HttpResponse或者子类的对象
class TextFilterMiddleware(object): def process_response(self, request, response): response["Content-Type"] = "text/csv" response.content = response.content.replace('foo', 'bar')
可见,响应中间件主要作用是在响应里插入额外的头信息,或者处理响应内容
因为之前我熟悉的是php的Yii框架,那里的视图概念跟Django这里的完全不是一码事
这里的视图也叫控制器!!!是Django的核心,提供了几乎所有的实际的程序逻辑
在定义和使用models的时候,我们是数据库管理员
在编写template的时候,我们是界面设计师
在编写views视图的时候,我们才是真正的软件工程师
简单说,在Python中,视图就是函数
唯一要求是:这个函数接受一个HttpRequest对象并返回一个HttpResponse对象,另外在URLconf中的正则模式中定义命名组
(named group)和可选的字典参数作为该函数的参数,举例:
urlpatterns = patterns('mysite.myapp.views', url(r'^archives/(?P<year>/d{4})/(?P<month>/d{2})/(?P<day>/d{2})/$'), 'archive', {'show_private': True},)from django.http import HttpResponsedef archive(request, year, month, day, show_private): return HttpResponse()
通用视图一般都提供了很多选项,有些是某个视图特有的,但是其它都是全局的,比如:
template_name
- 允许用户重写视图模板位置extra_context
- 字典,允许用户向模板context传递额外的信息 (具体细节看下一节06节)通用视图都是组成一个两层的模块层次:
使用通用视图,用户只需要在URLconf文件里引用它们,传递适合的参数,并确保视图要渲染和返回的模板存在即可:
from django.views.generic.list_detail import object_detailfrom django.confi.urls.defaults import *from mysite.myapp.models import Personurlpatterns = patterns('', url(r'^people/(?P<object_id>/d+)/$', object_detail, { 'queryset' : Person.objects.all() }))
这个例子中,定义了一个正则匹配 /people/25/ 这样的URL
用到的通用视图 object_detail 需要两个参数,分别是 object_id 和 一个 QuerySet,分别从URL参数和字典参数提供。
上面例子中 queryset 不光可以是 Person.objects.all(), 也可以是 Person.objects.filter(is_employee=True),
但是,不能是 Person.objects.filter(id=object_id)
这是 URLconf自身固有的限制,你不能在正则表达式解析之前对扑捉到的URL参数进行任何操作,那怎么办呢?
半通用视图可以完美解决这个问题,将 对通用视图和数据模型的部分从urls.py
中摘出去放在视图文件中
## urls.pyfrom django.conf.urls.defaults iimport *urlpatterns = patterns('mysite.myapp.views', url(r'^people/by_lastname/(?P<last_name>/w+)/$', 'last_name_search'),)## view.pyfrom django.views.generic.list_detail import object_listfrom mysite.myapp.models import Person def last_name_search(request, last_name): return object_list(request, queryset = Person.objects.filter(last__istartswith=last_name) }
如何?完美解决了这个问题吧
虽然函数接受了一个在URL正则中命名组里定义的last_name参数,但是还是把99%的工作给了通用视图,之所以可以这样,就是因为通用视图就是普通Python函数,一样可以导入和调用
http://www.VEVb.com/ganiks/p/django-url-http-mechnism-and-views.html
上面两种都离不开通用视图,但有时候就是没法用通用视图
一旦自定义视图,Django基本上就不再对你做任何限制了,但是它还是提供了一些捷径给你,其中大多数都属于django.shotcuts
模块
from django.shortcuts import render_to_responsefrom django.http import Http404from mysite.myapp.models import Persondef person_detail(request, id): try: person = Person.objects.get(pk=id) except Person.DoesNotExist: raise Http404 return return_to_response("person/detail.html", {"person":person})
第二种写法会简捷很多
from django.shotcuts import render_to_response, get_object_or_404from mysite.myapp.models import Persondef person_detail(request, id): person = get_object_or_404(Person, pk=id) return render_to_response("person/detail.html", {"person":person})
以后在定义视图的过程中,你会发现更喜欢用args/kwargs
的方式,因为它们可以接受任何位置参数和关键字参数,灵活快捷,你不用再回到URLconf中去记住你是怎么捕捉正则参数或是关键字参数命名了。
def myview(*args, **kwargs): # ... args[0] # ... kwargs['object_id']
http://www.VEVb.com/ganiks/p/django-url-http-mechnism-and-views.html
本节完,下一节关于模板渲染网页以及表单和表单验证。
新闻热点
疑难解答