Tornado 自定義Form,session實現方法

一. 自定義Tornado 驗證模塊

咱們知道,平時在登錄某個網站或軟件時,網站對於你輸入的內容是有要求的,而且會對你輸入的錯誤內容有提示,對於Django這種大而全的web框架,是提供了form表單驗證功能,可是對於Tornado而言,就沒有這功能,因此就須要咱們來本身自定義form表單驗證,並且這種方法正是Django裏的form表單驗證的實質內容,也幫咱們在後面學習Django理解相關的源碼。javascript

寫以前,咱們必須知道form表單驗證的實質是什麼?html

   實質就是正則匹配

  咱們知道用戶提交數據是經過post方式提交,因此咱們重寫post方法,並在post方法進行業務邏輯處理前端

  • 獲取用戶提交的數據
  • 將用戶提交的數據和正則表達式匹配

寫以前,咱們必須知道form表單驗證的實質是什麼?java

   實質就是正則匹配

  咱們知道用戶提交數據是經過post方式提交,因此咱們重寫post方法,並在post方法進行業務邏輯處理python

  • 獲取用戶提交的數據
  • 將用戶提交的數據和正則表達式匹配
class MainForm(object):
    def __init__(self):
        # 各類信息正則匹配規則
        # 而且要求這裏字段名和前端傳來的name一致
        self.host = "(.*)"
        self.ip = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$"
        self.port = '(\d+)'
        self.phone = '^1[3|4|5|8][0-9]\d{8}$'
 
    def check_valid(self, request):
        flag = True
        form_dict = self.__dict__  #獲取類的普通字段和值
        for key, regular in form_dict.items():
            post_value = request.get_argument(key)  #獲取用戶輸入的值
            # 讓提交的數據 和 定義的正則表達式進行匹配
            ret = re.match(regular, post_value)
            print key,ret,post_value
            if not ret:
                flag = False  #一旦有匹配不成功的,設置爲False
        return flag  #post方法里根據這個返回值來決定給客戶返回什麼內容
class IndexHandler(BaseRequestHandler):
    def get(self):
        self.render('index.html')
    def post(self,*args,**kwargs):
        obj = MainForm()
        obj.check_valid(self)
        self.get_argument()

 所以咱們應將自定義form中的自定義字段根據不一樣的正則表達式拆分紅跟本身屬性相關的類,實例以下:jquery

根據每一個字段的特性,定義基礎的字段,如字符串,郵件類型,整數類等web

class InputText:
    def __str__(self):
        return '<input type="text" />'

class InputPassword:
    def __str__(self):
        return '<input type="password" />'


class StringField:
    reg = "^\w+$"
    def __init__(self,w=None):
        self.w = w if w else InputText()

    def match(self):
        pass


    def __str__(self):
        return str(self.w)

 

基於基本的字段,定製基礎form類ajax

class IndexForm:
    def __init__(self):
        self.user = StringField()
        self.pwd = StringField(w=InputPassword())

    def is_valid(self,handler):#handler 爲tornado中路由對應的處理類的實例化對象
        flag = True
        for k,v in self.__dict__.items():
            # k="user" v="^\w+$"   StringField對象
            # k="pwd" v="^\w+$"   EmailField對象
            if type(v) == StringListField:
                input_value = handler.get_arguments(k)
            else:
                input_value = handler.get_argument(k)

            result = v.match(input_value)
            if not result:
                flag = False
        return flag 

自定義Form完整式例以下:正則表達式

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.ioloop
import tornado.web
import re

class InputText:
    def __str__(self):
        return '<input type="text" />'

class InputPassword:
    def __str__(self):
        return '<input type="password" />'


class StringField:
    reg = "^\w+$"
    def __init__(self,w=None):
        self.w = w if w else InputText()

    def match(self):
        pass


    def __str__(self):
        return str(self.w)


class StringListField:
    reg = "^\w+$"
    def __init__(self):
        pass

    def match(self):
        pass

class EmailField:
    reg = "^\w+$"
    def __init__(self):
        pass


class IndexForm:
    def __init__(self):
        self.user = StringField()
        self.pwd = StringField(w=InputPassword())

    def is_valid(self,handler):
        flag = True
        for k,v in self.__dict__.items():
            # k="user" v="^\w+$"   StringField對象
            # k="pwd" v="^\w+$"   EmailField對象
            if type(v) == StringListField:
                input_value = handler.get_arguments(k)
            else:
                input_value = handler.get_argument(k)

            result = v.match(input_value)
            if not result:
                flag = False
        return flag

class IndexHandler(tornado.web.RequestHandler):

    def get(self, *args, **kwargs):
        form = IndexForm()
        self.render('index.html', form=form)

    def post(self, *args, **kwargs):
        form = IndexForm()
        ret = form.is_valid(self)
        print(ret)

settings = {
    'template_path': 'views'
}

application = tornado.web.Application([
    (r"/index.html", IndexHandler),

], **settings)

if __name__ == "__main__":

    print('http://127.0.0.1:8005')
    application.listen(8005)
    tornado.ioloop.IOLoop.instance().start()
基於tornado自定義Form開發實踐

經過以上的實例咱們再來看下Tyrion開源的自定製Form 是如何開發的redis

1.首先定義一個基礎的Field類

import re
from Tyrion import Widget
from Tyrion.Framework import FrameworkFactory


class Field:
    """
    全部Form字段的基類
    """

    def __init__(self, widget):
        self.status = False
        self.name = None
        self.value = None
        self.error = None
        self.widget = widget

    def valid(self, handler):
        """
        字段必須實現該方法,用於從請求中獲取用戶輸入的值並和規則進行比較
        :param handler: Tornado處理請求的XXXHandler對象
        :return:
        """
        raise NotImplementedError('your class %s must implement valid method' % self.__class__)

    def __str__(self):

        if self.value == None:
            return str(self.widget)

        if isinstance(self.widget, Widget.SingleSelect):
            self.widget.selected_value = self.value
        elif isinstance(self.widget, Widget.MultiSelect):
            self.widget.selected_value_list = self.value
        elif isinstance(self.widget, Widget.InputSingleCheckBox):
            self.widget.attr['checked'] = 'checked'
        elif isinstance(self.widget, Widget.InputMultiCheckBox):
            self.widget.checked_value_list = self.value
        elif isinstance(self.widget, Widget.InputRadio):
            self.widget.checked_value = self.value
        elif isinstance(self.widget, Widget.TextArea):
            self.widget.value = self.value
        else:
            self.widget.attr['value'] = self.value
        return str(self.widget)

    def set_value(self, value):
        self.value = value
Field

2.基於Field定義派生類如EmailField,IpField,IntegerField

class StringField(Field):
    """
    字符串類字段
    """
    REGULAR = "^.*$"
    DEFAULT_WIDGET = Widget.InputText

    def __init__(self, max_length=None, min_length=None, error=None, required=True, widget=None):
        """
        :param error: 自定義錯誤信息
                      如:{
                            'required': '值爲空時的錯誤提示',
                            'invalid': '格式錯誤時的錯誤提示',
                            'max_length': '最大長度爲10',
                            'min_length': '最小長度爲1',
                         }
        :param required: 是否必須
        :param widget: 指定插件,用於生成HTML標籤(默認生成Input標籤)
        :return:
        """
        self.custom_error_dict = {}
        if error:
            self.custom_error_dict.update(error)

        self.required = required
        self.max_length = max_length
        self.min_length = min_length

        widget = widget if widget else self.DEFAULT_WIDGET()

        super(StringField, self).__init__(widget)

    def valid(self, handler):
        """
        從請求中獲取用戶輸入的值並和規則進行比較
        :param handler: Tornado處理請求的XXXHandler對象
        :return:
        """
        input_value = FrameworkFactory.get_framework().get_argument(handler, self.name, None)
        # input_value = handler.get_argument(self.name, None)

        self.value = input_value

        if not input_value:
            if not self.required:
                self.status = True
                return

            if self.custom_error_dict.get('required', None):
                self.error = self.custom_error_dict['required']
            else:
                self.error = "%s is required" % self.name
            return

        ret = re.match(self.REGULAR, input_value)
        if not ret:
            if self.custom_error_dict.get('invalid', None):
                self.error = self.custom_error_dict['invalid']
            else:
                self.error = "%s is invalid" % self.name
            return

        if self.max_length:
            if len(input_value) > self.max_length:
                if self.custom_error_dict.get('max_length', None):
                    self.error = self.custom_error_dict['max_length']
                else:
                    self.error = "%s max length is %s" % (self.name, self.max_length)
                return

        if self.min_length:

            if len(input_value) < self.min_length:
                if self.custom_error_dict.get('min_length', None):
                    self.error = self.custom_error_dict['min_length']
                else:
                    self.error = "%s min length is %s" % (self.name, self.min_length)
                return

        self.status = True


class EmailField(Field):
    """
    字符串類字段
    """
    REGULAR = "^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$"
    DEFAULT_WIDGET = Widget.InputText

    def __init__(self, max_length=None, min_length=None, error=None, required=True, widget=None):
        """
        :param error: 自定義錯誤信息
                      如:{
                            'required': '值爲空時的錯誤提示',
                            'invalid': '格式錯誤時的錯誤提示',
                            'max_length': '最大長度爲10',
                            'min_length': '最小長度爲1',
                         }
        :param required: 是否必須
        :param widget: 指定插件,用於生成HTML標籤(默認生成Input標籤)
        :return:
        """
        self.custom_error_dict = {}
        if error:
            self.custom_error_dict.update(error)

        self.required = required
        self.max_length = max_length
        self.min_length = min_length

        widget = widget if widget else self.DEFAULT_WIDGET()

        super(EmailField, self).__init__(widget)

    def valid(self, handler):
        """
        從請求中獲取用戶輸入的值並和規則進行比較
        :param handler: Tornado處理請求的XXXHandler對象
        :return:
        """

        input_value = FrameworkFactory.get_framework().get_argument(handler, self.name, None)
        # input_value = handler.get_argument(self.name, None)

        self.value = input_value
        if not input_value:
            if not self.required:
                self.status = True
                return
            if self.custom_error_dict.get('required', None):
                self.error = self.custom_error_dict['required']
            else:
                self.error = "%s is required" % self.name
            return

        ret = re.match(self.REGULAR, input_value)
        if not ret:
            if self.custom_error_dict.get('invalid', None):
                self.error = self.custom_error_dict['invalid']
            else:
                self.error = "%s is invalid" % self.name
            return

        if self.max_length:
            if len(input_value) > self.max_length:
                if self.custom_error_dict.get('max_length', None):
                    self.error = self.custom_error_dict['max_length']
                else:
                    self.error = "%s max length is %s" % (self.name, self.max_length)
                return

        if self.min_length:
            if len(input_value) < self.max_length:
                if self.custom_error_dict.get('min_length', None):
                    self.error = self.custom_error_dict['min_length']
                else:
                    self.error = "%s min length is %s" % (self.name, self.min_length)
                return

        self.status = True


class IPField(Field):
    """
    字符串類字段
    """
    REGULAR = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$"
    DEFAULT_WIDGET = Widget.InputText

    def __init__(self, max_length=None, min_length=None, error=None, required=True, widget=None):
        """
        :param error: 自定義錯誤信息
                      如:{
                            'required': '值爲空時的錯誤提示',
                            'invalid': '格式錯誤時的錯誤提示',
                            'max_length': '最大長度爲10',
                            'min_length': '最小長度爲1',
                         }
        :param required: 是否必須
        :param widget: 指定插件,用於生成HTML標籤(默認生成Input標籤)
        :return:
        """
        self.custom_error_dict = {}
        if error:
            self.custom_error_dict.update(error)

        self.required = required
        self.max_length = max_length
        self.min_length = min_length

        widget = widget if widget else self.DEFAULT_WIDGET()

        super(IPField, self).__init__(widget)

    def valid(self, handler):
        """
        從請求中獲取用戶輸入的值並和規則進行比較
        :param handler: Tornado處理請求的XXXHandler對象
        :return:
        """
        input_value = FrameworkFactory.get_framework().get_argument(handler, self.name, None)
        # input_value = handler.get_argument(self.name, None)

        self.value = input_value
        if not input_value:
            if not self.required:
                self.status = True
                return

            if self.custom_error_dict.get('required', None):
                self.error = self.custom_error_dict['required']
            else:
                self.error = "%s is required" % self.name
            return

        ret = re.match(self.REGULAR, input_value)
        if not ret:
            if self.custom_error_dict.get('invalid', None):
                self.error = self.custom_error_dict['invalid']
            else:
                self.error = "%s is invalid" % self.name
            return

        if self.max_length:
            if len(input_value) > self.max_length:
                if self.custom_error_dict.get('max_length', None):
                    self.error = self.custom_error_dict['max_length']
                else:
                    self.error = "%s max length is %s" % (self.name, self.max_length)
                return

        if self.min_length:
            if len(input_value) < self.max_length:
                if self.custom_error_dict.get('min_length', None):
                    self.error = self.custom_error_dict['min_length']
                else:
                    self.error = "%s min length is %s" % (self.name, self.min_length)
                return

        self.status = True


class IntegerField(Field):
    """
    字符串類字段
    """
    REGULAR = "^\d+$"
    DEFAULT_WIDGET = Widget.InputText

    def __init__(self, max_value=None, min_value=None, error=None, required=True, widget=None):
        """
        :param error: 自定義錯誤信息
                      如:{
                            'required': '值爲空時的錯誤提示',
                            'invalid': '格式錯誤時的錯誤提示',
                            'max_length': '最大值爲10',
                            'min_length': '最小值度爲1',
                         }
        :param required: 是否必須
        :param widget: 指定插件,用於生成HTML標籤(默認生成Input標籤)
        :return:
        """
        self.custom_error_dict = {}
        if error:
            self.custom_error_dict.update(error)

        self.required = required
        self.max_value = max_value
        self.min_value = min_value

        widget = widget if widget else self.DEFAULT_WIDGET()

        super(IntegerField, self).__init__(widget)

    def valid(self, handler):
        """
        從請求中獲取用戶輸入的值並和規則進行比較
        :param handler: Tornado處理請求的XXXHandler對象
        :return:
        """
        input_value = FrameworkFactory.get_framework().get_argument(handler, self.name, None)
        # input_value = handler.get_argument(self.name, None)

        self.value = input_value

        if not input_value:
            if not self.required:
                self.status = True
                return

            if self.custom_error_dict.get('required', None):
                self.error = self.custom_error_dict['required']
            else:
                self.error = "%s is required" % self.name
            return

        ret = re.match(self.REGULAR, input_value)
        if not ret:
            if self.custom_error_dict.get('invalid', None):
                self.error = self.custom_error_dict['invalid']
            else:
                self.error = "%s is invalid" % self.name
            return

        input_value = int(input_value)
        self.value = input_value

        if self.max_value:
            if input_value > self.max_value:
                if self.custom_error_dict.get('max_value', None):
                    self.error = self.custom_error_dict['max_value']
                else:
                    self.error = "%s max value is %s" % (self.name, self.max_value)
                return

        if self.min_value:
            if input_value < self.min_value:
                if self.custom_error_dict.get('min_value', None):
                    self.error = self.custom_error_dict['min_value']
                else:
                    self.error = "%s min value is %s" % (self.name, self.min_value)
                return

        self.status = True


class FloatField(Field):
    """
    字符串類字段
    """
    REGULAR = "^\d+(\.\d{1,2})?$"
    DEFAULT_WIDGET = Widget.InputText

    def __init__(self, max_value=None, min_value=None, error=None, required=True, widget=None):
        """
        :param error: 自定義錯誤信息
                      如:{
                            'required': '值爲空時的錯誤提示',
                            'invalid': '格式錯誤時的錯誤提示',
                            'max_length': '最大值爲10',
                            'min_length': '最小值度爲1',
                         }
        :param required: 是否必須
        :param widget: 指定插件,用於生成HTML標籤(默認生成Input標籤)
        :return:
        """
        self.custom_error_dict = {}
        if error:
            self.custom_error_dict.update(error)

        self.required = required
        self.max_value = max_value
        self.min_value = min_value

        widget = widget if widget else self.DEFAULT_WIDGET()

        super(FloatField, self).__init__(widget)

    def valid(self, handler):
        """
        從請求中獲取用戶輸入的值並和規則進行比較
        :param handler: Tornado處理請求的XXXHandler對象
        :return:
        """
        input_value = FrameworkFactory.get_framework().get_argument(handler, self.name, None)
        # input_value = handler.get_argument(self.name, None)

        self.value = input_value
        if not input_value:
            if not self.required:
                self.status = True
                return

            if self.custom_error_dict.get('required', None):
                self.error = self.custom_error_dict['required']
            else:
                self.error = "%s is required" % self.name
            return

        ret = re.match(self.REGULAR, input_value)
        if not ret:
            if self.custom_error_dict.get('invalid', None):
                self.error = self.custom_error_dict['invalid']
            else:
                self.error = "%s is invalid" % self.name
            return

        input_value = float(input_value)
        self.value = input_value

        if self.max_value:
            if input_value > self.max_value:
                if self.custom_error_dict.get('max_value', None):
                    self.error = self.custom_error_dict['max_value']
                else:
                    self.error = "%s max value is %s" % (self.name, self.max_value)
                return

        if self.min_value:
            if input_value < self.min_value:
                if self.custom_error_dict.get('min_value', None):
                    self.error = self.custom_error_dict['min_value']
                else:
                    self.error = "%s min value is %s" % (self.name, self.min_value)
                return

        self.status = True


class StringListField(Field):
    """
    字符串類字段
    """
    REGULAR = "^.*$"
    DEFAULT_WIDGET = Widget.InputMultiCheckBox

    def __init__(self, ele_max_length=None, ele_min_length=None, error=None, required=True, widget=None):
        """
        :param error: 自定義錯誤信息
                      如:{
                            'required': '值爲空時的錯誤提示',
                            'element': '列表中的元素必須是字符串',
                            'ele_max_length': '最大長度爲10',
                            'ele_min_length': '最小長度爲1',
                         }
        :param required: 是否必須
        :param widget: 指定插件,用於生成HTML標籤(默認生成Input標籤)
        :return:
        """
        self.custom_error_dict = {}
        if error:
            self.custom_error_dict.update(error)

        self.required = required
        self.ele_max_length = ele_max_length
        self.ele_min_length = ele_min_length

        widget = widget if widget else self.DEFAULT_WIDGET()

        super(StringListField, self).__init__(widget)

    def valid(self, handler):
        """
        從請求中獲取用戶輸入的值並和規則進行比較
        :param handler: Tornado處理請求的XXXHandler對象
        :return:
        """
        input_value = FrameworkFactory.get_framework().get_arguments(handler, self.name, [])
        # input_value = handler.get_arguments(self.name)

        self.value = input_value

        if not input_value:
            if not self.required:
                self.status = True
                return

            if self.custom_error_dict.get('required', None):
                self.error = self.custom_error_dict['required']
            else:
                self.error = "%s is required" % self.name
            return

        for value in input_value:
            ret = re.match(self.REGULAR, value)
            if not ret:
                if self.custom_error_dict.get('element', None):
                    self.error = self.custom_error_dict['element']
                else:
                    self.error = "element %s is invalid" % self.name
                return

            if self.ele_max_length:
                if len(value) > self.ele_max_length:
                    if self.custom_error_dict.get('ele_max_length', None):
                        self.error = self.custom_error_dict['ele_max_length']
                    else:
                        self.error = "element %s max length is %s" % (self.name, self.ele_max_length)
                    return

            if self.ele_min_length:

                if len(value) < self.ele_min_length:
                    if self.custom_error_dict.get('ele_min_length', None):
                        self.error = self.custom_error_dict['ele_min_length']
                    else:
                        self.error = "element %s min length is %s" % (self.name, self.ele_min_length)
                    return

        self.status = True


class IntegerListField(Field):
    """
    字符串類字段
    """
    REGULAR = "^\d+$"
    DEFAULT_WIDGET = Widget.InputMultiCheckBox

    def __init__(self, ele_max_value=None, ele_min_value=None, error=None, required=True, widget=None):
        """
        :param error: 自定義錯誤信息
                      如:{
                            'required': '值爲空時的錯誤提示',
                            'element': '列表中的元素必須是數字',
                            'ele_max_value': '最大值爲x',
                            'ele_min_value': '最小值爲x',
                         }
        :param required: 是否必須
        :param widget: 指定插件,用於生成HTML標籤(默認生成Input標籤)
        :return:
        """
        self.custom_error_dict = {}
        if error:
            self.custom_error_dict.update(error)

        self.required = required
        self.ele_max_value = ele_max_value
        self.ele_min_value = ele_min_value

        widget = widget if widget else self.DEFAULT_WIDGET()

        super(IntegerListField, self).__init__(widget)

    def valid(self, handler):
        """
        從請求中獲取用戶輸入的值並和規則進行比較
        :param handler: Tornado處理請求的XXXHandler對象
        :return:
        """
        input_value = FrameworkFactory.get_framework().get_arguments(handler, self.name, [])
        # input_value = handler.get_arguments(self.name)
        self.value = input_value

        if not input_value:
            if not self.required:
                self.status = True
                return

            if self.custom_error_dict.get('required', None):
                self.error = self.custom_error_dict['required']
            else:
                self.error = "%s is required" % self.name
            return

        success_value_list = []
        for value in input_value:
            ret = re.match(self.REGULAR, value)
            if not ret:
                if self.custom_error_dict.get('element', None):
                    self.error = self.custom_error_dict['element']
                else:
                    self.error = "element %s is invalid" % self.name
                return
            value = int(value)
            success_value_list.append(value)

            if self.ele_max_value:
                if value > self.ele_max_value:
                    if self.custom_error_dict.get('ele_max_value', None):
                        self.error = self.custom_error_dict['ele_max_value']
                    else:
                        self.error = "element %s max value is %s" % (self.name, self.ele_max_value)
                    return

            if self.ele_min_value:

                if value < self.ele_min_value:
                    if self.custom_error_dict.get('ele_min_value', None):
                        self.error = self.custom_error_dict['ele_min_value']
                    else:
                        self.error = "element %s min value is %s" % (self.name, self.ele_min_value)
                    return

        self.value = success_value_list
        self.status = True
基於Field定義派生字段相關的類

3.咱們發現上面定義的子Field中包含widget,這個是根據不一樣的Filed的需求生成不一樣的html標籤。具體代碼以下:

#!/usr/bin/env python
# -*- coding:utf-8 -*-


class Input:
    def __init__(self, attr=None):
        """
        :param attr: 生成的HTML屬性,如:{'id': '123'}
        :return:
        """
        self.attr = attr if attr else {}

    def __str__(self):
        """
        使用對象時返回的字符串
        :return:
        """
        t = "<input %s />"
        attr_list = []
        for k, v in self.attr.items():
            temp = "%s='%s' " % (k, v,)
            attr_list.append(temp)
        tag = t % (''.join(attr_list))
        return tag


class InputText(Input):
    def __init__(self, attr=None):
        attr_dict = {'type': 'text'}
        if attr:
            attr_dict.update(attr)
        super(InputText, self).__init__(attr_dict)


class InputEmail(Input):
    def __init__(self, attr=None):
        attr_dict = {'type': 'email'}
        if attr:
            attr_dict.update(attr)
        super(InputEmail, self).__init__(attr_dict)


class InputPassword(Input):
    def __init__(self, attr=None):
        attr_dict = {'type': 'password'}
        if attr:
            attr_dict.update(attr)
        super(InputPassword, self).__init__(attr_dict)


class InputSingleCheckBox(Input):
    def __init__(self, attr=None):
        attr_dict = {'type': 'checkbox'}
        if attr:
            attr_dict.update(attr)
        super(InputSingleCheckBox, self).__init__(attr_dict)

    def __str__(self):
        """
        使用對象時返回的字符串
        :return:
        """
        t = "<input %s />"
        attr_list = []
        for k, v in self.attr.items():
            temp = "%s='%s' " % (k, v,)
            attr_list.append(temp)
        tag = t % (''.join(attr_list))
        return tag


class InputMultiCheckBox:
    def __init__(self, attr=None, text_value_list=None, checked_value_list=None):
        """
        :param attr: 生成的HTML屬性,如:{'id': '123'}
        :param text_value_list: 生成CheckBox的value和內容,如:
                                [
                                    {'value':1, 'text': '籃球'},
                                    {'value':2, 'text': '足球'},
                                    {'value':3, 'text': '乒乓球'},
                                    {'value':4, 'text': '羽毛球'},
                                ]
        :param checked_value_list: 被選中的checked_value_list,如:[2,3]
        :return:
        """
        attr_dict = {'type': 'checkbox'}
        if attr:
            attr_dict.update(attr)
        self.attr = attr_dict

        self.text_value_list = text_value_list if text_value_list else []
        self.checked_value_list = checked_value_list if checked_value_list else []

    def __str__(self):
        """
        使用對象時返回的字符串
        :return:
        """
        tag_list = []
        for item in self.text_value_list:
            a = "<div><span>%s</span><span>%s</span></div>"
            b = "<input %s />"
            attr_list = []
            for k, v in self.attr.items():
                temp = "%s='%s' " % (k, v,)
                attr_list.append(temp)
            attr_list.append("%s='%s' " % ('value', item['value']))
            if item['value'] in self.checked_value_list:
                attr_list.append("checked='checked' ")
            input_tag = b % (''.join(attr_list))
            c = a % (input_tag, item['text'], )
            tag_list.append(c)
        return ''.join(tag_list)


class InputRadio:
    def __init__(self, attr=None, text_value_list=None, checked_value=None):
        """
        :param attr: 生成的HTML屬性,如:{'id': '123'}
        :param text_value_list: 生成CheckBox的value和內容,如:
                                [
                                    {'value':1, 'text': '籃球'},
                                    {'value':2, 'text': '足球'},
                                    {'value':3, 'text': '乒乓球'},
                                    {'value':4, 'text': '羽毛球'},
                                ]
        :param checked_value: 被選中的checked_value,如:2
        :return:
        """
        attr_dict = {'type': 'radio'}
        if attr:
            attr_dict.update(attr)
        self.attr = attr_dict

        self.text_value_list = text_value_list if text_value_list else []
        self.checked_value = checked_value

    def __str__(self):
        """
        使用對象時返回的字符串
        :return:
        """
        tag_list = []
        for item in self.text_value_list:
            a = "<div><span>%s</span><span>%s</span></div>"
            b = "<input %s />"
            attr_list = []
            for k, v in self.attr.items():
                temp = "%s='%s' " % (k, v,)
                attr_list.append(temp)
            attr_list.append("%s='%s' " % ('value', item['value']))
            if item['value'] == self.checked_value:
                attr_list.append("checked='checked' ")
            input_tag = b % (''.join(attr_list))
            c = a % (input_tag,item['text'])
            tag_list.append(c)
        return ''.join(tag_list)


class SingleSelect:
    def __init__(self, attr=None, text_value_list=None, selected_value=None):
        """
        :param attr: 生成的HTML屬性,如:{'id': '123'}
        :param text_value_list: 生成CheckBox的value和內容,如:
                                [
                                    {'value':1, 'text': '籃球'},
                                    {'value':2, 'text': '足球'},
                                    {'value':3, 'text': '乒乓球'},
                                    {'value':4, 'text': '羽毛球'},
                                ]
        :param selected_value: 被選中的checked_value,如:2
        :return:
        """
        attr_dict = {}
        if attr:
            attr_dict.update(attr)
        self.attr = attr_dict

        self.text_value_list = text_value_list if text_value_list else []
        self.selected_value = selected_value

    def __str__(self):
        """
        使用對象時返回的字符串
        :return:
        """

        a = "<select %s>%s</select>"

        attr_list = []
        for k, v in self.attr.items():
            temp = "%s='%s' " % (k, v,)
            attr_list.append(temp)

        option_list = []

        for item in self.text_value_list:

            if item['value'] == self.selected_value:
                b = "<option selected='selected' value='%s'>%s</option>"
            else:
                b = "<option value='%s'>%s</option>"
            option = b % (item['value'], item['text'],)
            option_list.append(option)

        tag = a % (''.join(attr_list), ''.join(option_list))

        return tag


class MultiSelect:
    def __init__(self, attr=None, text_value_list=None, selected_value_list=None):
        """
        :param attr: 生成的Select標籤的屬性,如:{'id': '123'}
        :param text_value_list: 生成CheckBox的value和內容,如:
                                [
                                    {'value':1, 'text': '籃球'},
                                    {'value':2, 'text': '足球'},
                                    {'value':3, 'text': '乒乓球'},
                                    {'value':4, 'text': '羽毛球'},
                                ]
        :param selected_value_list: selected_value_list,如:[2,3,4]
        :return:
        """
        attr_dict = {'multiple': 'multiple'}
        if attr:
            attr_dict.update(attr)
        self.attr = attr_dict

        self.text_value_list = text_value_list if text_value_list else []
        self.selected_value_list = selected_value_list if selected_value_list else []

    def __str__(self):
        """
        使用對象時返回的字符串
        :return:
        """

        a = "<select %s>%s</select>"

        attr_list = []
        for k, v in self.attr.items():
            temp = "%s='%s' " % (k, v,)
            attr_list.append(temp)

        option_list = []
        for item in self.text_value_list:
            if item['value'] in self.selected_value_list:
                b = "<option selected='selected' value='%s'>%s</option>"
            else:
                b = "<option value='%s'>%s</option>"
            option = b % (item['value'], item['text'],)
            option_list.append(option)

        tag = a % (''.join(attr_list), ''.join(option_list))
        return tag


class TextArea:
    def __init__(self, attr=None, value=""):
        """
        :param attr: 生成的HTML屬性,如:{'id': '123'}
        :return:
        """
        self.attr = attr if attr else {}
        self.value = value

    def __str__(self):
        """
        使用對象時返回的字符串
        :return:
        """
        t = "<textarea %s>%s</textarea>"
        attr_list = []
        for k, v in self.attr.items():
            temp = "%s='%s' " % (k, v,)
            attr_list.append(temp)
        tag = t % (''.join(attr_list), self.value)
        return tag
widget.py

 下面是具體的使用方法:

使用文檔

一、下載安裝

pip install PyTyrion 

二、配置WEB框架種類

因爲Tyrion同時支持Tornado、Django、Flask、Bottle多個WEB框架,全部在使用前須要進行指定。

import Tyrion
Tyrion.setup('tornado')
# setup的參數有:tornado(默認)、django、bottle、flask

三、建立Form類

Form類用於提供驗證規則、插件屬性、錯誤信息等

from Tyrion.Forms import Form
from Tyrion.Fields import StringField
from Tyrion.Fields import EmailField
 
class LoginForm(Form):
    username = StringField(error={'required': '用戶名不能爲空'})
    password = StringField(error={'required': '密碼不能爲空'})
    email = EmailField(error={'required': '郵箱不能爲空', 'invalid': '郵箱格式錯誤'})

四、驗證用戶請求

前端HTML代碼:

<form method="POST" action="/login.html">
    <div>
        <input type="text" name="username">
    </div>
    <div>
        <input type="text" name="password">
    </div>
    <div>
        <input type="text" name="email">
    </div>
 
    <input type="submit" value="提交">
</form>

 用戶提交數據時,在後臺書寫以下代碼便可實現用戶請求數據驗證(Tornado示例):

class LoginHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render('login.html')
 
    def post(self, *args, **kwargs):
        form = LoginForm(self)
 
        ###### 檢查用戶輸入是否合法 ######
        if form.is_valid():
 
            ###### 若是不合法,則輸出錯誤信息 ######
            print(form.error_dict)
        else:
            ###### 若是合法,則輸出用戶輸入的內容 ######

五、驗證用戶請求 && 生成HTML標籤 && 保留上次輸入內容 && 錯誤提示

Tyrion不只能夠驗證用戶請求,還能夠生成自動建立HTML標籤而且能夠保留用戶上次輸入的內容。在HTML模板中調用Form類對象的字段便可,如(Tornado示例):

  from Tyrion.Forms import Form
  from Tyrion.Fields import StringField
  from Tyrion.Fields import EmailField

        class LoginForm(Form):
            username = StringField(error={'required': '用戶名不能爲空'})
            password = StringField(error={'required': '密碼不能爲空'})
            email = EmailField(error={'required': '郵箱不能爲空', 'invalid': '郵箱格式錯誤'})
Form類
class LoginHandler(tornado.web.RequestHandler):
            def get(self, *args, **kwargs):
                form = LoginForm(self)
                self.render('login.html', form=form)

            def post(self, *args, **kwargs):
                form = LoginForm(self)

                print(form.is_valid())
                print(form.error_dict)
                print(form.value_dict)

                self.render('login.html', form=form)
處理請求
<form method="post" action="/login.html">
            <div>
                <!-- Form建立的標籤 -->
                {% raw form.username %}

                <!-- 錯誤信息 -->
                <span>{{form.error_dict.get('username',"")}}</span>
            </div>
            <div>
                {% raw form.password %}
                <span>{{form.error_dict.get('password',"")}}</span>
            </div>
            <div>
                {% raw form.email %}
                <span>{{form.error_dict.get('email',"")}}</span>
            </div>
            <input type="submit" value="提交"/>
        </form>
建立模板login.html

六、Form字段類型

Form的字段用於指定從請求中獲取的數據類型以及格式,以此來驗證用戶輸入的內容。

from Tyrion.Forms import Form
from Tyrion.Fields import StringField
from Tyrion.Fields import EmailField
 
class LoginForm(Form):
    username = StringField(error={'required': '用戶名不能爲空'})
    password = StringField(error={'required': '密碼不能爲空'})
    email = EmailField(error={'required': '郵箱不能爲空', 'invalid': '郵箱格式錯誤'})

以上代碼表示此Form類能夠用於驗證用戶輸入的內容,而且 username和password必須不能爲空,email必須不能爲空而且必須是郵箱格式。

目前支持全部字段:

StringField
    """
    要求必須是字符串,即:正則^.*$
 
    參數:
        required    布爾值,是否容許爲空
        max_length  整數,限制用戶輸入內容最大長度
        min_length  整數,限制用戶輸入內容最小長度
        error       字典,自定義錯誤提示,如:{
                                            'required': '值爲空時的錯誤提示',
                                            'invalid': '格式錯誤時的錯誤提示',
                                            'max_length': '最大長度爲10',
                                            'min_length': '最小長度爲1',
                                          }
        widget      定製生成的HTML插件(默認InputText)
    """
 
EmailField
    """
    要求必須是郵箱格式的字符串
 
    參數:
        required    布爾值,是否容許爲空
        max_length  整數,限制用戶輸入內容最大長度
        min_length  整數,限制用戶輸入內容最小長度
        error       字典,自定義錯誤提示,如:{
                                            'required': '值爲空時的錯誤提示',
                                            'invalid': '格式錯誤時的錯誤提示',
                                            'max_length': '最大長度爲10',
                                            'min_length': '最小長度爲1',
                                          }
        widget      定製生成的HTML插件(默認InputText)
    """
 
IPField
    """
    要求必須是IP格式
 
    參數:
        required    布爾值,是否容許爲空
        max_length  整數,限制用戶輸入內容最大長度
        min_length  整數,限制用戶輸入內容最小長度
        error       字典,自定義錯誤提示,如:{
                                            'required': '值爲空時的錯誤提示',
                                            'invalid': '格式錯誤時的錯誤提示',
                                            'max_length': '最大長度爲10',
                                            'min_length': '最小長度爲1',
                                          }
        widget      定製生成的HTML插件(默認InputText)
 
    """
 
IntegerField
    """
    要求必須整數格式
 
    參數:
        required    布爾值,是否容許爲空
        max_value   整數,限制用戶輸入數字最大值
        min_value   整數,限制用戶輸入數字最小值
        error       字典,自定義錯誤提示,如:{
                                            'required': '值爲空時的錯誤提示',
                                            'invalid': '格式錯誤時的錯誤提示',
                                            'max_value': '最大值爲10',
                                            'max_value': '最小值度爲1',
                                          }
        widget      定製生成的HTML插件(默認InputText)
 
    """
 
FloatField
    """
    要求必須小數格式
 
    參數:
        required    布爾值,是否容許爲空
        max_value   整數,限制用戶輸入數字最大值
        min_value   整數,限制用戶輸入數字最小值
        error       字典,自定義錯誤提示,如:{
                                            'required': '值爲空時的錯誤提示',
                                            'invalid': '格式錯誤時的錯誤提示',
                                            'max_value': '最大值爲10',
                                            'max_value': '最小值度爲1',
                                          }
        widget      定製生成的HTML插件(默認InputText)
    """
 
StringListField
    """
    用於獲取請求中的多個值,且保證每個元素是字符串,即:正則^.*$
    如:checkbox或selct多選時,會提交多個值,用此字段能夠將用戶提交的值保存至列表
 
    參數:
        required         布爾值,是否容許爲空
        ele_max_length   整數,限制用戶輸入的每一個元素內容最大長度
        ele_min_length   整數,限制用戶輸入的每一個元素內容最小長度
        error            字典,自定義錯誤提示,如:{
                                            'required': '值爲空時的錯誤提示',
                                            'element': '列表中的元素必須是字符串',
                                            'ele_max_length': '最大長度爲10',
                                            'ele_min_length': '最小長度爲1',
                                          }
        widget           定製生成的HTML插件(默認InputMultiCheckBox,即:checkbox)
    """
 
IntegerListField
    """
    用於獲取請求中的多個值,且保證每個元素是整數
    如:checkbox或selct多選時,會提交多個值,用此字段能夠將用戶提交的值保存至列表
 
    參數:
        required         布爾值,是否容許爲空
        ele_max_value   整數,限制用戶輸入的每一個元素內容最大長度
        ele_min_value   整數,限制用戶輸入的每一個元素內容最小長度
        error            字典,自定義錯誤提示,如:{
                                            'required': '值爲空時的錯誤提示',
                                            'element': '列表中的元素必須是數字',
                                            'ele_max_value': '最大值爲x',
                                            'ele_min_value': '最小值爲x',
                                          }
        widget           定製生成的HTML插件(默認InputMultiCheckBox,即:checkbox)
    """
View Code

七、Form字段widget參數:HTML插件

HTML插件用於指定當前字段在生成HTML時表現的種類和屬性,經過指定此參數從而實現定製頁面上生成的HTML標籤

from Tyrion.Forms import Form
from Tyrion.Fields import StringField
from Tyrion.Fields import EmailField
 
from Tyrion.Widget import InputPassword
 
class LoginForm(Form):
    password = StringField(error={'required': '密碼不能爲空'},widget=InputPassword())

上述LoginForm的password字段要求用戶輸入必須是字符串類型,而且指定生成HTML標籤時會建立爲<input type='password' > 標籤

目前支持全部插件:

InputText
        """
        設置Form對應字段在HTML中生成input type='text' 標籤

        參數:
            attr    字典,指定生成標籤的屬性,如: attr = {'class': 'c1', 'placeholder': 'username'}
        """
    InputEmail
        """
        設置Form對應字段在HTML中生成input type='email' 標籤

        參數:
            attr    字典,指定生成標籤的屬性,如: attr = {'class': 'c1', 'placeholder': 'username'}
        """
    InputPassword
        """
        設置Form對應字段在HTML中生成input type='password' 標籤

        參數:
            attr    字典,指定生成標籤的屬性,如: attr = {'class': 'c1', 'placeholder': 'username'}
        """
    TextArea
        """
        設置Form對應字段在HTML中生成 textarea 標籤

        參數:
            attr    字典,指定生成標籤的屬性,如: attr = {'class': 'c1'}
            value   字符串,用於設置textarea標籤中默認顯示的內容
        """

    InputRadio
        """
        設置Form對應字段在HTML中生成一系列 input type='radio' 標籤(選擇時互斥)

        參數:
            attr                字典,生成的HTML屬性,如:{'class': 'c1'}
            text_value_list     列表,生成的多個radio標籤的內容和值,如:[
                                                                {'value':1, 'text': '男'},
                                                                {'value':2, 'text': '女'},
                                                            ]
            checked_value       整數或字符串,默認被選中的標籤的value的值

        示例:
                from Tyrion.Forms import Form
                from Tyrion.Fields import IntegerField

                from Tyrion.Widget import InputRadio


                class LoginForm(Form):
                    favor = IntegerField(error={'required': '愛好不能爲空'},
                                         widget=InputRadio(attr={'class': 'c1'},
                                                           text_value_list=[
                                                               {'value': 1, 'text': '男'},
                                                               {'value': 2, 'text': '女'}, ],
                                                           checked_value=2
                                                           )
                                         )
                上述favor字段生成的HTML標籤爲:
                    <div>
                        <span>
                            <input class='c1' type="radio" name="gender" value="1">
                        </span>
                        <span></span>
                    </div>
                    <div>
                        <span>
                            <input class='c1' type="radio" name="gender" value="2" checked='checked'>
                        </span>
                        <span></span>
                    </div>
        """

    InputSingleCheckBox
        """
        設置Form對應字段在HTML中生成 input type='checkbox' 標籤
        參數:
            attr    字典,指定生成標籤的屬性,如: attr = {'class': 'c1'}
        """

    InputMultiCheckBox
        """
        設置Form對應字段在HTML中生成一系列 input type='checkbox' 標籤

        參數:
            attr                字典,指定生成標籤的屬性,如: attr = {'class': 'c1'}
            text_value_list     列表,生成的多個checkbox標籤的內容和值,如:[
                                                                        {'value':1, 'text': '籃球'},
                                                                        {'value':2, 'text': '足球'},
                                                                        {'value':3, 'text': '乒乓球'},
                                                                        {'value':4, 'text': '羽毛球'},
                                                                    ]
            checked_value_list  列表,默認選中的標籤對應的value, 如:[1,3]
        """
    SingleSelect
        """
        設置Form對應字段在HTML中生成 單選select 標籤

        參數:
            attr                字典,指定生成標籤的屬性,如: attr = {'class': 'c1'}
            text_value_list     列表,用於指定select標籤中的option,如:[
                                                                        {'value':1, 'text': '北京'},
                                                                        {'value':2, 'text': '上海'},
                                                                        {'value':3, 'text': '廣州'},
                                                                        {'value':4, 'text': '重慶'},
                                                                    ]
            selected_value      數字或字符串,默認被選中選項對應的值,如: 3
        """

    MultiSelect
        """
        設置Form對應字段在HTML中生成 多選select 標籤

        參數:
            attr                字典,指定生成標籤的屬性,如: attr = {'class': 'c1'}
            text_value_list     列表,用於指定select標籤中的option,如:[
                                                                        {'value':1, 'text': '籃球'},
                                                                        {'value':2, 'text': '足球'},
                                                                        {'value':3, 'text': '乒乓球'},
                                                                        {'value':4, 'text': '羽毛球'},
                                                                    ]
            selected_value_list 列表,默認被選中選項對應的值,如:[2,3,4]
        """
View Code

八、動態初始化默認值

因爲Form能夠用於生成HTML標籤,若是想要在建立標籤的同時再爲其設置默認值有兩種方式:

  • 靜態,在插件參數中指定
  • 動態,調用Form對象的 init_field_value 方法來指定
class InitValueForm(Form):
        username = StringField(error={'required': '用戶名不能爲空'})
        age = IntegerField(max_value=500,
                           min_value=0,
                           error={'required': '年齡不能爲空',
                                  'invalid': '年齡必須爲數字',
                                  'min_value': '年齡不能小於0',
                                  'max_value': '年齡不能大於500'})

        city = IntegerField(error={'required': '年齡不能爲空', 'invalid': '年齡必須爲數字'},
                            widget=SingleSelect(text_value_list=[{'value': 1, 'text': '上海'},
                                                                 {'value': 2, 'text': '北京'},
                                                                 {'value': 3, 'text': '廣州'}])
                            )

        gender = IntegerField(error={'required': '請選擇性別',
                                     'invalid': '性別必須爲數字'},
                              widget=InputRadio(text_value_list=[{'value': 1, 'text': '男', },
                                                                 {'value': 2, 'text': '女', }],
                                                checked_value=2))

        protocol = IntegerField(error={'required': '請選擇協議', 'invalid': '協議格式錯誤'},
                                widget=InputSingleCheckBox(attr={'value': 1}))

        favor_int_val = IntegerListField(error={'required': '請選擇愛好', 'invalid': '選擇愛好格式錯誤'},
                                         widget=InputMultiCheckBox(text_value_list=[{'value': 1, 'text': '籃球', },
                                                                                    {'value': 2, 'text': '足球', },
                                                                                    {'value': 3, 'text': '乒乓球', },
                                                                                    {'value': 4, 'text': '羽毛球'}, ]))

        favor_str_val = StringListField(error={'required': '請選擇愛好', 'invalid': '選擇愛好格式錯誤'},
                                        widget=InputMultiCheckBox(text_value_list=[{'value': '1', 'text': '籃球', },
                                                                                   {'value': '2', 'text': '足球', },
                                                                                   {'value': '3', 'text': '乒乓球', },
                                                                                   {'value': '4', 'text': '羽毛球'}, ]))

        select_str_val = StringListField(error={'required': '請選擇愛好', 'invalid': '選擇愛好格式錯誤'},
                                         widget=MultiSelect(text_value_list=[{'value': '1', 'text': '籃球', },
                                                                             {'value': '2', 'text': '足球', },
                                                                             {'value': '3', 'text': '乒乓球', },
                                                                             {'value': '4', 'text': '羽毛球'}, ]))

        select_int_val = IntegerListField(error={'required': '請選擇愛好', 'invalid': '選擇愛好格式錯誤'},
                                          widget=MultiSelect(text_value_list=[{'value': 1, 'text': '籃球', },
                                                                              {'value': 2, 'text': '足球', },
                                                                              {'value': 3, 'text': '乒乓球', },
                                                                              {'value': 4, 'text': '羽毛球'}, ]))
動態初始值 - Form類
class InitValueHandler(tornado.web.RequestHandler):

            def get(self, *args, **kwargs):
                form = InitValueForm(self)

                init_dict = {
                    'username': 'seven',
                    'age': 18,
                    'city': 2,
                    'gender': 2,
                    'protocol': 1,
                    'favor_int_val': [1, 3],
                    'favor_str_val': ['1', '3'],
                    'select_int_val': [1, 3],
                    'select_str_val': ['1', '3']

                }

                # 初始化操做,設置Form類中默認值以及默認選項
                form.init_field_value(init_dict)

                self.render('init_value.html', form=form)
動態初始值 - 處理請求的Handler(Tornado)

九、更多示例

示例源碼下載:猛擊這裏

a. 基本使用

class RegisterForm(Form):
        username = StringField(max_length=32,
                               min_length=6,
                               error={'required': '用戶名不能爲空',
                                      'min_length': '用戶名不能少於6位',
                                      'max_length': '用戶名不能超過32位'})
 
        password = StringField(max_length=32,
                               min_length=6,
                               error={'required': '密碼不能爲空'},
                               widget=InputPassword())
 
        gender = IntegerField(error={'required': '請選擇性別',
                                     'invalid': '性別必須爲數字'},
                              widget=InputRadio(text_value_list=[{'value': 1, 'text': '男', },
                                                                 {'value': 2, 'text': '女', }],
                                                checked_value=2))
 
        age = IntegerField(max_value=500,
                           min_value=0,
                           error={'required': '年齡不能爲空',
                                  'invalid': '年齡必須爲數字',
                                  'min_value': '年齡不能小於0',
                                  'max_value': '年齡不能大於500'})
 
        email = EmailField(error={'required': '郵箱不能爲空',
                                  'invalid': '郵箱格式錯誤'})
 
        city = IntegerField(error={'required': '城市選項不能爲空', 'invalid': '城市選項必須爲數字'},
                            widget=SingleSelect(text_value_list=[{'value': 1, 'text': '上海'},
                                                                 {'value': 2, 'text': '北京'},
                                                                 {'value': 3, 'text': '廣州'}])
                            )
        protocol = IntegerField(error={'required': '請選擇協議', 'invalid': '協議格式錯誤'},
                                widget=InputSingleCheckBox(attr={'value': 1}))
 
        memo = StringField(required=False,
                           max_length=150,
                           error={'invalid': '備註格式錯誤', 'max_length': '備註最大長度爲150字'},
                           widget=TextArea())
registerForm

b. 多選checkbox

class MultiCheckBoxForm(Form):
         favor_str_val = StringListField(error={'required': '請選擇愛好', 'invalid': '選擇愛好格式錯誤'},
                                         widget=InputMultiCheckBox(text_value_list=[{'value': '1', 'text': '籃球', },
                                                                                    {'value': '2', 'text': '足球', },
                                                                                    {'value': '3', 'text': '乒乓球', },
                                                                                    {'value': '4', 'text': '羽毛球'}, ]))
 
         favor_str_val_default = StringListField(error={'required': '請選擇愛好', 'invalid': '選擇愛好格式錯誤'},
                                                 widget=InputMultiCheckBox(text_value_list=[{'value': '1', 'text': '籃球', },
                                                                                            {'value': '2', 'text': '足球', },
                                                                                            {'value': '3', 'text': '乒乓球', },
                                                                                            {'value': '4', 'text': '羽毛球'}, ],
                                                                           checked_value_list=['1', '4']))
 
         favor_int_val = IntegerListField(error={'required': '請選擇愛好', 'invalid': '選擇愛好格式錯誤'},
                                          widget=InputMultiCheckBox(text_value_list=[{'value': 1, 'text': '籃球', },
                                                                                     {'value': 2, 'text': '足球', },
                                                                                     {'value': 3, 'text': '乒乓球', },
                                                                                     {'value': 4, 'text': '羽毛球'}, ]))
 
         favor_int_val_default = IntegerListField(error={'required': '請選擇愛好', 'invalid': '選擇愛好格式錯誤'},
                                                  widget=InputMultiCheckBox(text_value_list=[{'value': 1, 'text': '籃球', },
                                                                                             {'value': 2, 'text': '足球', },
                                                                                             {'value': 3, 'text': '乒乓球', },
                                                                                             {'value': 4, 'text': '羽毛球'}, ],
                                                                            checked_value_list=[2, ]))
MultiCheckBoxForm

c、多選select

class MultiSelectForm(Form):
        select_str_val = StringListField(error={'required': '請選擇愛好', 'invalid': '選擇愛好格式錯誤'},
                                         widget=MultiSelect(text_value_list=[{'value': '1', 'text': '籃球', },
                                                                             {'value': '2', 'text': '足球', },
                                                                             {'value': '3', 'text': '乒乓球', },
                                                                             {'value': '4', 'text': '羽毛球'}, ]))
 
        select_str_val_default = StringListField(error={'required': '請選擇愛好', 'invalid': '選擇愛好格式錯誤'},
                                                 widget=MultiSelect(text_value_list=[{'value': '1', 'text': '籃球', },
                                                                                     {'value': '2', 'text': '足球', },
                                                                                     {'value': '3', 'text': '乒乓球', },
                                                                                     {'value': '4', 'text': '羽毛球'}, ],
                                                                    selected_value_list=['1', '3']))
 
        select_int_val = IntegerListField(error={'required': '請選擇愛好', 'invalid': '選擇愛好格式錯誤'},
                                          widget=MultiSelect(text_value_list=[{'value': 1, 'text': '籃球', },
                                                                              {'value': 2, 'text': '足球', },
                                                                              {'value': 3, 'text': '乒乓球', },
                                                                              {'value': 4, 'text': '羽毛球'}, ]))
 
        select_int_val_default = IntegerListField(error={'required': '請選擇愛好', 'invalid': '選擇愛好格式錯誤'},
                                                  widget=MultiSelect(text_value_list=[{'value': 1, 'text': '籃球', },
                                                                                      {'value': 2, 'text': '足球', },
                                                                                      {'value': 3, 'text': '乒乓球', },
                                                                                      {'value': 4, 'text': '羽毛球'}, ],
                                                                     selected_value_list=[2]))
MultiSelectForm

d. 動態select選項

class DynamicSelectForm(Form):
    city = IntegerField(error={'required': '年齡不能爲空', 'invalid': '年齡必須爲數字'},
                        widget=SingleSelect(text_value_list=[{'value': 1, 'text': '上海'},
                                                             {'value': 2, 'text': '北京'},
                                                             {'value': 3, 'text': '廣州'}])
                        )
 
    multi_favor = IntegerListField(error={'required': '請選擇愛好', 'invalid': '選擇愛好格式錯誤'},
                                   widget=MultiSelect(text_value_list=[{'value': 1, 'text': '籃球', },
                                                                       {'value': 2, 'text': '足球', },
                                                                       {'value': 3, 'text': '乒乓球', },
                                                                       {'value': 4, 'text': '羽毛球'}, ]))
 
    def __init__(self, *args, **kwargs):
        super(DynamicSelectForm, self).__init__(*args, **kwargs)
 
        # 獲取數據庫中的最新數據並顯示在頁面上(每次建立對象都執行一次數據庫操做來獲取最新數據)
        self.city.widget.text_value_list = [{'value': 1, 'text': '上海'},
                                            {'value': 2, 'text': '北京'},
                                            {'value': 3, 'text': '南京'},
                                            {'value': 4, 'text': '廣州'}]
 
        self.multi_favor.widget.text_value_list = [{'value': 1, 'text': '籃球'},
                                                   {'value': 2, 'text': '足球'},
                                                   {'value': 3, 'text': '乒乓球'},
                                                   {'value': 4, 'text': '羽毛球'},
                                                   {'value': 5, 'text': '玻璃球'}]
DynamicSelectForm

三.自定義Session

對Session來講Tornado是沒有的

簡單回顧下:

一、自動生成一段字符串

二、將字符串發送到客戶端的瀏覽器,同時把字符串當作key放在session裏。(能夠理解爲session就是一個字典)

三、在用戶的session對應的value裏設置任意值

操做session

  • 獲取session:request.session[key]
  • 設置session:reqeust.session[key] = value
  • 刪除session:del request[key]
request.session.set_expiry(value)
* 若是value是個整數,session會在些秒數後失效。
* 若是value是個datatime或timedelta,session就會在這個時間後失效。
* 若是value是0,用戶關閉瀏覽器session就會失效。
* 若是value是None,session會依賴全局session失效策略。

 二、上面是Django的原理是同樣的,那麼咱們來本身寫一個Session

2.一、儲備知識點

#!/usr/bin/env python
# -*- coding:utf-8 -*-
  
class Foo(object):
  
    def __getitem__(self, key):
        print  '__getitem__',key
  
    def __setitem__(self, key, value):
        print '__setitem__',key,value
  
    def __delitem__(self, key):
        print '__delitem__',key

 2.二、應用工廠方法模式定義session保存的位置,用戶只需在配置文件修改便可

class SessionHandler:
    def initialize(self):
        self.session_obj = SessionFacotory.get_session_obj(self)


class SessionFacotory:
    @staticmethod
    def get_session_obj(handler):
        if conf.session_type == 'redis':
            return RedisSession(handler)
        elif conf.session_type == 'memcache':
            return RedisSession(handler)
        else:
            return MemorySession(handler)

 2.3.緩存session

class CacheSession:
    session_container = {}
    session_id = "__sessionId__"

    def __init__(self, handler):
        self.handler = handler
        client_random_str = handler.get_cookie(CacheSession.session_id, None)
        if client_random_str and client_random_str in CacheSession.session_container:
            self.random_str = client_random_str
        else:
            self.random_str = create_session_id()
            CacheSession.session_container[self.random_str] = {}

        expires_time = time.time() + config.SESSION_EXPIRES
        handler.set_cookie(CacheSession.session_id, self.random_str, expires=expires_time)

    def __getitem__(self, key):
        ret = CacheSession.session_container[self.random_str].get(key, None)
        return ret

    def __setitem__(self, key, value):
        CacheSession.session_container[self.random_str][key] = value

    def __delitem__(self, key):
        if key in CacheSession.session_container[self.random_str]:
            del CacheSession.session_container[self.random_str][key]
CacheSession

2.4. memcache session

import memcache

conn = memcache.Client(['192.168.11.119:12000'], debug=True, cache_cas=True)

class MemcachedSession:
    session_id = "__sessionId__"

    def __init__(self, handler):
        self.handler = handler
        # 從客戶端獲取隨機字符串
        client_random_str = handler.get_cookie(CacheSession.session_id, None)
        # 若是從客戶端獲取到了隨機字符串
        #
        if client_random_str and conn.get(client_random_str):
            self.random_str = client_random_str
        else:
            self.random_str = create_session_id()
            conn.set(self.random_str, json.dumps({}), config.SESSION_EXPIRES)
            #CacheSession.session_container[self.random_str] = {}

        conn.set(self.random_str, conn.get(self.random_str), config.SESSION_EXPIRES)

        expires_time = time.time() + config.SESSION_EXPIRES
        handler.set_cookie(MemcachedSession.session_id, self.random_str, expires=expires_time)

    def __getitem__(self, key):
        # ret = CacheSession.session_container[self.random_str].get(key, None)
        ret = conn.get(self.random_str)
        ret_dict = json.loads(ret)
        result = ret_dict.get(key,None)
        return result

    def __setitem__(self, key, value):
        ret = conn.get(self.random_str)
        ret_dict = json.loads(ret)
        ret_dict[key] = value
        conn.set(self.random_str, json.dumps(ret_dict), config.SESSION_EXPIRES)

        # CacheSession.session_container[self.random_str][key] = value

    def __delitem__(self, key):
        ret = conn.get(self.random_str)
        ret_dict = json.loads(ret)
        del ret_dict[key]
        conn.set(self.random_str, json.dumps(ret_dict), config.SESSION_EXPIRES)
MemcachedSession 

2.5 redis session

import redis

pool = redis.ConnectionPool(host='192.168.11.119', port=6379)
r = redis.Redis(connection_pool=pool)

class RedisSession:
    session_id = "__sessionId__"

    def __init__(self, handler):
        self.handler = handler
        # 從客戶端獲取隨機字符串
        client_random_str = handler.get_cookie(CacheSession.session_id, None)
        # 若是從客戶端獲取到了隨機字符串
        if client_random_str and r.exists(client_random_str):
            self.random_str = client_random_str
        else:
            self.random_str = create_session_id()
            r.hset(self.random_str,None,None)

            # conn.set(self.random_str, json.dumps({}), config.SESSION_EXPIRES)
            # CacheSession.session_container[self.random_str] = {}
        r.expire(self.random_str, config.SESSION_EXPIRES)
        # conn.set(self.random_str, conn.get(self.random_str), config.SESSION_EXPIRES)


        expires_time = time.time() + config.SESSION_EXPIRES
        handler.set_cookie(RedisSession.session_id, self.random_str, expires=expires_time)

    def __getitem__(self, key):
        # ret = CacheSession.session_container[self.random_str].get(key, None)
        result = r.hget(self.random_str,key)
        if result:
            ret_str = str(result, encoding='utf-8')
            try:
                result = json.loads(ret_str)
            except:
                result = ret_str
            return result
        else:
            return result

    def __setitem__(self, key, value):
        if type(value) == dict:
            r.hset(self.random_str, key, json.dumps(value))
        else:
            r.hset(self.random_str, key, value)

        # CacheSession.session_container[self.random_str][key] = value

    def __delitem__(self, key):
        r.hdel(self.random_str,key)
RedisSession

tornado cookie

 Cookie,有時也用其複數形式Cookies,指某些網站爲了辨別用戶身份、進行session跟蹤而儲存在用戶本地終端上的數據(一般通過加密)。定義於RFC2109和2965都已廢棄,最新取代的規範是RFC6265。(能夠叫作瀏覽器緩存)

一、cookie的基本操做

#!/usr/bin/env python
# -*- coding:utf-8 -*-
   
import tornado.ioloop
import tornado.web
   
   
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        print(self.cookies)              # 獲取全部的cookie
        self.set_cookie('k1','v1')       # 設置cookie
        print(self.get_cookie('k1'))     # 獲取指定的cookie
        self.write("Hello, world")
   
application = tornado.web.Application([
    (r"/index", MainHandler),
])
   
   
if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

 

二、加密cookie(簽名)

Cookie 很容易被惡意的客戶端僞造。加入你想在 cookie 中保存當前登錄用戶的 id 之類的信息,你須要對 cookie 做簽名以防止僞造。Tornado 經過 set_secure_cookie 和 get_secure_cookie 方法直接支持了這種功能。 要使用這些方法,你須要在建立應用時提供一個密鑰,名字爲 cookie_secret。 你能夠把它做爲一個關鍵詞參數傳入應用的設置中:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
   
import tornado.ioloop
import tornado.web
    
class MainHandler(tornado.web.RequestHandler):
    def get(self):
         if not self.get_secure_cookie("mycookie"):             # 獲取帶簽名的cookie
             self.set_secure_cookie("mycookie", "myvalue")      # 設置帶簽名的cookie
             self.write("Your cookie was not set yet!")
         else:
             self.write("Your cookie was set!")
application = tornado.web.Application([
    (r"/index", MainHandler),
])
   
if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

 簽名Cookie的本質是:

寫cookie過程:

    將值進行base64加密
    對除值之外的內容進行簽名,哈希算法(沒法逆向解析)
    拼接 簽名 + 加密值

讀cookie過程:

    讀取 簽名 + 加密值
    對簽名進行驗證
    base64解密,獲取值內容

3.JavaScript操做Cookie

/*
設置cookie,指定秒數過時,
name表示傳入的key,
value表示傳入相對應的value值,
expires表示當前日期在加5秒過時
 */

function setCookie(name,value,expires){
    var temp = [];
    var current_date = new Date();
    current_date.setSeconds(current_date.getSeconds() + 5);
    document.cookie = name + "= "+ value +";expires=" + current_date.toUTCString();
} 

tornado上傳文件

上傳文件這塊能夠分爲兩大類,第一類是經過form表單驗證進行上傳,還有一類就是經過ajax上傳,下面就來介紹一下這兩類

一、form表單上傳文件

import tornado.web
import tornado.ioloop
import os

class IndexHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render('index.html')

    def post(self, *args, **kwargs):
        file_metas = self.request.files["filename"]     # 獲取文件信息
        for meta in file_metas:                         
            file_name = meta['filename']                # 得到他的文件名字
            file_names = os.path.join('static','img',file_name)
            with open(file_names,'wb') as up:           # 打開本地一個文件
                up.write(meta['body'])                  # body就是文件內容,把他寫到本地

settings = {
    'template_path':'views',
    'static_path':'static',
    'static_url_prefix': '/statics/',
}

application = tornado.web.Application([
    (r'/index',IndexHandler)
],**settings)

if __name__ == '__main__':
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
app.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>上傳文件</title>
</head>
<body>
    <form action="/index" method="post" enctype="multipart/form-data">
        <input type="file" name = "filename">
        <input type="submit" value="提交">
    </form>
</body>
</html>
index.html

 二、ajax上傳文件

「假裝ajax」 iframe上傳

<iframe id="my_iframe"  name="my_iframe"  style="display: none" src=""></iframe>

    <form id="fo" method="POST" action="/upload/" enctype="multipart/form-data">
        <input type="text" id="user" name="user" />
        <input type="file" id="img" name="img" onchange="uploadFile3();" />
        <input type="submit" />
    </form>





##Js
function uploadFile3(){
            $('#container').find('img').remove();
            document.getElementById('my_iframe').onload = callback;
            document.getElementById('fo').target = 'my_iframe';
            document.getElementById('fo').submit();
        }
        function callback(){
            var text = $('#my_iframe').contents().find('body').text();
            var json_data = JSON.parse(text);
            console.log(json_data);
            if(json_data.status){
                // 已經上傳成功
                // 預覽,建立image標籤,src指向剛上傳的靜態文件路徑
                var tag = document.createElement('img');
                tag.src = "/" + json_data.data;
                tag.className = 'img';
                $('#container').append(tag);
            }else{
                alert(json_data.error);
            }
        }
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <input type="file" id="img" />
    <input type="button" onclick="UploadFile();" value="提交"/>

    <script src="/statics/jquery-1.12.4.js"></script>
    <script>
        function UploadFile(){
            var fileObj = $("#img")[0].files[0];
            var form = new FormData();
            form.append("filename", fileObj);

            $.ajax({
                type:'POST',
                url: '/index',
                data: form,
                processData: false,  // tell jQuery not to process the data
                contentType: false,  // tell jQuery not to set contentType
                success: function(arg){
                    console.log(arg);
                }
            })
        }
    </script>
</body>
</html>
jQuery上傳
<input type="file" id="img" name="img" onchange="uploadFile3();" />
<a onclick="uploadFile1();" >XMLHttpRequet上傳</a>


 function uploadFile1(){
            // 建立表單對象
            var form = new FormData();
            // 在表單對象中添加:user: 用戶輸入的用戶名
            form.append('user',document.getElementById('user').value);
            // 在表單對象中添加:img: 文件對象
            var fileObj = document.getElementById("img").files[0];
            form.append("img", fileObj);

            var xhr = new XMLHttpRequest();
            // 回調函數,當Ajax請求狀態變化時,自動觸發
            xhr.onreadystatechange = function(){
                // xhr.readyState=4 表示,客戶端已經將服務器端響應的內容所有獲取完畢
                if(xhr.readyState == 4){
                    //  xhr.responseText 獲取服務器端響應的文本內容,即: views中 return HttpResponse中的內容
                    var data = xhr.responseText;
                    console.log(data);
                }
            };
            // 建立異步鏈接
            xhr.open("post", '/upload/', true);
            // 發送請求,將form中的數據發送到服務器端
            xhr.send(form);
        }
XML上傳

tornado 生成隨機驗證碼

 用python生成隨機驗證碼須要借鑑一個插件,和一個io模塊,實現起來也很是容易,固然也須要借鑑session來判斷驗證碼是否錯誤,下面寫一段用戶登陸驗證帶驗證碼的,再看下效果,插件必須和執行文件必須放在更目錄下

import io
import datetime
import json
from backend.utils import check_code
from forms import account
from backend.utils.response import BaseResponse
from backend import commons
from models import chouti_orm as ORM
from sqlalchemy import and_, or_

create_session_id = lambda: sha1(bytes('%s%s' % (os.urandom(16), time.time()), encoding='utf-8')).hexdigest()


class SessionFactory:

    @staticmethod
    def get_session_obj(handler):
        obj = None

        if config.SESSION_TYPE == "cache":
            obj = CacheSession(handler)
        elif config.SESSION_TYPE == "memcached":
            obj = MemcachedSession(handler)
        elif config.SESSION_TYPE == "redis":
            obj = RedisSession(handler)
        return obj


class CacheSession:
    session_container = {}
    session_id = "__sessionId__"

    def __init__(self, handler):
        self.handler = handler
        client_random_str = handler.get_cookie(CacheSession.session_id, None)
        if client_random_str and client_random_str in CacheSession.session_container:
            self.random_str = client_random_str
        else:
            self.random_str = create_session_id()
            CacheSession.session_container[self.random_str] = {}

        expires_time = time.time() + config.SESSION_EXPIRES
        handler.set_cookie(CacheSession.session_id, self.random_str, expires=expires_time)

    def __getitem__(self, key):
        ret = CacheSession.session_container[self.random_str].get(key, None)
        return ret

    def __setitem__(self, key, value):
        CacheSession.session_container[self.random_str][key] = value

    def __delitem__(self, key):
        if key in CacheSession.session_container[self.random_str]:
            del CacheSession.session_container[self.random_str][key]

class BaseRequestHandler(tornado.web.RequestHandler):

    def initialize(self):

        self.session = SessionFactory.get_session_obj(self)


class CheckCodeHandler(BaseRequestHandler):
    def get(self, *args, **kwargs):
        stream = io.BytesIO()
        img, code = check_code.create_validate_code()
        img.save(stream, "png")
        self.session["CheckCode"] = code
        self.write(stream.getvalue())
CheckCode
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>驗證碼</title>
</head>
<body>
    <form action="/login" method="post">
        <p>用戶名: <input type="text" name="username"> </p>
        <p>密碼: <input type="password" name="password"> </p>
        <p>驗證碼: <input type="text" name="code"><img src="/check_code" onclick="ChangeCode();" id = "checkcode"></p>
        <input type="submit" value="submit"> <span>{{state}}</span>
    </form>
<script type="text/javascript">  //當點擊圖片的時候,會刷新圖片,這一段代碼就能夠實現
    function ChangeCode() {
        var code = document.getElementById('checkcode');
        code.src += "?";
    }
</script>
</body>
</html>
login.html

插件下載地址:猛擊這裏

相關文章
相關標籤/搜索