美文网首页菜鸟
Tornado实战-用户登录与注册

Tornado实战-用户登录与注册

作者: Python野路子 | 来源:发表于2018-08-02 00:13 被阅读2491次

    需要模块

    pip install redis
    pip install packet

    实现代码

    app.py

    import tornado.ioloop   #开启循环,让服务一直等待请求的到来
    import tornado.web      #web服务基本功能都封装在此模块中
    import tornado.options  #从命令行中读取设置
    from tornado.options import define,options  #导入包
    
    from handlers import main,auth
    
    define('port',default='8000',help='Listening port',type=int) #定义如何接受传进来的东西
    
    
    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), #命名组写法,使用关键字,路由与handler方法不一定顺序一致
                (r'/upload',main.UploadHandler),
                (r'/login',auth.LoginHandler),
                (r'/logout',auth.LogoutHandler),
                (r'/register',auth.RegisterHandler)
            ]
            settings = dict(
                debug = True, #调试模式,修改后自动重启服务,不需要自动重启,生产情况下切勿开启,安全性
                template_path='templates', #模板文件目录,想要Tornado能够正确的找到html文件,需要在 Application 中指定文件的位置
                static_path='static',  #静态文件目录,可用于用于访问js,css,图片之类的添加此配置之后,tornado就能自己找到静态文件
                login_url='/login', #没有登录则跳转至此
                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方法重新执行一遍,然后将handlers和settings传进去,完成初始化
    
    
    app = Application() #实例化
    
    
    if __name__ == '__main__':   #当.py文件被直接运行时,代码块将被运行;当.py文件以模块形式被导入时,代码块不被运行。
    
        tornado.options.parse_command_line()
        app.listen(options.port)  ##如果一个与define语句中同名的设置在命令行中被给出,那么它将成为全局的options的一个属性 即 options.port 相当于define的url的port
        print("Server start on port {}".format(str(options.port)))  #提示服务启动占用端口
        tornado.ioloop.IOLoop.current().start()   #执行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是一种会话状态,跟数据库的session可能不一样
    
    #添加装饰器,装饰需要验证的请求
    class IndexHandler(AuthBaseHandler):
        """
         Home page for user,photo feeds 主页----所关注的用户图片流
        """
        @tornado.web.authenticated   #@tornado.web.authenticated装饰器包裹get方法时,表示这个方法只有在用户合法时才会调用,authenticated装饰器会调用get_current_user()方法获取current_user的值,若值为False,则重定向到登录url装饰器判断有没有登录,如果没有则跳转到配置的路由下去,但是要在app.py里面设置login_url
        def get(self,*args,**kwargs):
            self.render('index.html')
    
    
    class ExploreHandler(AuthBaseHandler):
        """
        Explore page,photo of other users 发现页-----发现或最近上传的图片页面
        """
        @tornado.web.authenticated
        def get(self,*args,**kwargs):
            # image_urls = get_images("./static/uploads")  #打开指定路径下的文件,或者static/uploads
            os.chdir('static')  # 用于改变当前工作目录到指定的路径
            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  单个图片详情页面
        """
        @tornado.web.authenticated
        def get(self,post_id):
            print(post_id)
            self.render('post.html',post_id = post_id)   #根据正则输入的内容,接收到,打开相应的图片
    
    
    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)  #获取上传文件数据,返回文件列表
    
            for file_img in file_imgs: #可能同一个上传的文件会有多个文件,所以要用for循环去迭代它
                # filename 文件的实际名字,body 文件的数据实体;content_type 文件的类型。 这三个对象属性可以像字典一样支持关键字索引
                save_to = 'static/uploads/{}'.format(file_img['filename'])
                #以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。
                with open(save_to,'wb') as f: #二进制
                    f.write(file_img['body'])
                photo.make_thumb(save_to) #同时生成缩略图
    
            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('/') #那么直接跳转到主页
            else:
                nextname = self.get_argument('next','') #将原来的路由赋值给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) #将前面设置的cookie设置为username,保存用户登录信息
                next_url = self.get_argument('next', '')  # 获取之前页面的路由
    
                if next_url:
                    self.redirect(next_url) #跳转主页路由
                else:
                    self.redirect('/')
            else:
                self.write({'msg':'login fail'}) #不通过,有问题
    
    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'  #注意这个不是本地端口是指远程数据库端口,因为pycharm已经先SSH连接到本地了
    DATABASE = 'my_db'
    USERNAME = 'root'
    PASSWORD = '1q2w3e4r'
    
    db_url = 'mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8'.format(
        USERNAME,
        PASSWORD,
        HOSTNAME,
        PORT,
        DATABASE
    )
    
    
    #连接数据库
    engine = create_engine(db_url)
    
    Base = declarative_base(bind = engine)  #这个基类是维系类和数据表关系的目录。
    
    #在对表数据进行增删改查之前,先需要建立会话,建立会话之后才能进行操作,就类似于文件要打开之后才能对文件内容操作。
    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来算法加密,utf8不加的话就是默认utf8
    
        return text
    
    USER_DATA = {
        'name':'user',
        'password':hash('1q2w3e4r')
    }
    
    def authenticate(username,password):#用户密码匹配判断函数
        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}} <!--获取当前用户 -->
    {% for num in range(1,5) %}
    <a href="/post/{{num}}">
        <img src="{{static_url('images/{}.jpg'.format(num))}}" /> <!-- 使用此方法时,Tornado 会自动地给静态文件添加版本号,如果版本号更改了,浏览器会自动的缓存新的静态文件-->
    </a>
    
    {% end %}
    {% end %}
    
    image.png
    ![image.png](https://img.haomeiwen.com/i9286065/aa9c90ddb040bc5c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

    相关文章

      网友评论

        本文标题:Tornado实战-用户登录与注册

        本文链接:https://www.haomeiwen.com/subject/sxbqvftx.html