【Python之路】特別篇--抽屜新熱榜

登錄與註冊

註冊功能:javascript

流程: 填寫用戶名,郵箱,獲取郵箱驗證碼,填入密碼 單擊<下一步>按鈕,完成註冊!html

1.獲取郵箱驗證碼(具體步驟分析):前端

1.利用ajax 日後臺傳入郵箱,java

2.後臺表單驗證,郵箱填寫是否正確,     python

  => 成功,則繼續3,git

  => 錯誤,寫入錯誤信息,頁面輸出錯誤提示!web

3.查詢sendcode 數據庫表,查詢該郵箱是不是第一次發送驗證碼ajax

  => 第一次發送: 數據表中插入記錄 (郵箱,發送次數1,郵箱驗證碼,狀態1,有效時間:當前時間-1小時)正則表達式

  => 不然: 從數據表中查詢出該郵箱驗證碼發送次數,redis

      => 若是 >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]
View Code

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)
View Code

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)
View Code

須要注意的是:__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: 700;
    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);      // 得到後臺返回的數據...
}
View Code

抽屜網實現,完整代碼:

//圖片上傳按鈕:
$(".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: 700;
    text-decoration: none;
    text-indent: 0;
    line-height: 20px;
    margin: 10px 0 0 6px;
}
.a-upload input {
    position: absolute;
    right: 0;
    top: 0;
    opacity: 0;
}
.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);
	}
})
相關文章
相關標籤/搜索