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……

相關文章
相關標籤/搜索