美文网首页
flask之改框架

flask之改框架

作者: 马梦里 | 来源:发表于2017-12-13 16:22 被阅读0次

    二、修改以前的框架

    1. server.py

    import socket
    import _thread
    
    from request import Request
    from utils import log
    
    from routes import error
    
    from routes.routes_todo import route_dict as todo_routes
    from routes.api_todo import route_dict as todo_api
    from routes.routes_weibo import route_dict as weibo_routes
    from routes.routes_user import route_dict as user_routes
    from routes.routes_static import route_dict as static_routes
    
    def response_for_path(request):
        r = {}
        r.update(todo_api())
        r.update(todo_routes())
        r.update(weibo_routes())
        r.update(user_routes())
        r.update(static_routes())
        response = r.get(request.path, error)
        return response(request)
    
    def process_request(connection):
        r = connection.recv(1024)
        r = r.decode()
        # 把原始请求数据传给 Request 对象
        request = Request(r)
        # 用 response_for_path 函数来得到 path 对应的响应内容
        response = response_for_path(request)
        if 'static' in request.path:
            log("response for static size: {}".format(len(response)))
        else:
            log("response log:\n{}".format(response.decode()))
        connection.sendall(response)
        connection.close()
    
    def run(host, port):
        # 初始化 socket 套路
        # 使用 with 可以保证程序中断的时候正确关闭 socket 释放占用的端口
        log('开始运行于', '{}:{}'.format(host, port))
        with socket.socket() as s:
            # 使用 下面这句 可以保证程序重启后使用原有端口, 原因忽略
            s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            s.bind((host, port))
            s.listen(5)
            while True:
                connection, address = s.accept()
                # 第二个参数类型必须是 tuple
                _thread.start_new_thread(process_request, (connection,))
    
    if __name__ == '__main__':
        config = dict(
            host='127.0.0.1',
            port=3000,
        )
        run(**config)
    

    改为:

    from flask import(
        Flask,
    )
    from routes.routes_user import user_view
    from routes.routes_static import static_view
    
    if __name__ == '__main__':
        app = Flask(__name__)
        app.register_blueprint(user_view)
        app.register_blueprint(static_view)
        config = dict(
            debug = True,
            host='127.0.0.1',
            port=8000,
        )
        app.run(**config)
    

    run函数已经封装好了,路由由Blueprint处理。这个脚本就两个功能,启动服务器和分发路由。
    知识点:

    • Blueprint 的使用,记住就好。

    2.主页视图

    from routes import current_user, login_required
    from routes import http_response
    from utils import template, log
    
    def route_index(request):
        u = current_user(request)
        log('current user', u)
        body = template('index.html', username=u.username)
        return http_response(body)
    
    def route_static(request):
        filename = request.query.get('file', )
        path = 'static/' + filename
        with open(path, 'rb') as f:
            header = b'HTTP/1.1 200 OK\r\n\r\n'
            binary = header + f.read()
            return binary
    
    def route_dict():
        r = {
            '/': login_required(route_index),
            '/static': login_required(route_static),
        }
        return r
    

    改为:

    from flask import (Blueprint,render_template,send_from_directory,request,)
    from routes import current_user, login_required
    from utils import log
    
    static_view = Blueprint('static_view', __name__)
    
    # 先处理路由函数,如果没注册,再重定向到登录界面
    @static_view.route('/')
    @login_required
    def route_index():
        u = current_user()
        return render_template('index.html', username=u.username)
    
    @static_view.route('/static')
    def route_static():
        filename = request.args.get('file', None)
        return send_from_directory('static/' , filename)
    

    知识点:

    • 权限函数在路由函数之下,原因是先找到路由,发现没登录,再执行权限函数
    • Blueprint 的使用
      -flask.send_from_directory(dictionary, filename)
      就是读取文件,两个参数,一个是路径,一个是文件名;
      current_user()函数也需要更改
    def current_user(request):
        session_id = request.cookies.get('sid', '')
        sessions = Session.all()
        for s in sessions:
            if s.session_id == session_id:
                u = User.find_by(id=s.user_id)
                return u
        return None
    
    # 改为:去掉request参数,在脚本调用flask.request,
    # cookies也是通过POST进行传递的,在body里面;
    
    def current_user():
        session_id = request.cookies.get('sid', '')
        sessions = Session.all()
        for s in sessions:
            if s.session_id == session_id:
                u = User.find_by(id=s.user_id)
                return u
        return None
    

    3.登录和注册视图:

    from models.session import Session
    from routes import (random_str,redirect,http_response)
    from utils import log
    from utils import template
    from models.user import User
    
    def route_login(request):
        log('login, cookies', request.cookies)
        if request.method == 'POST':
            form = request.form()
            u = User(form)
            if u.validate_login():
                session_id = random_str()
                u = User.find_by(username=u.username)
                s = Session.new(dict(
                    session_id=session_id,
                    user_id=u.id,
                ))
                log('session', s)
                headers = {
                    'Set-Cookie': 'sid={}'.format(session_id)
                }
                # 登录后定向到 /
                return redirect('/', headers)
        # 显示登录页面
        body = template('login.html')
        return http_response(body)
    
    def route_register(request):
        if request.method == 'POST':
            form = request.form()
            u = User.new(form)
            if u.validate_register():
                # 注册成功再保存
                u.save()
                # 注册成功后 定向到登录页面
                return redirect('/login')
            else:
                # 注册失败 定向到注册页面
                return redirect('/register')
        # 显示注册页面
        body = template('register.html')
        return http_response(body)
    
    def route_dict():
        r = {
            '/login': route_login,
            '/register': route_register,
        }
        return r
    
    

    改为:

    from models.session import Session
    from flask import (Blueprint,render_template,redirect,request,make_response,)
    from routes import random_str
    from utils import log
    from models.user import User
    
    user_view = Blueprint('user_view', __name__)
    
    @user_view.route('/login', methods = ['POST', 'GET'])
    def route_login():
        log('login, cookies', request.cookies)
        if request.method == 'POST':
            form = request.form
            u = User(form)
            if u.validate_login():
                session_id = random_str()
                u = User.find_by(username=u.username)
                s = Session.new(dict(
                    session_id=session_id,
                    user_id=u.id,
                ))
                log('session', s)
                # 登录后定向到 /
                redirect_to_index = redirect('/')
                response = make_response(redirect_to_index)
                response.set_cookie('sid', value=session_id)
                return response
        # 显示登录页面
        return render_template('login.html')
    
    @user_view.route('/register', methods = ['POST', 'GET'])
    def route_register():
        if request.method == 'POST':
            form = request.form
            u = User.new(form)
            if u.validate_register():
                # 注册成功再保存
                u.save()
                # 注册成功后 定向到登录页面
                return redirect('/login')
            else:
                # 注册失败 定向到注册页面
                return redirect('/register')
        # 显示注册页面
        return render_template('register.html')
    
    1. request为全局变量,form为属性。template改为render_template()
    2. 主要是cookies设置的改变
    redirect_to_index = redirect('/')
        response = make_response(redirect_to_index)
        response.set_cookie('sid', value=session_id)
        return response
    

    做出了两个响应,一个是重定向到index页面,一个是响应一个cookies
    在登录成功之后,不仅要返回重定向,还要返回cookies

    1. 但是返回重定向为什么要用make_response()呢?
      因为return的内容,浏览器会默认为body,而cookies储存在header中。但是我们没有办法更改响应结构,就只能去创建一个请求结构了。构造一个response需要基于某种情况,这种是重定向,还可以是渲染页面(response = make_response(render_template('','')))。
    2. cookies的再理解:
      cookiescookies是保存在客户端的,当用户发送登录信息给服务器的时候,服务器将用户名存在请求头发送给浏览器,这样,浏览器以后的请求中都包含了用户名这个字段,服务器就可以通过这个字段辨别用户了。
      sessionssessions是保存在服务器的,在服务器端生成的,是依赖于cookies的,只不过在cookies的外面套上了一件衣服,将重要信息掩盖。客户将登陆信息发送给服务器,服务器生成一个随机字符串赋值于session_id,构架一个session_id和用户名两个字段的字典,一一对应,并将session_id发送给客户端。这样服务器就可以通过客户端发过来的session_id来辨别用户了。
      客户端每次登陆,都会生成一个随机字符串,服务器和客户端通过这个进行识别。下次相同用户登陆的时候,又会生成其他随机字符串。
    login_required也需要修改:
    def login_required(route_function):
        """
        def f(request):
            u = current_user(request)
            if u is None:
                return redirect('/login')
            else:
                return route_function(request)
        return f
    

    改为:

    def login_required(route_function):
        def f():
            u = current_user()
            if u is None:
                return redirect('/login')
            else:
                return route_function()
        return f
    

    4.todo首页视图

    from models.todo import Todo
    from routes import (
        redirect,
        http_response,
        current_user,
        login_required,
    )
    from utils import template, log
    
    def index(request):
        u = current_user(request)
        body = template('todo_index.html')
        return http_response(body)
    
    def route_dict():
        d = {
            '/todo/index': login_required(index),
        }
        return d
    

    改为:

    from flask import (render_template, Blueprint,)
    from routes import login_required
    todo_view = Blueprint('todo_vies', __name__)
    @todo_view.route('/todo/index')
    @login_required
    def index():
        return render_template('todo_index.html')
    

    5.api_todo

    from routes import json_response
    from models.todo import Todo
    
    # 本文件只返回 json 格式的数据
    # 而不是 html 格式的数据
    def all(request):
        todos = Todo.all_json()
        return json_response(todos)
    
    def add(request):
        # 得到浏览器发送的表单, 浏览器用 ajax 发送 json 格式的数据过来
        # 所以这里我们用新增加的 json 函数来获取格式化后的 json 数据
        form = request.json()
        # 创建一个 todo
        t = Todo.new(form)
        # 把创建好的 todo 返回给浏览器
        return json_response(t.json())
    
    def delete(request):
        todo_id = int(request.query.get('id'))
        t = Todo.delete(todo_id)
        return json_response(t.json())
    
    def update(request):
        form = request.json()
        log('api todo update', form)
        todo_id = int(form.get('id'))
        t = Todo.update(todo_id, form)
        return json_response(t.json())
    
    def route_dict():
        d = {
            '/api/todo/all': all,
            '/api/todo/add': add,
            '/api/todo/delete': delete,
            '/api/todo/update': update,
        }
        return d
    
    

    改为

    from flask import (request, Blueprint, jsonify)
    from routes import login_required
    from utils import log
    from models.todo import Todo
    
    api_todo_view = Blueprint('api_todo_view', __name__)
    
    # 本文件只返回 json 格式的数据
    # 而不是 html 格式的数据
    @api_todo_view.route('/api/todo/all')
    @login_required
    def api_all():
        todos = Todo.all_json()
        return jsonify(todos)
    
    @api_todo_view.route('/api/todo/add', methods = ['POST'])
    @login_required
    def api_add():
        # 得到浏览器发送的表单, 浏览器用 ajax 发送 json 格式的数据过来
        # 所以这里我们用新增加的 json 函数来获取格式化后的 json 数据
        form = request.json
        # 创建一个 todo
        t = Todo.new(form)
        # 把创建好的 todo 返回给浏览器
        return jsonify(t.json())
    
    @api_todo_view.route('/api/todo/delete')
    @login_required
    def api_delete():
        todo_id = int(request.args.get('id', None))
        t = Todo.delete(todo_id)
        return jsonify(t.json())
    
    @api_todo_view.route('/api/todo/update', methods = ['POST'])
    @login_required
    def api_update():
        form = request.json
        log('api todo update', form)
        todo_id = int(form.get('id'))
        t = Todo.update(todo_id, form)
        return jsonify(t.json())
    

    login_required()函数:

    from functools import wraps
    
    def login_required(route_function):
        @wraps(route_function)
        def f():
            u = current_user()
            if u is None:
                log('非登录用户')
                return redirect('/login')
            else:
                return route_function()
    
        return f
    
    1. 自己写的weibo程序
    # 用于评论的更新:评论的user_id与登录的id一直
    def comment_same_user_required(route_function):
        @wraps(route_function)
        def f():
            u = current_user()
            if request.method == 'GET':
                comment_id = int(request.args.get('id', None))
            else:
                form = request.json
                comment_id = form.get('id', None)
            c = Comment.find(int(comment_id))
            if u.id == c.user_id:
                log('权限用户')
                return route_function()
            else:
                return redirect('/webo/index')
        return f
    

    这里注意一点,由于ajax发过来的数据是json格式,所以form数据需要通过request.json来接收,二GET请求发过来的数据在url里面,不是json格式,所以可以直接取。ajax(method,path,form,callback)

    三个知识点:

    1. request.json
    2. jsonify()
    3. from functools import wraps @wraps(参数为母函数参数)
      http://www.w3school.com.cn/json/
      http://blog.csdn.net/hqzxsc2006/article/details/50337865
      https://www.tuicool.com/articles/NzEbqmj

    【13-2(全)】

    相关文章

      网友评论

          本文标题:flask之改框架

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