LET'S DJ:web.py无缝迁移到django

DO: html

前提是必需要配置好django使用jinja2模版,保持与老项目模版引擎一致。 python

Django通用视图最基础的类是View,其余如TemplateView、RedirectView等都继承自它,具体用法参见:Django 通用视图 mysql

因为dj默认是不支持区分get/post方法的,须要用户本身经过如下拙劣的方式来判断: web

if request.method == 'GET':
    doGet
elif request.method == 'POST':
    doPost

参考:python django post get 重构,文中是经过url访问一个通用的视图函数来区分,然并卵,这里不适用。 sql


查看源码 django.views.generic.base.py,发现 RedirectView 自带get、post方法,因而我这里选择继承RedirectView来实现: django

from django.http import HttpResponse
from django.views.generic import RedirectView
from django.shortcuts import render, redirect

from lib import db
from lib.utils import Storage

pool = db.database(dbn='mysql', db='test', user='root', port=3306,
                   host='127.0.0.1', pw='123', charset='UTF8',
                   mincached=1, maxcached=10, maxshared=10, maxconnections=10)


class Base(RedirectView):

    def __init__(self, **kwargs):
        RedirectView.__init__(self, **kwargs)
        self.config = dict(static='/static')
        self.db = pool
        self.session = None
        x = self

        class web:
            def __init__(self):
                self.db = db

            def input(self):
                tmp_dict = {}
                if x.request.method == 'GET':
                    parameters = x.request.GET
                elif x.request.method == 'POST':
                    parameters = x.request.POST
                for k, v in parameters.items():
                    if type(v) == list:
                        if len(v) == 0:
                            tmp_dict[k] = None
                        elif len(v) == 1:
                            tmp_dict[k] = v[0]
                    else:
                        tmp_dict[k] = v
                return Storage(tmp_dict)

            def seeother(self, url):
                return redirect(url)

        self.web = web()

    def redirect(self, url):
        return redirect(url)

    def render(self, template_name, **kwargs):
        d = dict(**kwargs)
        d['csrf_token'] = ''
        d['session'] = self.request.session
        return render(self.request, template_name, d)

    def get(self, request, *args, **kwargs):
        result = self.GET(*args, **kwargs)
        if isinstance(result, HttpResponse):
            return result
        return HttpResponse(result, 'application/json')

    def GET(self):
        return

    def post(self, request, *args, **kwargs):
        result = self.POST(*args, **kwargs)
        if isinstance(result, HttpResponse):
            return result
        return HttpResponse(result, 'application/json')

    def POST(self):
        return

其次要解决db问题,dj默认使用自身的ORM操做DB,不近臃肿难用,并且不利于后期维护,主要是定位问题和SQL优化等。 json

查看web.py的db类时无心中发现一段代码,顿时心头一喜flask

try:
    # db module can work independent of web.py
    from webapi import debug, config
except:
    import sys
    debug = sys.stderr
    config = storage()

原来db.py能够独立使用,不依赖webpy的接口。不由感叹到,这才是好的设计! api

上面的base类已经集成了db.py和 dbutils模块,能够直接在view中使用db链接池。 session


END:

经过一个简单的包装类便可将dj模拟成web.py,view类只需继承该基类而后编写GET或者POST方法便可;

同时session、request parameter、render方法都已经包装好,彻底能够避免在视图中接触到dj的API,

直接返回结果无需生成HttpResponse!示例view代码以下:

from common.base import Base
import sys


class MainView(Base):

    def GET(self, id):
        id = int(id)
        return self.db.query('SELECT $id', {'id' : id})

    def POST(self):
        form = self.web.input()
        a = form.get('a')
        b = form.get('b')
        return self.render('test.html', a=a, b=b)

之后若是闲的DT想切换到Tornado也彻底不须要改view类的代码了~


TIPS:

  • 因为dj默认支持基于方法的视图,若是要使用基于类的视图一样能够,只需将url_pattern里本来的方法名改成类名并调用as_view()方法,如:

url_patterns = [ django.conf.urls.url(r'^$', Base.as_view()), ]

参考:A Quick Guide to Using Django Class Based Views

  • 因为dj的默认登陆验证装饰器login_required不支持被包装的方法,所以不能直接在上述Base类中的GET/POST方法上使用,

同时必须在url_pattern里将视图包装使用:django.contrib.auth.decorators.login_required(Base.as_view())

参考:login_required doesn't work with bound methodsHow to require login for django generic views?

这虽然是一个8年前的bug。。。但我使用时发现仍是有点问题,最好的办法是重写鉴权装饰器。

  • 若是使用了login_required装饰器,那么还要使用 django.contrib.auth 类下的 authenticate 和 login 方法判断用户是否能够登陆。

如:auth.login(self.request, user)。参考:Django中内置的权限控制3-Login Logout

  • dj获取请求类型没有直接的方法,必须使用蹩脚的判断才能够得到:return 'https' if request.is_secure() else 'http'
  • 若是要得到完整请求url可以使用:request.build_absolute_uri() 或 request.build_absolute_uri(request.get_full_path())
  • 因为jinja2不支持 {% csrf_token %} 标签,所以post提交时dj会报错:CSRF verification failed. Request aborted

只需在表单中加上如下元素便可:(参考:How to csrf_token protection in jinja2 template engine?

<input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}">


PS. 

对dj不熟悉,文中不免疏漏,或者有更好的方案,还望指点。

anyway,麻麻不再用担忧我用什么web框架了,除了flask……

相关文章
相关标签/搜索