python web框架——扩展Django&tornado

一 Django自定义分页

   目的:自定义分页功能,并把它写成模块(注意其中涉及到的python基础知识)

    models.py文件

# Create your models here.
class UserList(models.Model):
    username = models.CharField(max_length=32)
    age = models.IntegerField()

    分页模块page.py文件

#!/usr/bin/env python
#-*- coding:utf-8 -*-
__author__ = 'mcp'

# 导入后台标记字符串安全的模块
from django.utils.safestring import mark_safe

# 自定义的分页类
class Pager(object):
    def __init__(self, current_page):
        self.current_page = int(current_page)
    # 让方法伪装成属性,加装饰器
    @property
    def start(self):
        return (self.current_page-1)*10
    @property
    def end(self):
        return self.current_page*10

    def page_str(self,all_item,base_url):
        all_page,div = divmod(all_item,10)
        if div>0:
            all_page += 1

        pager_list = []

        if all_page <= 11:
            start = 1
            end = all_page
        else:
            if self.current_page < 6:
                start = 1
                end =11 + 1
              else:
                start = self.current_page - 5
                end = self.current_page + 6
                  if self.current_page + 6 > all_page:
                    start = all_page - 11
                    end = all_page + 1

        for i in range(start,end):
            # 注意单双引号的使用,/user_list/?page=
            if i == self.current_page:
                tmp = '<a style="color:red;font-size:20px;"href="%s%d">%d</a>' %(base_url,i,i,)
           else:
                tmp = '<a href="%s%d">%d</a>' %(base_url,i,i,)
           pager_list.append(tmp)

        # 上一页
        if self.current_page > 1:
            pre_page = '<a href="%s%d">上一页</a>' %(base_url,self.current_page - 1)
       else:
            pre_page = '<a href="javascript:void(0);">上一页</a>'
         # 下一页
        if self.current_page < all_page:
            next_page = '<a href="%s%d">下一页</a>' %(base_url,self.current_page + 1)
       else:
            next_page = '<a href="javascript:void(0);">下一页</a>'
        
         #在最前面插入上一页按钮
        pager_list.insert(0,pre_page)
        #在最后面插入下一页按钮
        pager_list.append(next_page)

        # return ''.join(pager_list)
         # 直接在后台标记字符串是安全的
        return  mark_safe(''.join(pager_list))

    views.py文件

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

from django.shortcuts import render
from django.shortcuts import HttpResponse
from app01 import models
# 导入自定义分页模块
from app01 import page # Create your views here. def user_list(request): # 先创建100条表数据 # for item in range(101,500): # dic = {'username':'name%d' %item, 'age':item} # models.UserList.objects.create(**dic) # 再打印数据库中有多少条数据 # print models.UserList.objects.all().count() # 获取第0-9条数据,分片 # print type(models.UserList.objects.all()) #<class 'django.db.models.query.QuerySet'> # result = models.UserList.objects.all()[0:10] current_page = request.GET.get('page',1) # 注意current_page为字符串类型,要转换成整型 # current_page = int(current_page) # start = (current_page-1)*10 # end = current_page*10 # result = models.UserList.objects.all()[start:end] pager_obj = page.Pager(current_page) # result = models.UserList.objects.all()[pager_obj.start():pager_obj.end()] # 下面这句是从Django ORM中取数据,相当于在数据库中执行如下sql语句 # SELECT "app01_userlist"."id", "app01_userlist"."username", "app01_userlist"."age"
# FROM "app01_userlist" LIMIT 10 OFFSET 490
result = models.UserList.objects.all()[pager_obj.start:pager_obj.end] # 打印Django ORM中在底层实际执行的sql语句 print result.query # 每页显示10条数据 # 共100条 all_item = models.UserList.objects.all().count() # 把下面的代码封装到函数中了 pager_str = pager_obj.page_str(all_item,"/user_list/?page=") # all_page,div = divmod(all_item,10) # if div>0: # all_page += 1 # # pager_str = "" # for i in range(1,all_page+1): # # 注意单双引号的使用 # tmp = '<a href="/user_list/?page=%d">%d</a>' %(i,i,) # pager_str += tmp return render(request,'user_list.html',{'result':result,'pager_str':pager_str})

    user_list.html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <style>
        a{
            padding: 5px;
        }
    </style>
</head>
<body>
    <h1>测试</h1>
    <table>
        <tbody>
           {% for line in result %}
           <tr>
              <td>{{ line.username }}</td>
              <td>{{ line.age }}</td>
           </tr>
           {% endfor %}
        </tbody>
    </table>
{#    <div>#}
{#        <a href="/user_list/?page=1">1</a>#}
{#        <a href="/user_list/?page=2">2</a>#}
{#        <a href="/user_list/?page=3">3</a>#}
{#        <a href="/user_list/?page=4">4</a>#}
{#        <a>5</a>#}
{#        <a>6</a>#}
{#        <a>7</a>#}
{#        <a>8</a>#}
{#        <a>9</a>#}
{#        <a>10</a>#}
{#    </div>#}
    <div>
        {#在前端标记后台传的字符串是安全的#}
        {#{{ pager_str | safe }}#}
        {{ pager_str }}
    </div>
</body>
</html>

    自定义分页模块涉及到的知识以及注意点:

    (1)Django ORM

    (2)python中类、属性、div方法以及封装特性

    (3)XSS攻击(在前端或后端标记字符串是安全的)

    (4)python相关基础知识以及装饰器

二 自定义Tornado的Session框架

   1 知识回顾

   Tornado中没有Session部分,这需要开发者去自己扩展Tornado的Session框架。在自定义Session框架之前,先来回顾下Django中Session和Cookie的知识:

    用户登录过程中的Session和Cookie:
        1、在服务端随机生成一串字符;
        2、将该字符串写到浏览器端,同时将该字符串存储在服务端,存储形式为字典,其中该字符串为key(服务端Session为字典形式);
        3、在服务端Session中设置相应值,比如是否已经登录。

操作Session:
    获取Session:request.session[key]
    设置Session:reqeust.session[key] = value
    删除Session:del request[key]
    
request.session.set_expiry(value)
* 如果value为整数,则Session会在value秒后失效;
* 如果value为datatime或timedelta,则Session会在这个时间后失效;
* 如果value为0,则用户关闭浏览器后Session就会失效;
* 如果value为None,则Session会依赖全局Session失效策略。

    2 自定义Session

     (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
  
# 创建obj对象
obj = Foo()
# 获取key为'k1'的元素,这时会调用__getitem__方法
result = obj['k1']
# 给key为'k2'的元素赋值,这时会调用__setitem__方法
#obj['k2'] = 'wupeiqi'
# 删除key为'k3'的元素,这时会调用__delitem__方法
#del obj['k3'] 

    (2)Session框架代码

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

import tornado.ioloop
import tornado.web
from hashlib import sha1
import os, time

session_container = {}

create_session_id = lambda: sha1('%s%s' % (os.urandom(16), time.time())).hexdigest()

class Session(object):

    session_id = "__sessionId__"

    def __init__(self, request):
        #当请求过来时,先利用get_cookie方法获取cookie值,目的是为了确定是否存在Cookie,如果有则不生成,否则生成。
        session_value = request.get_cookie(Session.session_id)
        #如果Cookie不存在,则生成Cookie(创建随机字符串)
        if not session_value:
            self._id = create_session_id()
        else:
            #如果Cookie存在,则直接将获取的Cookie值赋值给_id字段
            self._id = session_value
        #设置Session值
        request.set_cookie(Session.session_id, self._id)
    
    #特殊方法:获取值时调用的方法 
    def __getitem__(self, key):
        ret = None
        try:
            #获取Session值方法
            ret =  session_container[self._id][key]
        except Exception,e:
            pass
        return ret

    #特殊方法:设置值时调用的方法 
    def __setitem__(self, key, value):
        #判断是否存在Cookie值,如果存在设置相应的值
        if session_container.has_key(self._id):
            session_container[self._id][key] = value
        #如果不存在,则生成相应的字典数据,类似:{'Cookie值':{'IS_LOGIN':'True'}}
        else: 
            session_container[self._id] = {key: value}
    #特殊方法:删除值时调用的方法 
    def __delitem__(self, key):
        del session_container[self._id][key]

class BaseHandler(tornado.web.RequestHandler):
    #扩展了Tornado的initialize方法,这个方法默认为空方法
    def initialize(self):
        '''
        这里initialize的self指向谁?
        obj = LoginHandler()
        obj.initialize() ==>LoginHandler类中没有initialize这个方法,该方法继承自它的父类
        所以initialize的self就是LoginHandler对象
        '''
        
        #执行Session类的构造方法,同时把LoginHandler对象传参给Session类的构造方法
        self.my_session = Session(self) 

class MainHandler(BaseHandler):

    def get(self):
        ret = self.my_session['is_login']
        if ret:
            self.write('index')
        else:
            self.redirect("/login")

class LoginHandler(BaseHandler):
    def get(self):
        '''
        当用户浏览器访问服务端时,就应该给浏览器写入cookie了,但是这里没有写?
        那在哪里写入cookie呢?上面定义的相关Handler类都是继承自RequestHandler,这次继承的是自定义的BaseHandler类,
        继承自定义的类,然后在类中扩展了initialize方法,获取用户cookie或者写cookie都可以在此方法中操作
        '''
        # self.set_cookie()
        # self.get_cookie()

        self.render('login.html', **{'status': ''})

    def post(self, *args, **kwargs):
        #获取用户提交的用户名和密码
        username = self.get_argument('username')
        password = self.get_argument('pwd')
        if username == 'wupeiqi' and password == '123':
            #如果认证通过,就可以访问这个self.my_session对象,然后就可以把Cookie写入字典中。
            self.my_session['is_login'] = 'true'
            self.redirect('/index')
        else:
            self.render('login.html', **{'status': '用户名或密码错误'})

settings = {
    'template_path': 'template',
    'static_path': 'static',
    'static_url_prefix': '/static/',
    'cookie_secret': 'abcdefgh',
    'login_url': '/login'
}

application = tornado.web.Application([
    #创建两个URL 分别对应MainHandler和LoginHandler
    (r"/index", MainHandler),
    (r"/login", LoginHandler),
], **settings)


if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

    (3)分布式Session框架

     上面使用Tornado预留扩展方法initialize和Class类实现了自定义的Session框架,下面探讨如何实现分布式Session框架。上面自定义的Session框架中,Session默认保存在session_container字典中。实际生产环境中,Session也可以存储在Redis,memcache,mysql中,如果把session存储在单台服务器上,那么存在着数据不安全或数据量大时满足不了性能要求。这时可以考虑分布式(假设有3台服务器存储Session),那么可以分别存储在A、B、C上:

    当用户第1次访问时,通过权重和hash计算把session加入到hash环中,实现分布式存储;当用户第2次携带Cookie访问时,通过计算找到相应的Session值并进行认证。

  一致性哈希

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

import sys
import math
from bisect import bisect


if sys.version_info >= (2, 5):
    import hashlib
    md5_constructor = hashlib.md5
else:
    import md5
    md5_constructor = md5.new


class HashRing(object):
    #一致性哈希 
    def __init__(self,nodes):
        '''
        初始化: 包含存储节点以及节点对应的权重
                默认每1个节点有32个虚拟节点
                对于权重,通过多创建虚拟节点来实现
                如:nodes = [
                        {'host':'127.0.0.1:8000','weight':1},
                        {'host':'127.0.0.1:8001','weight':2},
                        {'host':'127.0.0.1:8002','weight':1},
                    ]
        '''
        
        self.ring = dict()
        self._sorted_keys = []

        self.total_weight = 0
        
        self.__generate_circle(nodes)
          
    def __generate_circle(self,nodes):
        for node_info in nodes:
            self.total_weight += node_info.get('weight',1)
            
        for node_info in nodes:
            weight = node_info.get('weight',1)
            node = node_info.get('host',None)
                
            virtual_node_count = math.floor((32*len(nodes)*weight) / self.total_weight)
            for i in xrange(0,int(virtual_node_count)):
                key = self.gen_key_thirty_two( '%s-%s' % (node, i) )
                if self._sorted_keys.__contains__(key):
                    raise Exception('该节点已经存在.')
                self.ring[key] = node
                self._sorted_keys.append(key)
            
    def add_node(self,node):
        ''' 
        新建节点node: 新添加节点格式为:{'host':'127.0.0.1:8002','weight':1},其中第1个元素表示节点,
第2个元素表示该节点的权重。
''' node = node.get('host',None) if not node: raise Exception('节点的地址不能为空.') weight = node.get('weight',1) self.total_weight += weight nodes_count = len(self._sorted_keys) + 1 virtual_node_count = math.floor((32 * nodes_count * weight) / self.total_weight) for i in xrange(0,int(virtual_node_count)): key = self.gen_key_thirty_two( '%s-%s' % (node, i) ) if self._sorted_keys.__contains__(key): raise Exception('该节点已经存在.') self.ring[key] = node self._sorted_keys.append(key) def remove_node(self,node): ''' 移除节点node: 要移除的节点'127.0.0.1:8000' ''' for key,value in self.ring.items(): if value == node: del self.ring[key] self._sorted_keys.remove(key) def get_node(self,string_key): ''' 获取string_key所在的节点 ''' pos = self.get_node_pos(string_key) if pos is None: return None return self.ring[ self._sorted_keys[pos]].split(':') def get_node_pos(self,string_key): ''' 获取string_key所在的节点的索引 ''' if not self.ring: return None key = self.gen_key_thirty_two(string_key) nodes = self._sorted_keys pos = bisect(nodes, key) return pos def gen_key_thirty_two(self, key): m = md5_constructor() m.update(key) return long(m.hexdigest(), 16) def gen_key_sixteen(self,key): b_key = self.__hash_digest(key) return self.__hash_val(b_key, lambda x: x) def __hash_val(self, b_key, entry_fn): return (( b_key[entry_fn(3)] << 24)|(b_key[entry_fn(2)] << 16)|(b_key[entry_fn(1)] << 8)| b_key[entry_fn(0)] ) def __hash_digest(self, key): m = md5_constructor() m.update(key) return map(ord, m.digest()) """ nodes = [ {'host':'127.0.0.1:8000','weight':1}, {'host':'127.0.0.1:8001','weight':2}, {'host':'127.0.0.1:8002','weight':1}, ] ring = HashRing(nodes) result = ring.get_node('98708798709870987098709879087') print result """

    分布式Session框架代码:

from hashlib import sha1
import os, time

create_session_id = lambda: sha1('%s%s' % (os.urandom(16), time.time())).hexdigest()

class Session(object):

    session_id = "__sessionId__"

    def __init__(self, request):
        session_value = request.get_cookie(Session.session_id)
        if not session_value:
            self._id = create_session_id()
        else:
            self._id = session_value
        request.set_cookie(Session.session_id, self._id)

    def __getitem__(self, key):
        # 根据 self._id ,在一致性哈西中找到其对应的服务器IP
        # 找到相对应的redis服务器,如: r = redis.StrictRedis(host='localhost', port=6379, db=0)
        # 使用python redis api 链接
        # 获取数据,即:
        # return self._redis.hget(self._id, name)

    def __setitem__(self, key, value):
        # 根据 self._id ,在一致性哈西中找到其对应的服务器IP
        # 使用python redis api 链接
        # 设置session
        # self._redis.hset(self._id, name, value)

    def __delitem__(self, key):
        # 根据 self._id 找到相对应的redis服务器
        # 使用python redis api 链接
        # 删除,即:
        return self._redis.hdel(self._id, name)

自定义Tornado的Form框架

   python web框架中,只有Django提供了form框架。在Tornado等其他python web框架中,需要自定义form框架。Django中的form验证流程:
       用户输入 ==》request.post ==> obj = Form(request.POST) ==> obj.is_valid()
   存在问题:request在获取用户输入时并不知道用户输入的个数,Form表单内可能有1个也有可能有多个输入,这是Django中Form框架存在的问题。

   Form(模型绑定)有两个主要功能:
    (1)自动生成html表单
    (2)用户输入验证 

   1 简单的form框架

   index.html文件

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <link href="{{static_url("commons.css")}}" rel="stylesheet" />
</head>
<body>
    <h1>hello</h1>
    <form action="/index" method="post">

        <p>hostname: <input type="text" name="host" /> </p>
        <p>ip: <input type="text" name="ip" /> </p>
        <p>port: <input type="text" name="port" /> </p>
        <p>phone: <input type="text" name="phone" /> </p>
        <input type="submit" />
    </form>
</body>
</html>

   index.py文件

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

import tornado.ioloop
import tornado.web
from hashlib import sha1
import os, time
import re

#创建form类
class MainForm(object):
    #初始化
    def __init__(self):
        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):
        #循环当前类中的成员,注意此种方法
        for key, regular in self.__dict__.items():
            '''
            通过request.get_argument()来获取用户前端输入的值
            在循环时,不需要关心前端输入值的个数,这里以自定义方法为主
            '''
            post_value = request.get_argument(key)
            #前端提交的数据与自定义的正则表达式进行匹配验证
            ret = re.match(regular, post_value)
            print key, ret, post_value

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render('index.html')
    def post(self, *args, **kwargs):
        obj = MainForm()
        result = obj.check_valid(self)
        self.write('ok')
        
settings = {
    'template_path': 'template',
    'static_path': 'static',
    'static_url_prefix': '/static/',
    'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh',
    'login_url': '/login'
}

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

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

   2  自定义form框架优化版

     (1)验证字段写成相应的类;

     (2)类的继承

     (3)ListForm

  自定义Form完整代码

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

import tornado.ioloop
import tornado.web
import re

class Field(object):

    def __init__(self, error_msg_dict, required):
        self.id_valid = False
        self.value = None
        self.error = None
        self.name = None
        self.error_msg = error_msg_dict
        self.required = required

    def match(self, name, value):
        self.name = name

        if not self.required:
            self.id_valid = True
            self.value = value
        else:
            if not value:
                if self.error_msg.get('required', None):
                    self.error = self.error_msg['required']
                else:
                    self.error = "%s is required" % name
            else:
                ret = re.match(self.REGULAR, value)
                if ret:
                    self.id_valid = True
                    self.value = ret.group()
                else:
                    if self.error_msg.get('valid', None):
                        self.error = self.error_msg['valid']
                    else:
                        self.error = "%s is invalid" % name

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}$"

    def __init__(self, error_msg_dict=None, required=True):

        error_msg = {}  # {'required': 'IP不能为空', 'valid': 'IP格式错误'}
        if error_msg_dict:
            error_msg.update(error_msg_dict)

        super(IPField, self).__init__(error_msg_dict=error_msg, required=required)

class IntegerField(Field):
    REGULAR = "^d+$"

    def __init__(self, error_msg_dict=None, required=True):
        error_msg = {'required': '数字不能为空', 'valid': '数字格式错误'}
        if error_msg_dict:
            error_msg.update(error_msg_dict)

        super(IntegerField, self).__init__(error_msg_dict=error_msg, required=required)

class CheckBoxField(Field):

    def __init__(self, error_msg_dict=None, required=True):
        error_msg = {}  # {'required': 'IP不能为空', 'valid': 'IP格式错误'}
        if error_msg_dict:
            error_msg.update(error_msg_dict)

        super(CheckBoxField, self).__init__(error_msg_dict=error_msg, required=required)

    def match(self, name, value):
        self.name = name

        if not self.required:
            self.id_valid = True
            self.value = value
        else:
            if not value:
                if self.error_msg.get('required', None):
                    self.error = self.error_msg['required']
                else:
                    self.error = "%s is required" % name
            else:
                if isinstance(name, list):
                    self.id_valid = True
                    self.value = value
                else:
                    if self.error_msg.get('valid', None):
                        self.error = self.error_msg['valid']
                    else:
                        self.error = "%s is invalid" % name

class FileField(Field):
    REGULAR = "^(w+.pdf)|(w+.mp3)|(w+.py)$"

    def __init__(self, error_msg_dict=None, required=True):
        error_msg = {}  # {'required': '数字不能为空', 'valid': '数字格式错误'}
        if error_msg_dict:
            error_msg.update(error_msg_dict)

        super(FileField, self).__init__(error_msg_dict=error_msg, required=required)

    def match(self, name, value):
        self.name = name
        self.value = []
        if not self.required:
            self.id_valid = True
            self.value = value
        else:
            if not value:
                if self.error_msg.get('required', None):
                    self.error = self.error_msg['required']
                else:
                    self.error = "%s is required" % name
            else:
                m = re.compile(self.REGULAR)
                if isinstance(value, list):
                    for file_name in value:
                        r = m.match(file_name)
                        if r:
                            self.value.append(r.group())
                            self.id_valid = True
                        else:
                            self.id_valid = False
                            if self.error_msg.get('valid', None):
                                self.error = self.error_msg['valid']
                            else:
                                self.error = "%s is invalid" % name
                            break
                else:
                    if self.error_msg.get('valid', None):
                        self.error = self.error_msg['valid']
                    else:
                        self.error = "%s is invalid" % name

    def save(self, request, upload_path=""):

        file_metas = request.files[self.name]
        for meta in file_metas:
            file_name = meta['filename']
            with open(file_name,'wb') as up:
                up.write(meta['body'])

class Form(object):

    def __init__(self):
        self.value_dict = {}
        self.error_dict = {}
        self.valid_status = True

    def validate(self, request, depth=10, pre_key=""):

        self.initialize()
        self.__valid(self, request, depth, pre_key)

    def initialize(self):
        pass

    def __valid(self, form_obj, request, depth, pre_key):
        """
        验证用户表单请求的数据
        :param form_obj: Form对象(Form派生类的对象)
        :param request: Http请求上下文(用于从请求中获取用户提交的值)
        :param depth: 对Form内容的深度的支持
        :param pre_key: Html中name属性值的前缀(多层Form时,内部递归时设置,无需理会)
        :return: 是否验证通过,True:验证成功;False:验证失败
        """

        depth -= 1
        if depth < 0:
            return None
        form_field_dict = form_obj.__dict__
        for key, field_obj in form_field_dict.items():
            print key,field_obj
            if isinstance(field_obj, Form) or isinstance(field_obj, Field):
                if isinstance(field_obj, Form):
                    # 获取以key开头的所有的值,以参数的形式传至
                    self.__valid(field_obj, request, depth, key)
                    continue
                if pre_key:
                    key = "%s.%s" % (pre_key, key)

                if isinstance(field_obj, CheckBoxField):
                    post_value = request.get_arguments(key, None)
                elif isinstance(field_obj, FileField):
                    post_value = []
                    file_list = request.request.files.get(key, None)
                    for file_item in file_list:
                        post_value.append(file_item['filename'])
                else:
                    post_value = request.get_argument(key, None)

                print post_value
                # 让提交的数据 和 定义的正则表达式进行匹配
                field_obj.match(key, post_value)
                if field_obj.id_valid:
                    self.value_dict[key] = field_obj.value
                else:
                    self.error_dict[key] = field_obj.error
                    self.valid_status = False

class ListForm(object):
    def __init__(self, form_type):
        self.form_type = form_type
        self.valid_status = True
        self.value_dict = {}
        self.error_dict = {}

    def validate(self, request):
        name_list = request.request.arguments.keys() + request.request.files.keys()
        index = 0
        flag = False
        while True:
            pre_key = "[%d]" % index
            for name in name_list:
                if name.startswith(pre_key):
                    flag = True
                    break
            if flag:
                form_obj = self.form_type()
                form_obj.validate(request, depth=10, pre_key="[%d]" % index)
                if form_obj.valid_status:
                    self.value_dict[index] = form_obj.value_dict
                else:
                    self.error_dict[index] = form_obj.error_dict
                    self.valid_status = False
            else:
                break

            index += 1
            flag = False

class MainForm(Form):

    def __init__(self):
        # self.ip = IPField(required=True)
        # self.port = IntegerField(required=True)
        # self.new_ip = IPField(required=True)
        # self.second = SecondForm()
        self.fff = FileField(required=True)
        super(MainForm, self).__init__()

#
# class SecondForm(Form):
#
#     def __init__(self):
#         self.ip = IPField(required=True)
#         self.new_ip = IPField(required=True)
#
#         super(SecondForm, self).__init__()

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render('index.html')
    def post(self, *args, **kwargs):
        # for i in  dir(self.request):
        #     print i
        # print self.request.arguments
        # print self.request.files
        # print self.request.query
        # name_list = self.request.arguments.keys() + self.request.files.keys()
        # print name_list

        # list_form = ListForm(MainForm)
        # list_form.validate(self)
        #
        # print list_form.valid_status
        # print list_form.value_dict
        # print list_form.error_dict

        # obj = MainForm()
        # obj.validate(self)
        #
        # print "验证结果:", obj.valid_status
        # print "符合验证结果:", obj.value_dict
        # print "错误信息:"
        # for key, item in obj.error_dict.items():
        #     print key,item
        # print self.get_arguments('favor'),type(self.get_arguments('favor'))
        # print self.get_argument('favor'),type(self.get_argument('favor'))
        # print type(self.get_argument('fff')),self.get_argument('fff')
        # print self.request.files
        # obj = MainForm()
        # obj.validate(self)
        # print obj.valid_status
        # print obj.value_dict
        # print obj.error_dict
        # print self.request,type(self.request)
        # obj.fff.save(self.request)
        # from tornado.httputil import HTTPServerRequest
        # name_list = self.request.arguments.keys() + self.request.files.keys()
        # print name_list
        # print self.request.files,type(self.request.files)
        # print len(self.request.files.get('fff'))

        # obj = MainForm()
        # obj.validate(self)
        # print obj.valid_status
        # print obj.value_dict
        # print obj.error_dict
        # obj.fff.save(self.request)
        self.write('ok')

settings = {
    'template_path': 'template',
    'static_path': 'static',
    'static_url_prefix': '/static/',
    'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh',
    'login_url': '/login'
}

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

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

参考资料:

       http://www.cnblogs.com/wupeiqi/articles/5341480.html

     http://www.cnblogs.com/luotianshuai/p/5482612.html

原文地址:https://www.cnblogs.com/maociping/p/5351340.html