Tornado實(shí)戰(zhàn)-用戶登錄與注冊(cè)

需要模塊

pip install redis
pip install packet

實(shí)現(xiàn)代碼

app.py

import tornado.ioloop   #開啟循環(huán),讓服務(wù)一直等待請(qǐng)求的到來(lái)
import tornado.web      #web服務(wù)基本功能都封裝在此模塊中
import tornado.options  #從命令行中讀取設(shè)置
from tornado.options import define,options  #導(dǎo)入包

from handlers import main,auth

define('port',default='8000',help='Listening port',type=int) #定義如何接受傳進(jìn)來(lái)的東西


class Application(tornado.web.Application):  #引入Application類,重寫方法示弓,這樣做的好處在于可以自定義块差,添加另一些功能
    def __init__(self):
        handlers = [
            (r'/',main.IndexHandler),
            (r'/explore',main.ExploreHandler),
            (r'/post/(?P<post_id>[0-9]+)',main.PostHandler), #命名組寫法,使用關(guān)鍵字贡茅,路由與handler方法不一定順序一致
            (r'/upload',main.UploadHandler),
            (r'/login',auth.LoginHandler),
            (r'/logout',auth.LogoutHandler),
            (r'/register',auth.RegisterHandler)
        ]
        settings = dict(
            debug = True, #調(diào)試模式,修改后自動(dòng)重啟服務(wù)状答,不需要自動(dòng)重啟粉洼,生產(chǎn)情況下切勿開啟,安全性
            template_path='templates', #模板文件目錄,想要Tornado能夠正確的找到html文件仅财,需要在 Application 中指定文件的位置
            static_path='static',  #靜態(tài)文件目錄,可用于用于訪問(wèn)js,css,圖片之類的添加此配置之后狈究,tornado就能自己找到靜態(tài)文件
            login_url='/login', #沒(méi)有登錄則跳轉(zhuǎn)至此
            cookie_secret='1q2w3e4r',  # 加密cookie的字符串
            pycket={  #固定寫法packet,用于保存用戶登錄信息
                'engine': 'redis',
                'storage': {
                    'host': 'localhost',
                    'port': 6379,
                    'db_sessions': 5,
                    'db_notifications': 11,
                    'max_connections': 2 ** 33,
                },
                'cookie': {
                    'expires_days': 38,
                    'max_age': 100
                }
            }
        )

        super(Application,self).__init__(handlers,**settings) #用super方法將父類的init方法重新執(zhí)行一遍盏求,然后將handlers和settings傳進(jìn)去抖锥,完成初始化


app = Application() #實(shí)例化


if __name__ == '__main__':   #當(dāng).py文件被直接運(yùn)行時(shí),代碼塊將被運(yùn)行碎罚;當(dāng).py文件以模塊形式被導(dǎo)入時(shí)磅废,代碼塊不被運(yùn)行。

    tornado.options.parse_command_line()
    app.listen(options.port)  ##如果一個(gè)與define語(yǔ)句中同名的設(shè)置在命令行中被給出荆烈,那么它將成為全局的options的一個(gè)屬性 即 options.port 相當(dāng)于define的url的port
    print("Server start on port {}".format(str(options.port)))  #提示服務(wù)啟動(dòng)占用端口
    tornado.ioloop.IOLoop.current().start()   #執(zhí)行ioloop

main.py

import tornado.web
import os
from pycket.session import SessionMixin
from utils import photo


class AuthBaseHandler(tornado.web.RequestHandler,SessionMixin):
    def get_current_user(self): #重寫get_current_user()方法
        return self.session.get('user_info',None) #session是一種會(huì)話狀態(tài)拯勉,跟數(shù)據(jù)庫(kù)的session可能不一樣

#添加裝飾器,裝飾需要驗(yàn)證的請(qǐng)求
class IndexHandler(AuthBaseHandler):
    """
     Home page for user,photo feeds 主頁(yè)----所關(guān)注的用戶圖片流
    """
    @tornado.web.authenticated   #@tornado.web.authenticated裝飾器包裹get方法時(shí),表示這個(gè)方法只有在用戶合法時(shí)才會(huì)調(diào)用憔购,authenticated裝飾器會(huì)調(diào)用get_current_user()方法獲取current_user的值宫峦,若值為False,則重定向到登錄url裝飾器判斷有沒(méi)有登錄玫鸟,如果沒(méi)有則跳轉(zhuǎn)到配置的路由下去导绷,但是要在app.py里面設(shè)置login_url
    def get(self,*args,**kwargs):
        self.render('index.html')


class ExploreHandler(AuthBaseHandler):
    """
    Explore page,photo of other users 發(fā)現(xiàn)頁(yè)-----發(fā)現(xiàn)或最近上傳的圖片頁(yè)面
    """
    @tornado.web.authenticated
    def get(self,*args,**kwargs):
        # image_urls = get_images("./static/uploads")  #打開指定路徑下的文件,或者static/uploads
        os.chdir('static')  # 用于改變當(dāng)前工作目錄到指定的路徑
        image_urls = photo.get_images("uploads/thumbs")
        os.chdir("..")
        self.render('explore.html',image_urls=image_urls)

class PostHandler(AuthBaseHandler):
    """
    Single photo page and maybe  單個(gè)圖片詳情頁(yè)面
    """
    @tornado.web.authenticated
    def get(self,post_id):
        print(post_id)
        self.render('post.html',post_id = post_id)   #根據(jù)正則輸入的內(nèi)容屎飘,接收到妥曲,打開相應(yīng)的圖片


class UploadHandler(AuthBaseHandler):  #上傳文件
    @tornado.web.authenticated
    def get(self,*args,**kwargs):
        self.render('upload.html')

    def post(self,*args,**kwargs):
        file_imgs = self.request.files.get('newImg',None)  #獲取上傳文件數(shù)據(jù),返回文件列表

        for file_img in file_imgs: #可能同一個(gè)上傳的文件會(huì)有多個(gè)文件枚碗,所以要用for循環(huán)去迭代它
            # filename 文件的實(shí)際名字逾一,body 文件的數(shù)據(jù)實(shí)體;content_type 文件的類型肮雨。 這三個(gè)對(duì)象屬性可以像字典一樣支持關(guān)鍵字索引
            save_to = 'static/uploads/{}'.format(file_img['filename'])
            #以二進(jìn)制格式打開一個(gè)文件只用于寫入。如果該文件已存在則打開文件箱玷,并從開頭開始編輯怨规,即原有內(nèi)容會(huì)被刪除。如果該文件不存在锡足,創(chuàng)建新文件波丰。一般用于非文本文件如圖片等。
            with open(save_to,'wb') as f: #二進(jìn)制
                f.write(file_img['body'])
            photo.make_thumb(save_to) #同時(shí)生成縮略圖

        self.redirect('/explore')

auth.py

import tornado.web
from utils.account import authenticate
from .main import AuthBaseHandler



class RegisterHandler(AuthBaseHandler):
    def get(self, *args, **kwargs):
        print('register')
        self.render('register.html')

    def post(self, *args, **kwargs):
        print('registerpost')

        username = self.get_argument('username','')
        password1 = self.get_argument('password1','')
        password2 = self.get_argument('password2','')

        if username and password1 and (password1 == password2):
            pass
        else:
            self.write({'msg':'register fail'})


class LoginHandler(AuthBaseHandler):
    def get(self,*args,**kwargs):
        if self.current_user: #若用戶已登錄
            self.redirect('/') #那么直接跳轉(zhuǎn)到主頁(yè)
        else:
            nextname = self.get_argument('next','') #將原來(lái)的路由賦值給nextname
            self.render('login.html',nextname = nextname) #否則去登錄界面

    def post(self,*args,**kwargs):
        username = self.get_argument('username',None)
        password = self.get_argument('password',None)

        passed = authenticate(username,password)

        if passed:
            self.session.set('user_info',username) #將前面設(shè)置的cookie設(shè)置為username舶得,保存用戶登錄信息
            next_url = self.get_argument('next', '')  # 獲取之前頁(yè)面的路由

            if next_url:
                self.redirect(next_url) #跳轉(zhuǎn)主頁(yè)路由
            else:
                self.redirect('/')
        else:
            self.write({'msg':'login fail'}) #不通過(guò)掰烟,有問(wèn)題

class LogoutHandler(AuthBaseHandler):
    def get(self, *args, **kwargs):
        #self.session.set('user_info','') #將用戶的cookie清除
        self.session.delete('user_info')
        self.redirect('/login')

db.py

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker



HOSTNAME = '127.0.0.1'
PORT = '3306'  #注意這個(gè)不是本地端口是指遠(yuǎn)程數(shù)據(jù)庫(kù)端口,因?yàn)閜ycharm已經(jīng)先SSH連接到本地了
DATABASE = 'my_db'
USERNAME = 'root'
PASSWORD = '1q2w3e4r'

db_url = 'mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8'.format(
    USERNAME,
    PASSWORD,
    HOSTNAME,
    PORT,
    DATABASE
)


#連接數(shù)據(jù)庫(kù)
engine = create_engine(db_url)

Base = declarative_base(bind = engine)  #這個(gè)基類是維系類和數(shù)據(jù)表關(guān)系的目錄。

#在對(duì)表數(shù)據(jù)進(jìn)行增刪改查之前纫骑,先需要建立會(huì)話蝎亚,建立會(huì)話之后才能進(jìn)行操作,就類似于文件要打開之后才能對(duì)文件內(nèi)容操作先馆。
Session = sessionmaker(engine)
session = Session()

users.py

from datetime import datetime
from sqlalchemy import Column,Integer,String,DateTime
from .db import Base

class User(Base):

    __tablename__ = 'users'
    id = Column(Integer,primary_key=True,autoincrement=True)
    name = Column(String(50),unique=True,nullable=False)
    password = Column(String(50),nullable=False)
    last_login = Column(DateTime,default=datetime.now)

    def __repr__(self):
        return '<User #{}:{}>'.format(self.id,self.name)

account.py

import hashlib

def hash(text):
    text = hashlib.md5(text.encode()).hexdigest() #給密碼加密发框,用hashlib來(lái)算法加密,utf8不加的話就是默認(rèn)utf8

    return text

USER_DATA = {
    'name':'user',
    'password':hash('1q2w3e4r')
}

def authenticate(username,password):#用戶密碼匹配判斷函數(shù)
    if username and password:
        hash_pwd = hash(password)
        if username == USER_DATA['name'] and hash_pwd == USER_DATA['password']: #是否與保存的一致
            return True

    return False

index.html

{% extends 'base.html' %} #繼承base.html

{% block title %} index page {% end %}

{% block content %}
index content
current_user:{{current_user}} <!--獲取當(dāng)前用戶 -->
{% for num in range(1,5) %}
<a href="/post/{{num}}">
    <img src="{{static_url('images/{}.jpg'.format(num))}}" /> <!-- 使用此方法時(shí)煤墙,Tornado 會(huì)自動(dòng)地給靜態(tài)文件添加版本號(hào)梅惯,如果版本號(hào)更改了,瀏覽器會(huì)自動(dòng)的緩存新的靜態(tài)文件-->
</a>

{% end %}
{% end %}
image.png

![image.png](https://upload-images.jianshu.io/upload_images/9286065-aa9c90ddb040bc5c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末仿野,一起剝皮案震驚了整個(gè)濱河市铣减,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌脚作,老刑警劉巖葫哗,帶你破解...
    沈念sama閱讀 221,406評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異鳖枕,居然都是意外死亡魄梯,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門宾符,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)酿秸,“玉大人,你說(shuō)我怎么就攤上這事魏烫±彼眨” “怎么了?”我有些...
    開封第一講書人閱讀 167,815評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵哄褒,是天一觀的道長(zhǎng)稀蟋。 經(jīng)常有香客問(wèn)我,道長(zhǎng)呐赡,這世上最難降的妖魔是什么退客? 我笑而不...
    開封第一講書人閱讀 59,537評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮链嘀,結(jié)果婚禮上萌狂,老公的妹妹穿的比我還像新娘。我一直安慰自己怀泊,他們只是感情好茫藏,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,536評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著霹琼,像睡著了一般务傲。 火紅的嫁衣襯著肌膚如雪凉当。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,184評(píng)論 1 308
  • 那天售葡,我揣著相機(jī)與錄音看杭,去河邊找鬼。 笑死天通,一個(gè)胖子當(dāng)著我的面吹牛泊窘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播像寒,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼烘豹,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了诺祸?” 一聲冷哼從身側(cè)響起携悯,我...
    開封第一講書人閱讀 39,668評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎筷笨,沒(méi)想到半個(gè)月后憔鬼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,212評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡胃夏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,299評(píng)論 3 340
  • 正文 我和宋清朗相戀三年轴或,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仰禀。...
    茶點(diǎn)故事閱讀 40,438評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡照雁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出答恶,到底是詐尸還是另有隱情饺蚊,我是刑警寧澤,帶...
    沈念sama閱讀 36,128評(píng)論 5 349
  • 正文 年R本政府宣布悬嗓,位于F島的核電站污呼,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏包竹。R本人自食惡果不足惜燕酷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,807評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望周瞎。 院中可真熱鬧悟狱,春花似錦、人聲如沸堰氓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,279評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)双絮。三九已至浴麻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間囤攀,已是汗流浹背软免。 一陣腳步聲響...
    開封第一講書人閱讀 33,395評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留焚挠,地道東北人膏萧。 一個(gè)月前我還...
    沈念sama閱讀 48,827評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像蝌衔,于是被迫代替她去往敵國(guó)和親榛泛。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,446評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 22年12月更新:個(gè)人網(wǎng)站關(guān)停噩斟,如果仍舊對(duì)舊教程有興趣參考 Github 的markdown內(nèi)容[https://...
    tangyefei閱讀 35,187評(píng)論 22 257
  • 一曹锨、Python簡(jiǎn)介和環(huán)境搭建以及pip的安裝 4課時(shí)實(shí)驗(yàn)課主要內(nèi)容 【Python簡(jiǎn)介】: Python 是一個(gè)...
    _小老虎_閱讀 5,748評(píng)論 0 10
  • jHipster - 微服務(wù)搭建 CC_簡(jiǎn)書[http://www.reibang.com/u/be0d56c4...
    quanjj閱讀 816評(píng)論 0 2
  • 西部多天塹,陡壁云間斷剃允。 成昆斗士似飛仙沛简,左擷霓彩,右飲雪中霰斥废。 懸身一線天方好椒楣,不畏金沙險(xiǎn)。 索橋日夜冬夏牡肉,換得...
    冰熙舍人閱讀 653評(píng)論 2 12
  • 如此靜的天空捧灰,是不是活著的都進(jìn)入了冬眠 我靠著僵硬的墻站著,無(wú)奈地沉默 嗅著空氣中的夜的味道 仿佛見(jiàn)著一個(gè)人的路徑...
    木客的雨子閱讀 619評(píng)論 0 1