Tornado 自定义Form,session实现方法

一. 自定义Tornado 验证模块

我们知道,平时在登陆某个网站或软件时,网站对于你输入的内容是有要求的,并且会对你输入的错误内容有提示,对于Django这种大而全的web框架,是提供了form表单验证功能,但是对于Tornado而言,就没有这功能,所以就需要我们来自己自定义form表单验证,而且这种方法正是Django里的form表单验证的实质内容,也帮我们在后面学习Django理解相关的源码。

写之前,我们必须知道form表单验证的实质是什么?

   实质就是正则匹配

  我们知道用户提交数据是通过post方式提交,所以我们重写post方法,并在post方法进行业务逻辑处理

  • 获取用户提交的数据
  • 将用户提交的数据和正则表达式匹配

写之前,我们必须知道form表单验证的实质是什么?

   实质就是正则匹配

  我们知道用户提交数据是通过post方式提交,所以我们重写post方法,并在post方法进行业务逻辑处理

  • 获取用户提交的数据
  • 将用户提交的数据和正则表达式匹配
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中的自定义字段根据不同的正则表达式拆分成跟自己属性相关的类,实例如下:

根据每个字段的特性,定义基础的字段,如字符串,邮件类型,整数类等

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类

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 是如何开发的

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

 下面是具体的使用方法:

使用文档

1、下载安装

pip install PyTyrion 

2、配置WEB框架种类

由于Tyrion同时支持Tornado、Django、Flask、Bottle多个WEB框架,所有在使用前需要进行指定。

import Tyrion
Tyrion.setup('tornado')
# setup的参数有:tornado(默认)、django、bottle、flask

3、创建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': '邮箱格式错误'})

4、验证用户请求

前端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:
            ###### 如果合法,则输出用户输入的内容 ######

5、验证用户请求 && 生成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

6、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

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

8、动态初始化默认值

由于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)

9、更多示例

示例源码下载:猛击这里

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是没有的

简单回顾下:

1、自动生成一段字符串

2、将字符串发送到客户端的浏览器,同时把字符串当做key放在session里。(可以理解为session就是一个字典)

3、在用户的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失效策略。

 2、上面是Django的原理是一样的,那么我们来自己写一个Session

2.1、储备知识点

#!/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.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。(可以叫做浏览器缓存)

1、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()

2、加密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上传,下面就来介绍一下这两类

1、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

 2、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

插件下载地址:猛击这里

原文地址:https://www.cnblogs.com/jasonwang-2016/p/6000071.html