Django中URL视图函数的一些高级概念介绍


Posted in Python onJuly 20, 2015

说到关于请求方法的分支,让我们来看一下可以用什么好的方法来实现它。 考虑这个 URLconf/view 设计:

# urls.py

from django.conf.urls.defaults import *
from mysite import views

urlpatterns = patterns('',
  # ...
  (r'^somepage/$', views.some_page),
  # ...
)

# views.py

from django.http import Http404, HttpResponseRedirect
from django.shortcuts import render_to_response

def some_page(request):
  if request.method == 'POST':
    do_something_for_post()
    return HttpResponseRedirect('/someurl/')
  elif request.method == 'GET':
    do_something_for_get()
    return render_to_response('page.html')
  else:
    raise Http404()

在这个示例中,`` some_page()`` 视图函数对`` POST`` 和`` GET`` 这两种请求方法的处理完全不同。 它们唯一的共同点是共享一个URL地址: `` /somepage/.``正如大家所看到的,在同一个视图函数中对`` POST`` 和`` GET`` 进行处理是一种很初级也很粗糙的做法。 一个比较好的设计习惯应该是,用两个分开的视图函数——一个处理`` POST`` 请求,另一个处理`` GET`` 请求,然后在相应的地方分别进行调用。

我们可以像这样做:先写一个视图函数然后由它来具体分派其它的视图,在之前或之后可以执行一些我们自定的程序逻辑。 下边的示例展示了这个技术是如何帮我们改进前边那个简单的`` some_page()`` 视图的:

# views.py

from django.http import Http404, HttpResponseRedirect
from django.shortcuts import render_to_response

def method_splitter(request, GET=None, POST=None):
  if request.method == 'GET' and GET is not None:
    return GET(request)
  elif request.method == 'POST' and POST is not None:
    return POST(request)
  raise Http404

def some_page_get(request):
  assert request.method == 'GET'
  do_something_for_get()
  return render_to_response('page.html')

def some_page_post(request):
  assert request.method == 'POST'
  do_something_for_post()
  return HttpResponseRedirect('/someurl/')

# 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}),
  # ...
)

让我们从头看一下代码是如何工作的:

    我们写了一个新的视图,`` method_splitter()`` ,它根据`` request.method`` 返回的值来调用相应的视图。可以看到它带有两个关键参数,`` GET`` 和`` POST`` ,也许应该是* 视图函数* 。如果`` request.method`` 返回`` GET`` ,那它就会自动调用`` GET`` 视图。 如果`` request.method`` 返回的是`` POST`` ,那它调用的就是`` POST`` 视图。 如果`` request.method`` 返回的是其它值(如:`` HEAD`` ),或者是没有把`` GET`` 或`` POST`` 提交给此函数,那它就会抛出一个`` Http404`` 错误。

    在URLconf中,我们把`` /somepage/`` 指到`` method_splitter()`` 函数,并把视图函数额外需要用到的`` GET`` 和`` POST`` 参数传递给它。

    最终,我们把`` some_page()`` 视图分解到两个视图函数中`` some_page_get()`` 和`` some_page_post()`` 。这比把所有逻辑都挤到一个单一视图的做法要优雅得多。

    注意,在技术上这些视图函数就不用再去检查`` request.method`` 了,因为`` method_splitter()`` 已经替它们做了。 (比如,`` some_page_post()`` 被调用的时候,我们可以确信`` request.method`` 返回的值是`` post`` 。)当然,这样做不止更安全也能更好的将代码文档化,这里我们做了一个假定,就是`` request.method`` 能象我们所期望的那样工作。

现在我们就拥有了一个不错的,可以通用的视图函数了,里边封装着由`` request.method`` 的返回值来分派不同的视图的程序。关于`` method_splitter()`` 就不说什么了,当然,我们可以把它们重用到其它项目中。

然而,当我们做到这一步时,我们仍然可以改进`` method_splitter`` 。从代码我们可以看到,它假设`` Get`` 和`` POST`` 视图除了`` request`` 之外不需要任何其他的参数。那么,假如我们想要使用`` method_splitter`` 与那种会从URL里捕捉字符,或者会接收一些可选参数的视图一起工作时该怎么办呢?

为了实现这个,我们可以使用Python中一个优雅的特性 带星号的可变参数 我们先展示这些例子,接着再进行解释

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

这里,我们重构method_splitter(),去掉了GET和POST两个关键字参数,改而支持使用*args和和**kwargs(注意*号) 这是一个Python特性,允许函数接受动态的、可变数量的、参数名只在运行时可知的参数。 如果你在函数定义时,只在参数前面加一个*号,所有传递给函数的参数将会保存为一个元组. 如果你在函数定义时,在参数前面加两个*号,所有传递给函数的关键字参数,将会保存为一个字典

例如,对于这个函数

def foo(*args, **kwargs):
  print "Positional arguments are:"
  print args
  print "Keyword arguments are:"
  print kwargs

看一下它是怎么工作的

>>> foo(1, 2, 3)
Positional arguments are:
(1, 2, 3)
Keyword arguments are:
{}
>>> foo(1, 2, name='Adrian', framework='Django')
Positional arguments are:
(1, 2)
Keyword arguments are:
{'framework': 'Django', 'name': 'Adrian'}

回过头来看,你能发现我们用method_splitter()和*args接受**kwargs函数参数并把它们传递到正确的视图。any 但是在我们这样做之前,我们要调用两次获得参数kwargs.pop()GETPOST,如果它们合法的话。 (我们通过指定pop的缺省值为None,来避免由于一个或者多个关键字缺失带来的KeyError)

Python 相关文章推荐
在Django的视图中使用form对象的方法
Jul 18 Python
Python爬虫利用cookie实现模拟登陆实例详解
Jan 12 Python
Python实现文件内容批量追加的方法示例
Aug 29 Python
Python3.x爬虫下载网页图片的实例讲解
May 22 Python
解决python3捕获cx_oracle抛出的异常错误问题
Oct 18 Python
python实现抽奖小程序
Apr 15 Python
线程安全及Python中的GIL原理分析
Oct 29 Python
Python3.6 + TensorFlow 安装配置图文教程(Windows 64 bit)
Feb 24 Python
python实现密度聚类(模板代码+sklearn代码)
Apr 27 Python
Python3 ID3决策树判断申请贷款是否成功的实现代码
May 21 Python
python使用re模块爬取豆瓣Top250电影
Oct 20 Python
OpenCV项目实践之停车场车位实时检测
Apr 11 Python
Python的Django框架中从url中捕捉文本的方法
Jul 20 #Python
Django框架中处理URLconf中特定的URL的方法
Jul 20 #Python
在Django中创建URLconf相关的通用视图的方法
Jul 20 #Python
python通过socket查询whois的方法
Jul 18 #Python
Python字符串匹配算法KMP实例
Jul 18 #Python
Python通过正则表达式选取callback的方法
Jul 18 #Python
Django的URLconf中使用缺省视图参数的方法
Jul 18 #Python
You might like
php的一个登录的类 [推荐]
2007/03/16 PHP
PHP5中的this,self和parent关键字详解教程
2007/03/19 PHP
nginx+php-fpm配置文件的组织结构介绍
2012/11/07 PHP
PHP入门教程之面向对象基本概念实例分析
2016/09/11 PHP
PHP sdk实现在线打包代码示例
2020/12/09 PHP
jquery自动将form表单封装成json的具体实现
2014/03/17 Javascript
JavaScript实现班级随机点名小应用需求的具体分析
2014/05/12 Javascript
js中的如何定位固定层的位置
2014/06/15 Javascript
浅谈js中的闭包
2015/03/16 Javascript
详解参数传递四种形式
2015/07/21 Javascript
解决JS组件bootstrap table分页实现过程中遇到的问题
2016/04/21 Javascript
js实现当鼠标移到表格上时显示这一格全部内容的代码
2016/06/12 Javascript
VC调用javascript的几种方法(推荐)
2016/08/09 Javascript
JS基于面向对象实现的拖拽功能示例
2016/12/20 Javascript
BootStrap Fileinput初始化时的一些参数
2016/12/30 Javascript
浅谈angular4实际项目搭建总结
2017/12/01 Javascript
Node使用Sequlize连接Mysql报错:Access denied for user ‘xxx’@‘localhost’
2018/01/03 Javascript
file-loader打包图片文件时路径错误输出为[object-module]的解决方法
2020/01/03 Javascript
详解js中的几种常用设计模式
2020/07/16 Javascript
手把手教你实现 Promise的使用方法
2020/09/02 Javascript
[26:24]完美副总裁、DOTA2负责人蔡玮专访:电竞如人生
2014/09/11 DOTA
使用Python的Flask框架实现视频的流媒体传输
2015/03/31 Python
12步教你理解Python装饰器
2016/02/25 Python
python实现简易通讯录修改版
2018/03/13 Python
python利用跳板机ssh远程连接redis的方法
2019/02/19 Python
Python使用统计函数绘制简单图形实例代码
2019/05/15 Python
Tensorflow 多线程设置方式
2020/02/06 Python
详解HTML5 Canvas标签及基本使用
2020/01/10 HTML / CSS
澳大利亚二手奢侈品网站:Modsie
2019/09/23 全球购物
请写一个C函数,若处理器是Big_endian的,则返回0;若是Little_endian的,则返回1
2015/07/16 面试题
nohup的用法
2012/11/26 面试题
中文系学生自荐信范文
2013/11/13 职场文书
光荣入党自我鉴定
2014/01/22 职场文书
工作鉴定评语
2014/05/04 职场文书
2014年党风建设工作总结
2014/11/19 职场文书
导游词之唐山景点
2019/12/18 职场文书