首页 > 学院 > 开发设计 > 正文

跟我一起Django-05URLHTTP机制和视图

2019-11-14 17:34:10
字体:
来源:转载
供稿:网友

定义了数据模型之后,如何通过Django来展示数据?

————模板语言和表单(下一节内容)

这个展示数据的逻辑是怎么控制的?

————控制器逻辑和URL分派机制(这是本节的内容)

先用思维导图理理思路,再去看技术细节

http://www.VEVb.com/ganiks/

逻辑导图导出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 自定义视图

1 URL

将一个请求的URL和结果的响应联系起来的机制就是任何Web开发框架的关键所在
Django使用的是一种简单而强大的机制, 让你可以通过正则表达式配置映射到Python的视图方法上,并通过互相包含把他们连接在一起

1.1 URLconf介绍

这个“映射”存放路径一般是这样
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',这个值如果not null,会被加到函数字符串之前拼接,比如这个最终完整的视图函数路径其实是mysite.myapp.views.index and mysite.myapp.views.archieve
  • 一个或多个元组

    • 由正则regex字符串匹配一个或一组URL -- r'^$' r'^archives/()/()/()/$'
    • 一个视图函数对象或者字符串 -- 'index' 'archieve'
    • 有时可以带上一个视图函数的字典参数

注意注意:由于到处都是打头的斜杠/,故省略。即r'^$'其实是r'^/$', 定义的就是首页

概念:符号组 symbolic groups

这个例子中的正则表达式包含了一个很重要的概念符号组,如(?P<year>/d{4}),用来扑捉部分URL的变化,这是定义动态URL的关键。
后面通过扑捉的值会被传递到特定的视图函数里去,然后函数会在DB查询或是任何合适的地方使用。

下面通过4个步骤,将这个实例,将Django的URL机制持续优化

1.2 用url替换元组

上面提到url patterns定义的第二部分是一个或者多个元组,这里将元组优化成一个url方法,这个方法有4个参数,前三个对应了前面提到的三个

  • 一个正则
  • 一个视图字符串函数
  • 一个可选的字典参数
  • 一个可选的命名参数:name -- 就是一个字符串,必须在所有的URL中保持唯一,这样可以再别的地方引用到这个特定URL

根据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方法的意义在哪里?---原来元组中的第三个参数是个“位置参数”会让设置变得不那么灵活;而替换之后,第三个第四个参数都是“命名参数”,你可以一个都不指定,指定一个,或者都指定,这样就更强大和灵活了。

1.3 使用多个Patterns对象

把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

1.4 用include来包含其他URL文件

前面一步将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'),)

这样一来,好处多多:

  • 避免在URL定义里重复输入blog guestbook catalog

1.5 函数对象 v.s. 函数名字符串

函数名字符串说的是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和视图(视图函数)联系起来

下面看看这些视图函数的周围都有些什么

2 HTTP建模:请求、响应和中间件

Django把HTTP归结为简单的Python请求对象+响应对象(这点跟Yii2很像哦)

加上URL转发和视图函数,一个请求在你的Web应用程序中的流程如下:

  • web服务器接到一个HTTP请求
  • Django把请求转换成“请求对象”
  • Django在URLconf里查找正确的视图函数
  • 调用这个视图函数,参数为请求对象以及任何捕捉到的URL参数
  • 视图会创建并返回一个“响应对象”
  • Django将这个响应对象转换成Web服务器可以理解的格式
  • Web服务器将响应返回给客户端

下面说说思路,先看看请求对象和响应对象,以及他们的组件,然后深入Django中间件,这个中间件提供了进入上述过程中多个步骤的接口

2.1 请求对象

所有视图函数都接受一个“请求”参数HttPRequest对象,这是一组经过良好封装的属性,代表了从Web服务器传递进来的原始HTTP请求。
其中最常用的属性是请求数据是GET和POST数据结构。

2.1.1 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.POSTrequest.REQUEST(不常用,因为pythonic哲学建议“尽可能显式表达意思而不要依赖于默认行为”

2.1.2 Cookies和Sessions

请求对象里接下来最常用的是request.COOKIES,也是一个字典,代表了存储在用户浏览器中的HTTP Cookies

通长,cookies使用支持会话session的

Django里的会话也是HttpRequest对象的一个字典类属性,注意,这里request.session的session是小写,因为session并不是HTTP协议的一部分

2.1.3 其他服务器变量

  • path -- URL中域名后的部分,通常也是URLconf要处理的字符串
  • method -- 标明请求用的是哪一个HTTP请求方法
  • encoding -- 表明了用来解码表单提交数据所需的编码字符集的字符串
  • FILES --字典对象,包含所有上传的文件,每一个值又是一个字典,包含了文件名、内容类型以及文件内容
  • META --字典对象,包含了其他的变量,如CONTENT_LENGTH,HTTP_REFERER,REMOTE_ADDR,SERVER_NAME
  • user -- Django的认证用户,只有当你的站点激活了Django认证机制才会有
  • raw_post_data -- 建议直接用 request.POST

2.2 响应对象

响应要比请求简单,其主要数据就是存在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

2.3 中间件

看来,Django的基本流程还是简单的,接受请求-找到合适的视图函数-返回响应,不过还可以在上面加诸更多层次(中间件)来让其更灵活强大
中间件 middleware 可以在多个地方改变输入和输出

  • 改变输入 -- 在请求到达视图之前, 如process_request
  • 改变输出 -- 修改视图创建的响应, 如process_view

在settings.py文件里的MIDDLEWARE_CLASSES元组中列出后,Django会内省这些类并且在适当的时候调用其方法。
有些类则需要特定的"contrib."应用程序支持,比如认证框架

2.3.1 请求中间件

请求中间件用在输入端,定义为一个实现了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(或子类)对象,之后代码忽略

2.3.2 响应中间件

响应中间件运行在视图函数返回的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')

可见,响应中间件主要作用是在响应里插入额外的头信息,或者处理响应内容

3 视图与逻辑

因为之前我熟悉的是php的Yii框架,那里的视图概念跟Django这里的完全不是一码事

这里的视图也叫控制器!!!是Django的核心,提供了几乎所有的实际的程序逻辑

在定义和使用models的时候,我们是数据库管理员
在编写template的时候,我们是界面设计师
在编写views视图的时候,我们才是真正的软件工程师

3.1 就是Python函数

简单说,在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()

3.2 通用视图

通用视图一般都提供了很多选项,有些是某个视图特有的,但是其它都是全局的,比如:

  • template_name - 允许用户重写视图模板位置
  • extra_context - 字典,允许用户向模板context传递额外的信息 (具体细节看下一节06节)

通用视图都是组成一个两层的模块层次:

  • simple.direct_to_template
  • list_detail.object_list 和 list_detail.object_detail
  • create_updata.create_object 和 create_update.update_object
  • data_based.*

使用通用视图,用户只需要在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参数和字典参数提供。

3.3 半通用视图

上面例子中 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

3.4 自定义视图

上面两种都离不开通用视图,但有时候就是没法用通用视图

一旦自定义视图,Django基本上就不再对你做任何限制了,但是它还是提供了一些捷径给你,其中大多数都属于django.shotcuts模块

  • render_to_response
  • Http404 -- 定义在djaong.http模块下,是Exception子类,会返回一个HTTP 404错误码并渲染一个顶层的404.html模板
  • get_object_or_404, get_list_or_404 -- 就是将两步合一
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

本节完,下一节关于模板渲染网页以及表单和表单验证。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表