一、session
1.Session本质是保存在服务器端的数据,可以看作是键值对。
用户第一次打开网站页面
- 生成一段随机字符串,作为value发给客户端浏览器,客户端带着字符串获取对应的session
- 在session中保存,随机字符串作为key,value={'user':'Mitsui','pwd':123....}
2.session模块代码:
import time
import hashlib
import settings
def get_random_str():
"""
获取作为session key的随机字符串
:return:
"""
md5 = hashlib.md5()
md5.update(str(time.time()).encode('utf-8'))
return md5.hexdigest()
class RedisSession(object):
def __init__(self,handler):
"""
基于Redis在服务端存储session
:param handler:
"""
self.handler = handler
self.session_id = settings.SESSION_ID
self.expires = settings.EXPIRERS
self.initial()
@property
def conn(self):
import redis
conn = redis.Redis(host='192.168.xx.xx',port=6379)
return conn
def initial(self):
client_random_str = self.handler.get_cookie(self.session_id)
if client_random_str and self.conn.exists(client_random_str):
self.random_str = client_random_str
else:
self.random_str = get_random_str()
expires = time.time() + self.expires
self.handler.set_cookie(self.session_id,self.random_str,expires=expires)
#除了对浏览器cookies设置超时时间,也需要对redis数据设置超时时间,可以定时清除数据节省空间
self.conn.expire(self.random_str,self.expires)
def __getitem__(self, item):
"""
取session,item为key,如self.session['user']
:param item:
:return:
"""
import json
#由于Python数据类型直接存入redis后受到转换,因此在存储时会先dumps,相应的取值时会先loads。
data_str = self.conn.hget(self.random_str,item)
if data_str:
return json.loads(data_str)
else:
return None
def __setitem__(self, key, value):
"""
设置session
:param key:
:param value:
:return:
"""
import json
self.conn.hset(self.random_str,key,json.dumps(value))
def __delitem__(self, key):
"""
删除session
:param key:
:return:
"""
self.conn.hdel(self.random_str)
class CacheSession(object):
container = {}
def __init__(self,handler):
"""
普通的内存存取session
:param handler: 视图函数传进的self,可以使用get_cookie等方法
"""
self.handler = handler
self.session_id = settings.SESSION_ID #存储在浏览器的cookies的key
self.expires = settings.EXPIRERS #超时时间
self.initial()
def initial(self):
"""
:return:
"""
client_random_str = self.handler.get_cookie(self.session_id)
if client_random_str and client_random_str in self.container:
#如果session中已经有值,赋原值刷新超时时间
self.random_str = client_random_str
else:
#没有则获取随机字符串 作为session的key存储在内存中container,并设置超时时间,
self.random_str = get_random_str()
self.container[self.random_str] = {}
expires = time.time() + self.expires
self.handler.set_cookie(self.session_id,self.random_str,expires=expires)
def __getitem__(self, item):
"""
获取session
:param item:key, session['user']
:return:
"""
return self.container[self.random_str].get(item)
def __setitem__(self, key, value):
"""
设置session
:param key: session['user']
:param value: =user
:return:
"""
self.container[self.random_str][key] = value
def __delitem__(self, key):
"""
删除session del self.session时触发
:param key:
:return:
"""
if key in self.container[self.random_str]:
del self.container[self.random_str][key]
class SessionFactory(object):
"""
读取配置文件,根据配置文件返回定制的session类
"""
@staticmethod
def get_session():
import settings
import importlib
engine = settings.SESSION_ENGINE
module_path,cls_name = engine.rsplit('.',maxsplit=1)
md = importlib.import_module(module_path)
cls = getattr(md,cls_name)
return cls
3.基于Tornado的使用:
import tornado.ioloop
import tornado.web
from tornado.web import RequestHandler
from session_code import SessionFactory
class SessionHandler(object):
def initialize(self,*args,**kwargs):
cls = SessionFactory.get_session()
# cls是CacheSession对象,RedisSession对象
#执行cls的init方法
self.session = cls(self)
class LoginHandler(SessionHandler,RequestHandler):
"""
init方法执行之后,执行get 跟post方法之前,会先执行initialize方法,
这里用多继承的方法继承自SessionHandler,执行initialize方法,获取
CacheSession或者RedisSession对象的session方法
"""
def get(self, *args, **kwargs):
self.render("login.html")
def post(self, *args, **kwargs):
user = self.get_argument('user')
pwd = self.get_argument('pwd')
if user == "Mitsui" and pwd == '123':
self.session['user'] = user
self.redirect('/index')
else:
self.render('login.html')
class IndexHandler(SessionHandler,RequestHandler):
def get(self, *args, **kwargs):
user = self.session['user']
if user:
self.write('欢迎登录!')
else:
self.redirect('/login')
sett = {
'template_path':'views',
#XSRF
'xsrf_cookies':True,
}
application = tornado.web.Application([
(r"/login",LoginHandler),
(r"/index",IndexHandler),
],**sett)
if __name__ == '__main__':
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
配置文件settings:
#所选的session处理类 SESSION_ENGINE = "session_code.CacheSession" #客户端cookie中保存的key SESSION_ID = "__session__id__" #超时时间 EXPIRERS = 300
二、自定制Form组件:
1.Form组件核心原理:
import re
import copy
# ##################### 定制插件(HTMl) #####################
class TextInput(object):
"""
定制前端页面的标签:
:return: <input type='text' class="c1" ID='I1' ..../>"
"""
def __init__(self,attrs=None):
"""
标签自定制属性功能
:param attrs: {'class':'c1', .....}
"""
if attrs:
self.attrs = attrs
else:
self.attrs = {}
def __str__(self):
data_list = []
for k,v in self.attrs.items():
tmp = "{0}='{1}'".format(k,v)
data_list.append(tmp)
tpl = "<input type='text' {0}>".format(" ".join(data_list))
return tpl
class EmailInput(object):
def __init__(self, attrs=None):
if attrs:
self.attrs = attrs
else:
self.attrs = {}
def __str__(self):
data_list = []
for k, v in self.attrs.items():
tmp = "{0}='{1}'".format(k, v)
data_list.append(tmp)
tpl = "<input type='email' {0} />".format(" ".join(data_list))
return tpl
class PasswordInput(object):
def __init__(self, attrs=None):
if attrs:
self.attrs = attrs
else:
self.attrs = {}
def __str__(self):
data_list = []
for k, v in self.attrs.items():
tmp = "{0}='{1}'".format(k, v)
data_list.append(tmp)
tpl = "<input type='password' {0} />".format(" ".join(data_list))
return tpl
# ##################### 定制字段(正则) #####################
class Field(object):
def __str__(self):
"""
保存用户输入的值,当用户调用过is_valid,则self.value有值,
在插件中增加属性 value = 用户提交过来的值
:return: 插件的str值,验证过则新增value = 输入值的属性
"""
if self.value:
self.widget.attrs['value'] = self.value
return str(self.widget)
class CharField(Field):
default_widget = TextInput
regex = "w+"
def __init__(self,widget=None):
"""
初始化的时候,设置对应的插件,如果传入widget,则使用widget传入的插件对象,
如果未传入则使用默认的插件对象。
:param widget: 插件对象,TextInput()、EmailInput()....
"""
self.value = None
self.widget = widget if widget else self.default_widget()
def valid_field(self,value):
self.value = value
if re.match(self.regex,value):
return True
else:
return False
class EmailField(Field):
default_widget = EmailInput
regex = "w+@w+"
def __init__(self,widget=None):
self.value = None
self.widget = widget if widget else self.default_widget()
def valid_field(self,value):
self.value = value
if re.match(self.regex,value):
return True
else:
return False
# ##################### 定制Form #####################
class BaseForm(object):
def __init__(self,data):
"""
获取在类中生成的所有插件,设置到对象中,并添加到self.fields字典中,
供is_valid方法对所有注册的插件进行数据验证。
:param data:
"""
self.fields = {}
self.data = data #用户form表单提交值 {"user":'Mitsui','email':'Mitsui@live.com'}
#需要使用Form表单时,会继承BaseForm类,实例化生成对象时,self即需要在前端展示的form对象
#通过type(self)找到Form类,Form类__dict__中包含所有的类的静态字段,即使用form时创建的插件,
#user = CharField() 插件都是继承自Field类,由此获取所有的插件
for name,field in type(self).__dict__.items():
#name:user, field:CharField()
if isinstance(field,Field):
#由于是静态字段,所以使用的是同一个对象,如果对其进行修改,会影响其它的form对象,
#所以这里通过深拷贝防止对其进行修改
new_field = copy.deepcopy(field)
#将类的这些静态字段设置到对象中,方便调用
setattr(self,name,new_field)
self.fields[name] = new_field
def is_valid(self):
"""
将form组件设置的所有字段循环,交给每一个Field字段验证,如果有一个错误
返回False,否则返回True
:return:
"""
flag = True
for name,field in self.fields.items():
# name:user, field:CharField()
user_input_val = self.data.get(name)
result = field.valid_field(user_input_val)
if not result:
flag = False
return flag
# ##################### 使用Form #####################
class LoginForm(BaseForm):
user = CharField()
email = EmailField(widget=EmailInput())
#Django:
# if request == "GET":
# form = LoginForm()
# return render('login.html',{'form':form})
# else:
# form = LoginForm(request.POST)
# if form.is_valid():
# pass
# else:
# pass
# Tornado:
# def get(self, *args, **kwargs):
# form = LoginForm()
# self.render("login.html",form=form)
#
# def post(self, *args, **kwargs):
# post_data = {}
# for key in self.request.arguments:
# if key == '_xsrf': continue
# post_data[key] = self.get_arguments(key)[0]
# form = LoginForm(post_data)
# if form.is_valid():
# pass
# else:
# self.render('login.html',form=form)
2.支持多个WEB框架的Form表单验证 - Tyrion