一. 自定义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()
通过以上的实例我们再来看下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
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
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
下面是具体的使用方法:
使用文档
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': '邮箱格式错误'})
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>
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)
"""
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] """
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': '羽毛球'}, ]))
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)
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())
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, ]))
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]))
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': '玻璃球'}]
三.自定义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]
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)
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)
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()
<!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>
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>
<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); }
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())
<!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>
插件下载地址:猛击这里