【Python之路】特别篇--抽屉新热榜
登陆与注册
注册功能:
流程: 填写用户名,邮箱,获取邮箱验证码,填入密码 单击<下一步>按钮,完成注册!
1.获取邮箱验证码(具体步骤分析):
1.利用ajax 往后台传入邮箱,
2.后台表单验证,邮箱填写是否正确,
=> 成功,则继续3,
=> 错误,写入错误信息,页面输出错误提示!
3.查询sendcode 数据库表,查询该邮箱是否是第一次发送验证码
=> 第一次发送: 数据表中插入记录 (邮箱,发送次数1,邮箱验证码,状态1,有效时间:当前时间-1小时)
=> 否则: 从数据表中查询出该邮箱验证码发送次数,
=> 如果 >5 则,再判断上次发送时间+1小时 是否大于当前时间!
=> 大于当前时间,则发送验证码,修改数据表记录 (发送次数+1,邮箱验证码,有效时间)
=> 小于当前时间,则为多次恶意发送,不操作,写入错误信息,需要等待1小时,
=> 如果<5 则直接发送验证码,修改数据表记录 (发送次数+1,邮箱验证码,有效时间)
4.json信息返回。
import json
import datetime from core.request_handle import BaseHandler
# 公共方法
from backend import commons,message
# 数据库查询
from models.sendcode import Insert,QueryCounts,Update
from models.userinfo import InsertUser
# 表单验证
from forms.account import emailForm,registerForm class SendEmailCodeHandler(BaseHandler):
def post(self):
obj = emailForm()
val, success_info, error_info = obj.check_value(self)
if not error_info:
email = [success_info['email']]
now = datetime.datetime.now()
que = QueryCounts()
ret = que.checkstime(email)
if ret:
if ret['stime'] < 5:
UpdateCode(email)
else:
if (ret['ctime'] + datetime.timedelta(hours=1)) > now :
error_info['stime'] = '验证码发送次数过多,请1小时后再发送'
else:
UpdateCode(email)
else:
InsertCode(email)
data = {'success_info': success_info, 'error_info': error_info}
self.write(json.dumps(data)) # 第一次发送code 数据表添加
def InsertCode(email):
code = commons.random_code() # 生成4位随机验证码(字母+数字)
message.send_email(email, code)
ins = Insert()
ins.insertcode(email, code)
print(email, code) # 多次发送, 数据表修改
def UpdateCode(email):
code = commons.random_code()
message.send_email(email, code)
update = Update()
update.updatestime(email, code)
print(email, code)
SendEmailCodeHandler
import random
import string def random_code():
code = ''.join(random.sample(string.ascii_letters + string.digits, 4))
return code
commons.py 公共方法:生成4位验证码
//注册:生成邮箱验证码:
$('#email-code').on('click',function () {
var email = $('#re-email').val();
$.ajax({
url:'/email-code',
type: 'POST',
dataType: 'json',
data:{'email':email},
success: function(obj){
if( isEmptyObject(obj['error_info']) ){
$('.register-error').text('');
$('.ac-code').addClass('hide');
$('.ac-time').removeClass('hide');
timechange();
}
$.each(obj['error_info'],function (k,v) {
$('.register-error').text(v);
return false;
});
}
})
});
前端JQ:发送验证码
2.验证码发送过程: 页面倒计时
原理:设计计时器,每1秒修改1次text值, 时间小于0时,取消计时器
//邮箱验证码倒计时:
function timechange() {
var time = 60;
var interval = setInterval(function () {
if(time <= 0 ){
$('.ac-time').addClass('hide');
$('.ac-code').removeClass('hide');
clearInterval(interval);
}
time = time - 1;
var temp = "已发送("+time+"s)";
$('.ac-time').text(temp)
},1000)
}
前端JQ:倒计时
3.帐号注册
1.JQ获取前台输入表单内容
2.ajax传输给后台,建立表单验证
=> 正确,继续3
=> 错误,返回错误信息,页面显示错误信息
3.数据库中查询用户邮箱是否存在
=> 不存在,插入数据库,继续4
=> 存在,返回错误信息,页面显示
4.注册账号完成,写入session,注册完成页面刷新实现自动登录
//注册帐号
$('#register-next').on('click',function () {
registerUser();
clearinput();
});
function registerUser(){
var username= $('#re-username').val();
var email = $('#re-email').val();
var code = $('#re-code').val();
var password = $('#re-password').val();
$.ajax({
url:'/register',
type: 'POST',
dataType: 'json',
data:{'username':username,'email':email,'code':code,'password':password},
success: function(obj){
if( isEmptyObject(obj['error_info']) ){
$('.register-error').text('');
//跳转(隐藏输入页面):
$('.login-block').addClass('hide');
$('.register-info').removeClass('hide');
}
$.each(obj['error_info'],function (k,v) {
$('.register-error').text(v);
return false;
});
}
})
}
前端JQ:获取输入值
class RegisterUserHandler(BaseHandler):
def post(self):
obj = registerForm()
val, success_info, error_info = obj.check_value(self)
print(val, success_info, error_info)
if not error_info:
ins = InsertUser()
ins.inser(success_info['username'],success_info['password'],success_info['email'])
self.session['is_login'] = 1
self.session['username'] = success_info['username']
data = {'success_info': success_info, 'error_info': error_info}
self.write(json.dumps(data))
RegisterUserHandler
表单错误时,页面信息提示(效果图):
登录功能:
原理:与注册功能类似
1.图片验证码:点击验证码,自动更改
验证码为后台生成的图片,每点击一次,src地址后面 多加?实现切换
<div class="inp-block">
<input type="text" name="phoneregister" placeholder="请输入验证码" id="u-code" class="phonenum" autocomplete="off"/>
<img src="/check_code" onclick='ChangeCode();' id='imgCode' class="codeimg">
</div>
//注册页面验证码切换
function ChangeCode() {
var code = document.getElementById('imgCode');
code.src += '?';
}
前端JQ:点击改变验证码
//登录,确定按钮:(邮箱形式)
$('.login-btn').on('click',function () {
var username = $('#username').val();
var password = $('#password').val();
var code = $('#u-code').val();
var remember = 0;
if($('#phone-remember').is(':checked')){
remember = 1;
}
$.ajax({
url:'/userlogin',
type: 'POST',
dataType: 'text',
data:{'username':username,'password':password,'code':code,'remember':remember},
success: function(data, statusText, xmlHttpRequest){
obj = JSON.parse(data);
if( isEmptyObject(obj['error_info']) ){
$('.login-error').text('');
window.location.reload();
}
$.each(obj['error_info'],function (k,v) {
$('.login-error').text(v);
return false;
});
}
})
});
前端JQ:登录按钮
import io from backend.utils import check_code
from core.request_handle import BaseHandler class CheckcodeHandler(BaseHandler):
def get(self, *args, **kwargs):
mstream = io.BytesIO()
img, code = check_code.create_validate_code()
self.session['code'] = code
print(self.session['code'])
img.save(mstream, "GIF")
self.write(mstream.getvalue())
CheckcodeHandler
import tornado.ioloop
import tornado.web
import json
import os
import sys from core.request_handle import BaseHandler
from models.userinfo import QueryUser
from forms.account import LoginForm class LoginUserHandler(BaseHandler):
def post(self, *args, **kwargs):
obj = LoginForm()
val, success_info, error_info = obj.check_value(self)
print(val, success_info, error_info)
if not error_info:
if self.session['code'].upper() == success_info['code'].upper():
query = QueryUser()
ret = query.checkusername(success_info['username'],success_info['password'])
if ret:
self.session['is_login'] = 1
self.session['username'] = ret['username']
self.session['user_id'] = ret['user_id']
else:
error_info = { 'username' : '用户名或密码错误'}
else:
error_info = {'code': '验证码错误'}
data = { 'success_info': success_info , 'error_info': error_info }
self.write(json.dumps(data))
LoginUserHandler
#!/usr/bin/env python
#coding:utf-8 import random
from PIL import Image, ImageDraw, ImageFont, ImageFilter _letter_cases = "abcdefghjkmnpqrstuvwxy" # 小写字母,去除可能干扰的i,l,o,z
_upper_cases = _letter_cases.upper() # 大写字母
_numbers = ''.join(map(str, range(3, 10))) # 数字
init_chars = ''.join((_letter_cases, _upper_cases, _numbers)) def create_validate_code(size=(120, 30),
chars=init_chars,
img_type="GIF",
mode="RGB",
bg_color=(255, 255, 255),
fg_color=(0, 0, 255),
font_size=18,
font_type="Monaco.ttf",
length=4,
draw_lines=True,
n_line=(1, 2),
draw_points=True,
point_chance = 2):
'''
@todo: 生成验证码图片
@param size: 图片的大小,格式(宽,高),默认为(120, 30)
@param chars: 允许的字符集合,格式字符串
@param img_type: 图片保存的格式,默认为GIF,可选的为GIF,JPEG,TIFF,PNG
@param mode: 图片模式,默认为RGB
@param bg_color: 背景颜色,默认为白色
@param fg_color: 前景色,验证码字符颜色,默认为蓝色#0000FF
@param font_size: 验证码字体大小
@param font_type: 验证码字体,默认为 ae_AlArabiya.ttf
@param length: 验证码字符个数
@param draw_lines: 是否划干扰线
@param n_lines: 干扰线的条数范围,格式元组,默认为(1, 2),只有draw_lines为True时有效
@param draw_points: 是否画干扰点
@param point_chance: 干扰点出现的概率,大小范围[0, 100]
@return: [0]: PIL Image实例
@return: [1]: 验证码图片中的字符串
''' width, height = size # 宽, 高
img = Image.new(mode, size, bg_color) # 创建图形
draw = ImageDraw.Draw(img) # 创建画笔 def get_chars():
'''生成给定长度的字符串,返回列表格式'''
return random.sample(chars, length) def create_lines():
'''绘制干扰线'''
line_num = random.randint(*n_line) # 干扰线条数 for i in range(line_num):
# 起始点
begin = (random.randint(0, size[0]), random.randint(0, size[1]))
#结束点
end = (random.randint(0, size[0]), random.randint(0, size[1]))
draw.line([begin, end], fill=(0, 0, 0)) def create_points():
'''绘制干扰点'''
chance = min(100, max(0, int(point_chance))) # 大小限制在[0, 100] for w in range(width):
for h in range(height):
tmp = random.randint(0, 100)
if tmp > 100 - chance:
draw.point((w, h), fill=(0, 0, 0)) def create_strs():
'''绘制验证码字符'''
c_chars = get_chars()
strs = ' %s ' % ' '.join(c_chars) # 每个字符前后以空格隔开 font = ImageFont.truetype(font_type, font_size)
font_width, font_height = font.getsize(strs) draw.text(((width - font_width) / 3, (height - font_height) / 3),
strs, font=font, fill=fg_color) return ''.join(c_chars) if draw_lines:
create_lines()
if draw_points:
create_points()
strs = create_strs() # 图形扭曲参数
params = [1 - float(random.randint(1, 2)) / 100,
0,
0,
0,
1 - float(random.randint(1, 10)) / 100,
float(random.randint(1, 2)) / 500,
0.001,
float(random.randint(1, 2)) / 500
]
img = img.transform(size, Image.PERSPECTIVE, params) # 创建扭曲 img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) # 滤镜,边界加强(阈值更大) return img, strs
check_code.py
表单验证
设计思想: 无论表单通过form形式,或者ajax形式,发送数据给后端,我们都要先通过:某些规则进行过滤和验证,再对其进行数据的插入,修改等操作
实现思路:1,有form表单数据来临时,初始化一个表单类,传入self;
2,自动获取form上数据字段,self.get_arguments(name),name与前端input标签的name名字一一对应;
3,通过表单类,调用检测方法 check_value(),
4,如果form表单数据,符合自定义的规则,以字典形式返回数据,{ ‘前端input的name值’:‘input的value值’ } =》 存储在表单类的 self._value_dict 中
5,如果form表单数据,不符合自定义的规则,以字典形式返回错误信息,{ ‘前端input的name值’:‘错误信息’ } =》 存储在表单类的 self._error_dict 中
1. 定义一个表单类 LoginForm ,__init__方法中对象名为 前端input标签的name值
required 是否可以为空值, error_dict 定义错误信息输出内容
class LoginForm(BaseForm):
def __init__(self):
self.username = UsernameField(required=True,error_dict={'required':'用户名不能为空' , 'valid':'用户名错误'})
self.password = PasswordField(required=True,error_dict={'required':'密码不能为空' , 'valid':'用户名或密码错误'})
self.code = CodeField(required=True,error_dict={'required':'验证码不能为空' , 'valid':'验证码错误'})
self.remember = CheckBoxField(required=False,error_dict={'valid':'格式错误'})
super(LoginForm, self).__init__()
2. 继承BaseForm类,该类初始化了,当前Form表单共用的最后返回数据和错误信息的字典对象
class BaseForm():
def __init__(self):
self._value_dict = {}
self._error_dict = {}
self._valid_status = True def check_value(self):
....
check_value() 执行获取前端输入的数据,self.get_argument(xxxx) 并且根据每个input标签定义的规则去验证数据的正确性 (上文的UsernameField,PasswordField..等)
通过self.__dict__循环获取LoginForm的成员对象,调用Field的validate()方法,验证Form表单中每一个的值。验证正确,信息存储在self._value_dict 中, 错误信息存储在self._error_dict 中
def check_value(self,handler):
for key,regular in self.__dict__.items():
input_value = handler.get_argument(key,None)
regular.validate(key,input_value)
if regular.is_valid:
self._value_dict[key] = regular.value
else:
self._error_dict[key] = regular.error
class BaseForm():
def __init__(self):
self._value_dict = {}
self._error_dict = {}
self._valid_status = True def check_value(self,handler):
for key,regular in self.__dict__.items():
if key.startswith('_'):
continue
if type(regular) == fields.CheckBoxField:
input_value = handler.get_arguments(key) # checkbox取值
elif type(regular) == fields.FileField:
file_list = handler.request.files.get(key,[]) # 文件对象
input_value = []
for item in file_list:
input_value.append(item['filename'])
else:
input_value = handler.get_argument(key,None) regular.validate(key,input_value) if regular.is_valid:
self._value_dict[key] = regular.value
else:
self._error_dict[key] = regular.error
self._valid_status = False
return self._valid_status
BaseForm 完整代码
3. Field 自定义的规则类
为前端input标签,定义不同的验证规则(正则表达式),验证用户输入的数据
class PasswordField(Field):
REGULAR = "[0-9 | A-Z | a-z]{6,16}"
def __init__(self,required=True,error_dict=None): self.error_dict = {} #错误信息
if error_dict:
self.error_dict.update(error_dict) #用户自定义的错误信息 self.required = required
super(PasswordField, self).__init__()
继承父类Field,初始化存储信息的成员
class Field: def __init__(self):
self.is_valid = False # 验证规则是否通过,默认False
self.name = None
self.value = None # 获取的前端input值
self.error = None def validate(self, name, input_value):
...
执行validate()方法,
1.先判断该值是否允许为空?,
=> 可以为空,验证通过,self.value = input输入值
=> 不可以为空, 判断 input输入值 是否为空?
=> input输入值为空,self.error = 定义的错误信息(required)
=> 不为空,继续正则表达式判断,re.match(REGULAR,input_value)
=> 正则通过,self.value = input输入值, self.is_valid = True
=> 正则不通过, self.error = 定义的错误信息(valid)
class Field: def __init__(self):
self.is_valid = False
self.name = None
self.value = None
self.error = None def validate(self, name, input_value):
self.name = name if not self.required: # 可以为空
self.value = input_value
self.is_valid = True
else:
if not input_value:
if self.error_dict.get('required', None):
self.error = self.error_dict['required']
else:
self.error = '%s is requires ' % (name)
else:
val = re.match(self.REGULAR, input_value)
if not val:
if self.error_dict.get('valid', None):
self.error = self.error_dict['valid']
else:
self.error = '%s is valid ' % (name)
else:
self.value = input_value
self.is_valid = True
Field 完整代码
自定义的Field 供参考:
class CheckBoxField(Field):
REGULAR = "^\d+$" def __init__(self,required=True,error_dict=None): self.error_dict = {}
if error_dict:
self.error_dict.update(error_dict)
self.required = required
super(CheckBoxField, self).__init__() def validate(self,name,input_value):
if not self.required:
self.value = input_value
self.is_valid = True
else:
if not input_value:
if self.error_dict.get('required',None):
self.error = self.error_dict['required']
else:
self.error = '%s is requires '%(name)
else:
if isinstance(name, list):
self.is_valid = True
self.value = input_value
else:
if self.error_dict.get('valid', None):
self.error = self.error_dict['valid']
else:
self.error = "%s is invalid" % name
CheckBoxField
class FileField(Field):
REGULAR = "^(\w+\.jpg)|(\w+\.jpeg)|(\w+\.gif)|(\w+\.png)$" def __init__(self, required=True, error_dict=None):
self.error_dict = {}
if error_dict:
self.error_dict.update(error_dict)
self.required = required
self.value = []
self.success_file_name_list = []
super(FileField, self).__init__() def validate(self, name, all_file_name_list):
self.name = name
if not self.required:
self.value = all_file_name_list
else:
if not all_file_name_list:
self.is_valid = False
if self.error_dict.get('required', None):
self.error = self.error_dict['required']
else:
self.error = '%s is requires ' % (name)
else:
for file_name in all_file_name_list:
if not file_name or not file_name.strip():
if self.error_dict.get('required', None):
self.error = self.error_dict['required']
else:
self.error = "%s is required" % name
self.is_valid = False
break
else:
val = re.match(FileField.REGULAR, file_name)
if not val:
self.is_valid = False
if self.error_dict.get('valid', None):
self.error = self.error_dict['valid']
else:
self.error = '%s is valid ' % (name)
break
else:
self.value.append(file_name)
FileField
class EmailField(Field): REGULAR = "^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$" def __init__(self, required=True, error_dict=None): self.error_dict = {} # 错误信息
if error_dict:
self.error_dict.update(error_dict) # 用户自定错误信息
self.required = required
super(EmailField, self).__init__()
EmailField (input类型为text通用)
Session
设计思想: 利用Cookie 自定义一个Session来存储每个用户信息
实现思路: 1. Session信息设计成一个大字典,key对应用户唯一识别加密串,value对应空字典{}存储用户信息,存放在服务端上。
2. 为用户请求生成一个唯一加密串,写入到Cookie信息中,Session_id = 加密串,用于区分每一个用户。
3. 当用户请求到来时,获取该请求的Cookie信息,判断是否存在Session_id(用户唯一识别加密串)?
=> 如果存在Session_id 并且在 Session大字典中找到相同的 key,记录Session_id
=> 其他情况下一律,生成加密串,写入到Cookie中,同时写入到 Session大字典中
class CacheSession():
session_id = "__balabala__" # Cookie中为Session存储的名字
session_container = {} # Session大字典 def __init__(self,handler):
self.handler = handler
client_random_str = self.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 = self.__container__random__str()
CacheSession.session_container[self.random_str] = {} expires_time = time.time() + config.SESSION_EXPIRES
self.handler.set_cookie(CacheSession.session_id, self.random_str, expires=expires_time) # 方便后续定义过期时间! def __container__random__str(self): # 生成加密串
hash = hashlib.md5()
hash.update(bytes(str(time.time()), encoding='utf-8'))
random_str = hash.hexdigest()
return random_str
利用方法:1. 当用户请求到达每个Handler时,我们都需要先实例化一个CacheSession(),
2. 此时我们可以定义一个父类BaseHandler,initialize() 方法中写入要执行代码,
import tornado.web
form session import SessionFactory class BaseHandler(tornado.web.RequestHandler):
def initialize(self):
self.session = SessionFactory.get_session_obj(self)
可以看到,我们在session.py中定义了一个新的类SessionFactory,用来选择合适的方法,初始化Session,
该类通过读取配置文件config中的SESSION_TYPE选择适合的Session类进行初始化,并且返回一个Session对象,该对象最终存储在 self.session中。
class SessionFactory(): @staticmethod
def get_session_obj(handler):
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
Handler 中使用Session
class LoginUserHandler(BaseHandler):
def post(self, *args, **kwargs):
self.session['is_login'] = 1
self.write('ok')
1.缓存Session
import time
import hashlib
import config
import memcache
import json
import redis class CacheSession():
session_id = "__balabala__"
session_container = {} def __init__(self,handler):
self.handler = handler
client_random_str = self.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 = self.__container__random__str()
CacheSession.session_container[self.random_str] = {} expires_time = time.time() + config.SESSION_EXPIRES
self.handler.set_cookie(CacheSession.session_id, self.random_str, expires=expires_time) # 方便后续定义过期时间! def __container__random__str(self):
hash = hashlib.md5()
hash.update(bytes(str(time.time()), encoding='utf-8'))
random_str = hash.hexdigest()
return random_str def __setitem__(self,key,value):
CacheSession.session_container[self.random_str][key] = value def __getitem__(self,key):
result = CacheSession.session_container[self.random_str].get(key,None)
return result def __delitem__(self,key):
if key in CacheSession.session_container[self.random_str]:
del CacheSession.session_container[self.random_str][key]
2.memcache session
conn = memcache.Client(['127.0.0.1:11210'], debug=True) class MemcachedSession():
session_id = "__balabala__" def __init__(self,handler):
self.handler = handler
client_random_str = self.handler.get_cookie(MemcachedSession.session_id,None)
if client_random_str and conn.get(client_random_str):
self.random_str = client_random_str
else:
self.random_str = self.__container__random__str()
conn.set(self.random_str,json.dumps({}),config.SESSION_EXPIRES) conn.set(self.random_str, conn.get(self.random_str), config.SESSION_EXPIRES) expires_time = time.time() + config.SESSION_EXPIRES
self.handler.set_cookie(CacheSession.session_id, self.random_str, expires=expires_time) # 方便后续定义过期时间! def __container__random__str(self):
hash = hashlib.md5()
hash.update(bytes(str(time.time()), encoding='utf-8'))
random_str = hash.hexdigest()
return random_str 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) def __getitem__(self,key):
ret = conn.get(self.random_str)
ret_dict = json.loads(ret)
result = ret_dict.get(key,None)
return result 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)
3.radis session
pool = redis.ConnectionPool(host='127.0.0.1', port=6379)
r = redis.Redis(connection_pool=pool) class RedisSession():
session_id = "__balabala__" def __init__(self, handler):
self.handler = handler
client_random_str = self.handler.get_cookie(RedisSession.session_id, None)
if client_random_str and r.exists(client_random_str):
self.random_str = client_random_str
else:
self.random_str = self.__container__random__str()
r.hset(self.random_str, None, None) r.expire(self.random_str,config.SESSION_EXPIRES) expires_time = time.time() + config.SESSION_EXPIRES
self.handler.set_cookie(CacheSession.session_id, self.random_str, expires=expires_time) # 方便后续定义过期时间! def __container__random__str(self):
hash = hashlib.md5()
hash.update(bytes(str(time.time()), encoding='utf-8'))
random_str = hash.hexdigest()
return random_str 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) def __getitem__(self, key):
ret = r.hget(self.random_str,key)
if ret :
ret_str = str(ret,encoding='utf-8')
try:
result = json.loads(ret_str)
except:
result = ret_str
return result
else:
return ret def __delitem__(self, key):
r.hdel(self.random_str, key)
需要注意的是:__setitem__,__getitem__和__delitem__ 使用类似字典方式设置,访问,删除成员。
在缓存Session 中,他们的使用方法与字典差别不大。
在memcache 中,键值对key,value 都是以字符串的形式存储的,
在设置值前需要将value值通过json转换成字典形式,再对字典进行操作,操作完毕后,用json转换回字符串,存储回原来位置!
在redis 中,选用hash操作进行存储,如果待存储的value值为字典,需要先把value通过json转换成字符串,再存储在redis中,
获取某个key的value值时,由于hash中value是以bytes存储,需要先转换成str类型,再判断该key存储的是字典,还是普通字符串
Session所有的完整代码:
#!/usr/bin/env python
# -*-coding:utf-8 -*- import time
import hashlib
import config
import memcache
import json
import redis class CacheSession():
session_id = "__balabala__"
session_container = {} def __init__(self,handler):
self.handler = handler
client_random_str = self.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 = self.__container__random__str()
CacheSession.session_container[self.random_str] = {} expires_time = time.time() + config.SESSION_EXPIRES
self.handler.set_cookie(CacheSession.session_id, self.random_str, expires=expires_time) # 方便后续定义过期时间! def __container__random__str(self):
hash = hashlib.md5()
hash.update(bytes(str(time.time()), encoding='utf-8'))
random_str = hash.hexdigest()
return random_str def __setitem__(self,key,value):
CacheSession.session_container[self.random_str][key] = value def __getitem__(self,key):
result = CacheSession.session_container[self.random_str].get(key,None)
return result def __delitem__(self,key):
if key in CacheSession.session_container[self.random_str]:
del CacheSession.session_container[self.random_str][key] conn = memcache.Client(['127.0.0.1:11210'], debug=True) class MemcachedSession():
session_id = "__balabala__" def __init__(self,handler):
self.handler = handler
client_random_str = self.handler.get_cookie(MemcachedSession.session_id,None)
if client_random_str and conn.get(client_random_str):
self.random_str = client_random_str
else:
self.random_str = self.__container__random__str()
conn.set(self.random_str,json.dumps({}),config.SESSION_EXPIRES) conn.set(self.random_str, conn.get(self.random_str), config.SESSION_EXPIRES) expires_time = time.time() + config.SESSION_EXPIRES
self.handler.set_cookie(CacheSession.session_id, self.random_str, expires=expires_time) # 方便后续定义过期时间! def __container__random__str(self):
hash = hashlib.md5()
hash.update(bytes(str(time.time()), encoding='utf-8'))
random_str = hash.hexdigest()
return random_str 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) def __getitem__(self,key):
ret = conn.get(self.random_str)
ret_dict = json.loads(ret)
result = ret_dict.get(key,None)
return result 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) pool = redis.ConnectionPool(host='127.0.0.1', port=6379)
r = redis.Redis(connection_pool=pool) class RedisSession():
session_id = "__balabala__" def __init__(self, handler):
self.handler = handler
client_random_str = self.handler.get_cookie(RedisSession.session_id, None)
if client_random_str and r.exists(client_random_str):
self.random_str = client_random_str
else:
self.random_str = self.__container__random__str()
r.hset(self.random_str, None, None) r.expire(self.random_str,config.SESSION_EXPIRES) expires_time = time.time() + config.SESSION_EXPIRES
self.handler.set_cookie(CacheSession.session_id, self.random_str, expires=expires_time) # 方便后续定义过期时间! def __container__random__str(self):
hash = hashlib.md5()
hash.update(bytes(str(time.time()), encoding='utf-8'))
random_str = hash.hexdigest()
return random_str 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) def __getitem__(self, key):
ret = r.hget(self.random_str,key)
if ret :
ret_str = str(ret,encoding='utf-8')
try:
result = json.loads(ret_str)
except:
result = ret_str
return result
else:
return ret def __delitem__(self, key):
r.hdel(self.random_str, key) class SessionFactory(): @staticmethod
def get_session_obj(handler):
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
session.py
#!/usr/bin/env python
# -*-coding:utf-8 -*- import tornado.ioloop
import tornado.web from backend.session.session import SessionFactory class BaseHandler(tornado.web.RequestHandler):
def initialize(self):
self.session = SessionFactory.get_session_obj(self)
request_handle.py
#!/usr/bin/env python
# -*-coding:utf-8 -*- # Session类型:cache/redis/memcached
SESSION_TYPE = "cache" # Session超时时间(秒)
SESSION_EXPIRES = 180
config.py 配置文件
分页
设计思路:与Tornado篇分页同理
前端:
<div class="pagination">
{% raw str_page%} #展示原生html
</div>
前端页面
后台Url配置:
(r"/index/(?P<page>\d*)", IndexHandler),
后台Handle:
class IndexHandler(BaseHandler):
def get(self, page):
query = QueryContent()
page_obj = pager.Pagiantion(page, all_item=query.queryCounts(), per_num=5) #page 当前页码 , all_item 总数据条数, per_num 每页显示条数
str_page = page_obj.page_str('/index/') self.render("index.html", str_page=str_page )
Pagiantion 分页类:
class Pagiantion:
def __init__(self,current_page,all_counts,per_num): all_page, c = divmod(all_counts, per_num)
if c>0:
all_page += 1
self.all_page = all_page try:
current_page = int(current_page)
except:
current_page = 1
if current_page < 1:
current_page = 1
self.current_page = current_page def page_str(self,base_url):
if self.all_page < 10:
s = 1
t = self.all_page
else:
if self.current_page < 7:
s = 1
t = 10
else:
if self.all_page - self.current_page >= 4 :
s = self.current_page - 3
t = self.current_page + 4
else:
s = self.all_page - 6
t = self.all_page
list_page = [] # 上一页
if self.current_page != 1:
pre_page = "<a href='%s%s' class='pageedg'>上一页</a>" % (base_url, self.current_page - 1,)
list_page.append(pre_page) # 页码生成
# 生成 1 , ... ,
if( self.current_page >= 7 ):
temp = "<a class='pageNum' href='%s%s'>%s</a>" % (base_url, 1, 1)
list_page.append(temp)
temp = "<span class='ignore' >...</span>"
list_page.append(temp) for i in range(s, t + 1):
if i == self.current_page:
temp = "<span class='active-page' href='%s%s'>%s</span>" % (base_url,i, i)
else:
temp = "<a href='%s%s' class='pageNum'>%s</a>" % (base_url , i, i)
list_page.append(temp) # 下一页
if self.current_page < self.all_page:
next_page = "<a href='%s%s' class='pageedg'>下一页</a>" % (base_url, self.current_page + 1,)
list_page.append(next_page) # 数据拼接 返回
str_page = ''.join(list_page) # 列表连接成为字符串
return str_page
Pagiantion 分页类
/*分页*/
.dig-page-block{
width: 640px;
height: 40px;
/*border: 1px solid red;*/
float: left;
margin:20px 0 60px 0;
}
.dig-page {
width:630px;
height: 38px;
}
.dig-page a.pageNum{
display: inline-block;
min-width:34px;
height: 34px;
color: #369;
line-height: 34px;
text-align: center;
border: 1px solid #e1e1e1;
border-radius: 5px 5px;
margin-right:6px;
text-decoration: none;
font-family: "Arial","Microsoft YaHei","黑体","宋体",sans-serif;
}
.dig-page a.pageNum:hover{
color: white;color: #fff;
background-color: #369;
border: 1px solid #369;
}
/*页码中文字体*/
.dig-page a.pageedg{
display: inline-block;
width: 77px;
height: 34px;
color: #369;
line-height: 34px;
text-align: center;
border: 1px solid #e1e1e1;
border-radius: 5px 5px;
margin-right:6px;
text-decoration: none;
font-family: "Arial","Microsoft YaHei","黑体","宋体",sans-serif;
}
.dig-page a.pageedg:hover{
color: #fff;
background-color: #369;
border: 1px solid #369;
}
.dig-page .ignore{
display: inline-block;
width: 32px;
height: 32px;
color: #369;
line-height: 32px;
text-align: center;
margin-right:6px;
}
.dig-page .active-page{
display: inline-block;
min-width:34px;
height: 34px;
font-weight:;
color: #333;
line-height: 34px;
text-align: center;
margin-right:6px;
font-family: "Arial","Microsoft YaHei","黑体","宋体",sans-serif;
}
分页标签 Css
页面登陆验证(装饰器方式实现)
1.普通登陆验证
LOGIN_URL = '/login' def auth_login_redirect(func): def inner(self, *args, **kwargs):
if not self.session['is_login']:
self.redirect(config.LOGIN_URL)
return
func(self, *args, **kwargs)
return inner
2.ajax提交数据的登陆验证
def auth_login_json(func): def inner(self, *args, **kwargs):
if not self.session['is_login']:
rep = BaseResponse()
rep.summary = "auth failed"
self.write(json.dumps(rep.__dict__))
return
func(self, *args, **kwargs)
return inner
文件上传
为了美观 , 文件上传标签一半由两部分组成 file标签和 button标签
<div id="main" class="up-block">
<input name="file" id="my_file" class="file-path" type="file" onchange="imagechange()"/>
<input type="button" name="action" value="Upload" class="a-upload"/>
<div>
file标签会设置 透明度 和 定位在button上 , 绑定onchange事件
.up-block{
position: relative;
}
.file-path{
position: absolute;
width: ..px;
height: ..px;
font-size: ..px;
opacity: 0;
z-index: 10;
}
图片上传后,展示在上传页面:
方法一:利用iframe实现
<div id="main" class="up-block">
<input name="file" id="my_file" class="file-path" type="file" onchange="imagechange()"/>
<input type="button" name="action" value="Upload" class="a-upload"/>
<div>
<iframe id='my_iframe' name='my_iframe' src="" class="hide"></iframe> function imagechange(){
imgupload();
}
// iframe图片上传
function imgupload(){
document.getElementById('my_iframe').onload = Testt;
document.getElementById('my_form').target = 'my_iframe';
document.getElementById('my_form').submit();
}
function Testt(ths) {
var r = $("#my_iframe").contents().find("body").text();
ret = JSON.parse(r); // 获得后台返回的数据...
}
抽屉网实现,完整代码:
//图片上传按钮:
$(".a-upload").on("change","input[type='file']",function(){
imgupload()
});
// iframe图片上传
function imgupload(){
document.getElementById('my_iframe').onload = Testt;
document.getElementById('my_form').target = 'my_iframe';
document.getElementById('my_form').submit();
}
function Testt(ths) {
var r = $("#my_iframe").contents().find("body").text();
ret = JSON.parse(r);
if( ret.status ){
var img_path = ret['message']['file_path'];
console.log(img_path);
var pre = 'http://localhost:8888/';
$('.img-alt').addClass('hide');
$('.upload-btn').addClass('upload-btn-show');
$('.upload-show').removeClass('hide').attr('src',pre+img_path);
$(".upload-error").text('');
}else{
$.each(ret['message'],function (k,v) {
$(".upload-error").text(v).show();
console.log(v);
//清除信息:
$('#uploadimage').val('');
$(".img-alt").text("支持jpg、jpeg、gif、png格式,且不超过5MB");
$(".upload-error").text("您上传的图片格式不合法,请重新上传").show();
return false;
});
}
}
jQ 代码
<div class="img-upload-block">
<img src="" alt="" class="upload-show hide"/>
<div class="upload-btn">
<a href="javascript:;" class="a-upload">上传
<input type="file" name="uploadimage" id="uploadimage">
</a>
</div>
<span class="img-alt">支持jpg、jpeg、gif、png格式,且不超过5MB</span>
</div>
Html 代码
/*上传文件按钮*/
.a-upload {
position: relative;
display: inline-block;
background: url('http://dig.chouti.com/images/bottom.png?v=2.8')no-repeat center center;
background-position: 0 0px;
border-radius: 4px;
padding: 4px 16px;
overflow: hidden;
color: white;
font-weight:;
text-decoration: none;
text-indent:;
line-height: 20px;
margin: 10px 0 0 6px;
}
.a-upload input {
position: absolute;
right:;
top:;
opacity:;
}
.a-upload:hover {
color: white;
background-position: 0 -33px;
text-decoration: none;
}
.pic-content .img-upload-block .img-alt{
color: #8ca1c1;
padding-left: 6px;
vertical-align: -12px;
margin-left: 4px;
font-size: 12px;
font-family: "\5b8b\4f53";
}
Css 代码
方法二:ajax实现
通过Ajax的FormData对象来实现。详情参考ajax篇
<input type="file" name="uploadimage" id="uploadimage"> var fileObj = $("#uploadimage")[0].files[0];
var form = new FormData();
form.append("uploadimage", fileObj);
$.ajax({
url:'/uploadimg',
type:'POST',
data:form,
processData: false, // tell jQuery not to process the data
contentType: false,
success:function (data, statusText, xmlHttpRequest) {
obj = JSON.parse(data);
}
})
【Python之路】特别篇--抽屉新热榜的更多相关文章
- Python之路【第二十篇】:python项目之旧版抽屉新热榜
旧版抽屉新热榜 代码如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset=& ...
- 【IOS】模仿"抽屉新热榜"动态启动页YFSplashScreen
IOS最好要设置系统默认启动页面,不然进入应用就会突然闪现黑色画面 下图是我们要实现的效果: 总体思路:设置一个系统默认启动页面,在进入didFinishLaunchingWithOptions时, ...
- 利用scrapy获取抽屉新热榜的标题和内容以及新闻地址保存到本地
1.安装scrapy pip3 install scrapy 2.打开terminal,cd 到想要创建程序的目录下 3.创建一个scrapy项目 在终端输入:scrapy startproject ...
- python之路入门篇
一. Python介绍 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,Guido开始写能够解释Python语言语法的解释器.Python这个名字,来 ...
- python之路——基础篇(2)模块
模块:os.sys.time.logging.json/pickle.hashlib.random.re 模块分为三种: 自定义模块 第三方模块 内置模块 自定义模块 1.定义模块 将一系列功能函数或 ...
- python之路基础篇
基础篇 1.Python基础之初识python 2.Python数据类型之字符串 3.Python数据类型之列表 4.Python数据类型之元祖 5.Python数据类型之字典 6.Python Se ...
- python之路第二篇(基础篇)
入门知识: 一.关于作用域: 对于变量的作用域,执行声明并在内存中存在,该变量就可以在下面的代码中使用. if 10 == 10: name = 'allen' print name 以下结论对吗? ...
- Python之路(第四十一篇)线程概念、线程背景、线程特点、threading模块、开启线程的方式
一.线程 之前我们已经了解了操作系统中进程的概念,程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程.程序和进程的区别就在于:程序是指令的集合,它是 ...
- python之路第一篇
一.python环境的搭建 1.window下环境的搭建 (1).在 https://www.python.org/downloads/ 下载自己系统所需要的python版本 (2).安装python ...
随机推荐
- SQLite进阶-16.索引
目录 索引 创建索引 查看索引 删除索引 创建索引的注意项 索引使用(Indexed By) 索引 索引(Index)是一种特殊的查找表,数据库搜索引擎用来加快数据检索.简单地说,索引是一个指向表中数 ...
- Spring 加载项目外部配置文件
背景 在项目的部署过程中,一般是打成 war 或者 jar 包,这样一般存在两种问题: 即使是配置文件修改,也还需要整个项目重新打包和部署. 整个项目只有一套环境,不能切换. 针对上面的问题,可以使用 ...
- Kubernetes组件-DaemonSet
⒈简介 Replicationcontroller和ReplicaSet都用于在Kubermetes集群上部署运行特定数量的pod.但是,当某些情况下我们希望在集群中的每个节点上运行同一个指定的pod ...
- IPv4
1.IPv4分类地址 PC0(192.168.0.1) 和PC1(172.168.0.1)如果要ping通的话需要设置各自网关 PC0 设置IP 和 默认网关=路由器设置IP 2.Gigabit ...
- 【Trie】The XOR-longest Path
[题目链接]: https://loj.ac/problem/10056 [题意] 请输出树上两个点的异或路径 的最大值. [题解] 这个题目,y总说过怎么做之后,简直就是醍醐灌顶了. 我们知道Xo ...
- Linux:删除一个目录下的所有文件,但保留一个指定文件
面试题:删除一个目录下的所有文件,但保留一个指定文件 解答: 假设这个目录是/xx/,里面有file1,file2,file3..file10 十个文件 [root@oldboy xx]# touc ...
- sleep(0)、usleep(0)与sched_yield() 调度
结论: 如果你是为了耗掉一个机器周期 ,那直接asm ("nop") , 如果是为了让权,建议把 所有使用 usleep(0) 换成 sched_yield() ; 最近发现很多 ...
- django API返回中文乱码
renturn HttpResponse(json.dumps(data,ensure_ascii=False))
- Python 装饰&生成&迭代器
Python 的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解释程序,作为ABC语言的一种继承.Py ...
- Spring mvc 参数半丁
http://blog.csdn.net/eson_15/article/details/51718633 众所周知,springmvc是用来处理页面的一些请求,然后将数据再通过视图返回给用户的,前面 ...