Flask

作者: 文化银儿 | 来源:发表于2018-10-09 19:30 被阅读215次

    flask使用操作指南1


    1. flask介绍

    Flask是一个基于Python实现的web开发的'微'框架

    中文文档地址

    Flask和Django一样,也是一个基于MVC设计模式的Web框架

    flask流行的主要原因:

    a)有非常齐全的官方文档,上手非常方便
    
    b) 有非常好的拓展机制和第三方的拓展环境,工作中常见的软件都有对应的拓展,自己动手实现拓展也很容易
    
    c) 微型框架的形式给了开发者更大的选择空间
    

    2. 安装flask

    2.1虚拟环境搭建

    virtualenv --no-site-packages falskenv
      
    激活windows下虚拟环境
    cd Scripts
    activate
    

    2.2 安装

    pip install flask
    

    3. 基于flask的最小的应用

    创建hello.py文件

    from flask import Flask
    
    app = Flask(__name__)
    
    @app.route('/')
    def gello_world():
        return 'Hello World'
    
    if __name__ == '__main__':
    
        app.run()
    

    运行:python hello.py

    3.1 初始化

    from flask import Flask
    
    app = Flask(__name__)
    

    Flask类构造函数唯一需要的参数就是应用程序的主模块或包。对于大多数应用程序,Python的__name__变量就是那个正确的、你需要传递的值。Flask使用这个参数来确定应用程序的根目录,这样以后可以相对这个路径来找到资源文件。

    3.2 路由

    @app.route('/')
    

    客户端例如web浏览器发送 请求 给web服务,进而将它们发送给Flask应用程序实例。应用程序实例需要知道对于各个URL请求需要运行哪些代码,所以它给Python函数建立了一个URLs映射。这些在URL和函数之间建立联系的操作被称之为 路由 。

    在Flask应程序中定义路由的最便捷的方式是通过显示定义在应用程序实例之上的app.route装饰器,注册被装饰的函数来作为一个路由

    3.3 视图函数

    在上一个示例给应用程序的根URL注册gello_world()函数作为事件的处理程序。如果这个应用程序被部署在服务器上并绑定了 www.example.com 域名,然后在你的浏览器地址栏中输入 http://www.example.com 将触发gello_world()来运行服务。客户端接收到的这个函数的返回值被称为 响应 。如果客户端是web浏览器,响应则是显示给用户的文档。

    类似于gello_world()的函数被称作 视图函数

    3.4 动态名称组件路由

    你的Facebook个人信息页的URL是 http://www.facebook.com/<username> ,所以你的用户名是它的一部分。Flask在路由装饰器中使用特殊的语法支持这些类型的URLs。下面的示例定义了一个拥有动态名称组件的路由:

    @app.route('/hello/<name>')
    
    def gello_world(name):
    
        return 'Hello World %s' % name
    

    用尖括号括起来的部分是动态的部分,所以任何URLs匹配到静态部分都将映射到这个路由。当视图函数被调用,Flask发送动态组件作为一个参数。在前面的示例的视图函数中,这个参数是用于生成一个个性的问候作为响应。

    在路由中动态组件默认为字符串,但是可以定义为其他类型。例如,路由/user/<int:id>只匹配有一个整数在id动态段的URLs。Flask路由支持int、float、string、path

    路由匹配的规则
    1.<id> :默认接受的类型是str
    2.<string:id> :指定id的类型为str
    3.<int:id> :指定的id类型是整性
    4.<float:id> : 指定id的类型为浮点数(四舍五入,且不能接收整数类型)
    5.<path:path1> : 指定接收的path为url中的路径

    如下:

      
    @blue.route('/get_id/<id>/')
    def get_id(id):
        #匹配str类型的id值
        return 'id: %s' % id
    
    
    @blue.route('/get_int_id/<int:id>/')
    def get_int_id(id):
        #匹配int类型的id值
        return 'id:%d' % id
    
    
    @blue.route('/get_float/<float:uid>/')
    def get_float(uid):
        #匹配float类型的值,不能匹配int类型(四舍五入)
        return 'uid:%.2f' %uid
    
    
    @blue.route('/get_path/<path:upath>/')
    def get_path(upath):
        #匹配url路径
        return 'path:%s' % upath
    
     
    

    3.5 服务启动

    if __name__ == '__main__':
    
        app.run()
    

    注意: __name__ == '__main__'在此处使用是用于确保web服务已经启动当脚本被立即执行。当脚本被另一个脚本导入,它被看做父脚本将启动不同的服务,所以app.run()调用会被跳过。

    一旦服务启动,它将进入循环等待请求并为之服务。这个循环持续到应用程序停止,例如通过按下Ctrl-C。

    有几个选项参数可以给app.run()配置web服务的操作模式。在开发期间,可以很方便的开启debug模式,将激活 debugger 和 reloader 。这样做是通过传递debug为True来实现的。

    app.run(host='0.0.0.0',port=8080,debug=True)
    

    run()中参数有如下:

    debug 是否开启调试模式,开启后修改python的代码会自动重启
    
    port 启动指定服务器的端口号
    
    host主机,默认是127.0.0.1
    

    4. 修改启动方式,使用命令行参数启动服务

    4.1 安装插件

    pip install flask-script
    

    调整代码
    manager = Manager(app=‘自定义的flask对象’)

    启动的地方
    manager.run()

    4.2 启动命令

    python hellow.py runserver -h 地址 -p 端口 -d -r
    

    其中:-h表示地址。-p表示端口。-d表示debug模式。-r表示自动重启

    如:
    manager.run(python xx.py runserver -h 0.0.0.0 -p 8080 -d)
    

    5. route规则

    5.1 规则

    写法:<converter:variable_name>

    converter类型:

    string 字符串
    int 整形
    float 浮点型
    path 接受路径,接收的时候是str,/也当做字符串的一个字符
    uuid 只接受uuid字符串
    any 可以同时指定多种路径,进行限定
    

    例子:

    @app.route('/helloint/<int:id>/')
    
    @app.route('/getfloat/<float:price>/')
    
    @app.route('/getstr/<string:name>/',methods=['GET', 'POST'])
    
    @app.route('/getpath/<path:url_path>/')
    
    @app.route('/getbyuuid/<uuid:uu>/',methods=['GET', 'POST'])
    

    实现对应的视图函数:

    @blue.route('/hello/<name>/')
    def hello_man(name):
        print(type(name))
        return 'hello name:%s type:%s' % (name, type(name))
    
    
    @blue.route('/helloint/<int:id>/')
    def hello_int(id):
        print(id)
        print(type(id))
        return 'hello int: %s' % (id)
    
    
    @blue.route('/index/')
    def index():
        return render_template('hello.html')
    
    
    @blue.route('/getfloat/<float:price>/')
    def hello_float(price):
        return 'float: %s' % price
    
    
    @blue.route('/getstr/<string:name>/')
    def hello_name(name):
        return 'hello name: %s' % name
    
    
    @blue.route('/getpath/<path:url_path>/')
    def hello_path(url_path):
        return 'path: %s' % url_path
    
    
    @blue.route('/getuuid/')
    def gello_get_uuid():
        a = uuid.uuid4()
        return str(a)
    
    
    @blue.route('/getbyuuid/<uuid:uu>/')
    def hello_uuid(uu):
        return 'uu:%s' % uu
    

    5.2 methods请求方法

    常用的请求类型有如下几种

    GET : 获取
    POST : 创建
    PUT : 修改(全部属性都修改)
    DELETE : 删除
    PATCH : 修改(修改部分属性)
    

    定义url的请求类型:

    @blue.route('/getrequest/', methods=['GET', 'POST'])
    

    flask使用操作指南2


    1. 什么是蓝图

    在Flask项目中可以用Blueprint(蓝图)实现模块化的应用,使用蓝图可以让应用层次更清晰,开发者更容易去维护和开发项目。蓝图将作用于相同的URL前缀的请求地址,将具有相同前缀的请求都放在一个模块中,这样查找问题,一看路由就很快的可以找到对应的视图,并解决问题了。

    2. 使用蓝图

    2.1 安装

    pip install flask_blueprint
    

    2.2 实例化蓝图应用

    blue = Blueprint(‘first’,_ _name_ _)
    

    注意:Blueprint中传入了两个参数,第一个是蓝图的名称,第二个是蓝图所在的包或模块,_ name _代表当前模块名或者包名

    2.3 注册

    app = Flask(_ _name_ _)
    
    app.register_blueprint(blue, url_prefix='/user')
    

    注意:第一个参数即我们定义初始化定义的蓝图对象,第二个参数url_prefix表示该蓝图下,所有的url请求必须以/user开始。这样对一个模块的url可以很好的进行统一管理

    3 使用蓝图

    修改视图上的装饰器,修改为@blue.router(‘/’)

    @blue.route('/', methods=['GET', 'POST'])
    def hello():
        # 视图函数
        return 'Hello World'
    

    注意:该方法对应的url为127.0.0.1:5000/user/

    4 url_for反向解析

    语法:

    url_for('蓝图中定义的第一个参数.函数名', 参数名=value)
    

    定义跳转:

    from flask import url_for, redirect
    
    @blue.route('/redirect/')
    def make_redirect():
        # 第一种方法
        return redirect('/hello/index/')
        # 第二种方法
        return redirect(url_for('first.index'))
    

    flask使用操作指南3


    1. 请求request

    服务端在接收到客户端的请求后,会自动创建Request对象

    由Flask框架创建,Requesy对象不可修改

    属性:

    url:完整的请求地址
    
    base_url:去掉GET参数的url
    
    host_url:只有主机和端口号的url
    
    path:路由中的路径
    
    method:请求方法
    
    remote_addr:请求的客户端的地址
    
    args:GET请求参数
    
    form:POST请求参数
    
    files:文件上传
    
    headers:请求头
    
    cookies:请求中的cookie
    

    1.1 args-->GET请求参数包装

    a)args是get请求参数的包装,args是一个ImmutableMultiDict对象,类字典结构对象

    b)数据存储也是key-value

    1.2 form-->POST请求参数包装

    a)form是post请求参数的包装,args是一个ImmutableMultiDict对象,类字典结构对象

    b)数据存储也是key-value

    重点:ImmutableMultiDict是类似字典的数据结构,但是与字典的区别是,<font style="color:red; font-weight:bold;">可以存在相同的键</font>。

    在ImmutableMultiDict中获取数据的方式,dict['key']或者dict.get('key')或者dict.getlist('key')

    image.png

    2. 响应Response

    Response是由开发者自己创建的

    创建方法:

    from flask import make_response
    
    make_response创建一个响应,是一个真正的Response对象
    

    状态码:

    格式:make_reponse(data,code),其中data是返回的数据内容,code是状态码

    a)直接将内容当做make_response的第一个参数,第二个参数直接写返回的状态码
    
    b)直接在render后加返回的状态码
    

    例子1:

    定义一个获取GET请求的request的方法,并将返回成功的请求的状态码修改为200

    @blue.route('/getrequest/', methods=['GET'])
    def get_request():
    
        print(request)
    
        return '获取request', 200
    

    例子2:

    返回response响应,并添加返回结果的状态码200

    @blue.route('/getresponse/')
    def get_response():
        response = make_response('<h2>我是响应</h2>', 500)
        return response
    

    或者:

    @blue.route('/getresponse/', methods=['GET'])
    def get_user_response():
        login_html = render_template('login.html')
        res = make_response(login_html, 200)
        return res
    

    3. 重定向/反向解析

    url_for('蓝图定义的名称.方法名')
    

    例子1:

    定义跳转方法,跳转到get_response的方法上

    @blue.route('/getredirect/')
    def get_redirect():
    
        return redirect('getresponse')
    

    例子2:

    使用url_for反向解析

    from flask import redirect, url_for
    
    @blue.route('/getredirect/')
    def get_redirect():
    
        return redirect(url_for('first.get_response'))
    

    4. 终止/异常捕获

    自动抛出异常:abort(状态码)

    捕获异常处理:errorhandler(状态码),定义的函数中要包含一个参数,用于接收异常信息

    4.1 定义终止程序

    @blue.route('/make_abort/')
    def get_abort():
        abort(400)
        return '终止'
    

    4.2 捕获定义的异常

    @blue.errorhandler(400)
    def handler(exception):
    
        return '捕获到异常信息:%s' % exception
    

    flask使用操作指南之session/cookie


    前言

    访问者的标识问题服务器需要识别来自同一访问者的请求。这主要是通过浏览器的cookie实现的。 访问者在第一次访问服务器时,服务器在其cookie中设置一个唯一的ID号——会话ID(session)。 这样,访问者后续对服务器的访问头中将自动包含该信息,服务器通过这个ID号,即可区 隔不同的访问者。

    1. Cookie

    概念:

    a)客户端会话技术,浏览器的会话技术
    
    b)数据全部存储在客户端中
    
    c)存储使用的键值对结构进行存储
    
    特性:
        支持过期时间
        默认会自动携带本网站的cookie
        不能跨域名
        不能跨浏览器
    

    创建:

    Cookie是通过服务器创建的Response来创建的
    
    设置:set_cookie('key', value, max_ages='', expires='')
    
    删除, 有三种删除方式
        
        1. 直接清空浏览器的cookie
        2. delete_cookie('key') 直接使用delete_cookie函数
        3. set_cookie('key','',expires=0) 重新设置key的值为空,过期时间为0
    

    获取:

    在每次请求中,url都会向服务器传递Request,在request中可以获取到cookie的信息
    
    request.cookies.get('name')
    

    例子1,设置cookie:

    import datetime
    
    @blue.route('/setcookie/')
    def set_cookie():
        temp = render_template('index.html')
        response = make_response(temp)
        outdate=datetime.datetime.today() + datetime.timedelta(days=30)
        # 设置cookie中的name的存在时长,设置为30天才过期  
        response.set_cookie('name','cocoococo',expires=outdate)
        return response
    

    例子2,删除cookie中的值

    @blue.route('/setcookie/')
    def set_cookie():
        temp = render_template('index.html')
        response = make_response(temp)
        # 第一种方式,通过set_cookie去删除
        response.set_cookie('name','',expires=0)
        # 第二种方式,del_cookie删除
        response.del_cookie('name')
        return response
    

    例子3,获取cookie中的值

    @blue.route('/getcookie/')  
    def get_cookie():
        name=request.cookies.get('name')  
        return name
    

    2. Session

    flask-session是flask框架的session组件

    该组件则将支持session保存到多个地方

    如:

    redis:保存数据的一种工具,五大类型。非关系型数据库
    
    memcached
    
    mongodb
    
    sqlalchmey:那数据存到数据库表里面
    

    2.1 安装

    pip install flask-session
    

    如果指定存session的类型为redis的话,需要安装redis

    pip install redis
    

    2.2 语法

    设置session:

    session['key'] = value
    

    读取session:

    result = session['key'] :如果内容不存在,将会报异常
    
    result = session.get('key') :如果内容不存在,将返回None
    

    删除session:

    session.pop('key')
    

    清空session中所有数据:

    session.clear()
    

    2.2 使用

    我们在初始化文件中创建一个方法,通过调用该方法来获取到Flask的app对象

    def create_app():
        app = Flask(__name__)
        # SECRET_KEY 秘钥
        app.config['SECRET_KEY'] = 'secret_key'
        # session类型为redis
        app.config['SESSION_TYPE'] = 'redis'
        # 添加前缀
        app.config['SESSION_KEY_PREFIX'] = 'flask'
        
        # 加载app的第一种方式
        se = Session()
        se.init_app(app=app)
        #加载app的第二种方式
        Session(app=app)
        app.register_blueprint(blueprint=blue)
    
        return app
    

    2.3 案例

    定义一个登陆的方法,post请求获取到username,直接写入到redis中,并且在页面中展示出redis中的username

    a)需要先启动redis,开启redis-server,使用redis-cli进入客户端

    b)定义方法

    @blue.route('/login/', methods=['GET', 'POST'])
    def login():
        if request.method == 'GET':
            username = session.get('username')
            return render_template('login.html', username=username)
        else:
            username = request.form.get('username')
            session['username'] = username
    
            return redirect(url_for('first.login'))
    

    c)定义模板

    <body>
    <h3>欢迎:{{ username }}</h3>
    <form action="" method="POST">
        用户名:<input type="text" name="username" placeholder="请输入你的名字">
        <input type="submit" value="提交">
    </form>
    </body>
    

    d)redis中数据


    image.png

    注意:我们在定义app.config的时候指定了SESSION_KEY_PREFIX为flask,表示存在session中的key都会加一个前缀名flask

    e) cookie和session的联系

    image.png

    访问者在第一次访问服务器时,服务器在其cookie中设置一个唯一的ID号——会话ID(session)。 这样,访问者后续对服务器的访问头中将自动包含该信息,服务器通过这个ID号,即可区 隔不同的访问者。然后根据不同的访问者来获取其中保存的value值信息。

    flask使用操作指南之session实现登录验证


    功能描述

    使用session实现用户的模拟登陆功能。在前面已经说过了,在用户第一次访问服务端的时候,在服务端的redis中会创建一个session值,在客户端浏览器的cookies中也会创建一个session的值。该cookies中的session值和redis中的session值是一样的,那么在往后的访问操作中,请求request都会传递给后端,后端在获取到request的时候,其实就是获取到了request.cookies中的session的值了,那么就可以做登录的校验了。校验功能如下:

    素材地址

    1. 前端login.html页面

    登录页面就两个输入框,分别接收用户名和密码

      <dd class="user_icon">
       <input type="text" name="username" placeholder="账号" class="login_txtbx"/>
      </dd>
      <dd class="pwd_icon">
       <input type="password" name="password" placeholder="密码" class="login_txtbx"/>
      </dd>
    

    2. 后端方法

    模拟用户的登录,直接判断用户的名称为妲己以及密码为123123.如果验证成功,就向session中保存用户的id值。如果没有登录成功的话,那就对session不做任何的处理,直接跳转到登录页面上去。

    @app_blue.route('/new_login/', methods=['GET', 'POST'])
    def new_login():
        if request.method == 'GET':
            return render_template('login.html')
        else:
            username = request.form.get('username')
            password = request.form.get('password')
            # 数据库校验,用户密码是否正确
            if username == '妲己' and password == '123123':
                session['user_id'] = 1
                return redirect((url_for('first.index')))
            else:
                return redirect(url_for('first.new_login'))
    


    @app_blue.route('/index/', methods=['GET'])
    def index():
    return render_template('index.html')

    3. 装饰器

    使用装饰器去装饰我们的index()函数,如果用户登录了,则session中有user_id的key,如果没有登录的话,session中是没有user_id的key的。那么验证用户是否登录了,其实就是验证session的user_id

    def is_login(func):
        @wraps(func)
        def check_login(*args, **kwargs):
            if 'user_id' in session:
                return func(*args, **kwargs)
            else:
                return redirect(url_for('first.new_login'))
        return check_login
    

    4. 修改index()函数,使用装饰器装饰

    @app_blue.route('/index/', methods=['GET'])
    @is_login
    def index():
        return render_template('index.html')
    

    flask使用操作指南之模板


    1. jinja2

    Flask中使用jinja2模板引擎

    jinja2是由Flask作者开发,模仿Django的模板引擎

    优点:

    速度快,被广泛使用
    
    HTML设计和后端python分离
    
    非常灵活,快速和安全
    
    提供了控制,继承等高级功能
    

    2. 模板语法

    2.1 模板语法主要分为两种:变量和标签

    模板中的变量:{{ var }}

    视图传递给模板的数据
    
    前面定义出来的数据
    
    变量不存在,默认忽略
    

    模板中的标签:{% tag %}

    控制逻辑
    
    使用外部表达式
    
    创建变量
    
    宏定义
    

    2.2 结构标签:

    block

    {% block xxx %}
    
    {% endblock %}
    
    块操作
        父模板挖坑,子模板填坑
    

    extends

    {% extends ‘xxx.html’ %}
    
    继承以后保留块中的内容
    {{ super() }}
    

    挖坑继承体现的化整为零的操作

    macro

    {% macro hello(name) %}
    
        {{ name }}
    
    {% endmacro %}
    
    宏定义,可以在模板中定义函数,在其他地方调用
    

    宏定义可导入

    {% from 'xxx' import xxx %}
    

    例子1:

    在index.html中定义macro标签,定义一个方法,然后去调用方法,结果是展示商品的id和商品名称

    {% macro show_goods(id, name) %}
        商品id:{{ id }}
        商品名称:{{ name }}
    {% endmacro %}
    
    {{ show_goods('1', '娃哈哈') }}
    <br>
    {{ show_goods('2', '雪碧') }}
    

    例子2:

    在index.html页面中定义一个say()方法,然后解析该方法:

    {% macro say() %}
    
        <h3>今天天气气温回升</h3>
        <h3>适合去游泳</h3>
        <h3>适合去郊游</h3>
    
    {% endmacro %}
    
    {{ say() }}
    

    例子3:

    定义一个function.html中定义一个方法:

    {% macro create_user(name) %}
        创建了一个用户:{{ name }}
    {% endmacro %}
    

    在index.html中引入function.html中定义的方法

    {% from 'functions.html' import create_user %}
    
    {{ create_user('小花') }}
    

    2.3 循环

    {% for item in cols %}
    
        aa
    
    {% else %}
    
        bb
    
    {% endfor %}
    

    也可以获取循环信息loop

    loop.first
    
    loop.last
    
    loop.index
    
    loop.revindex
    

    例子:

    在视图中定义一个视图函数:

    @stu.route('/scores/')
    def scores():
    
        scores_list = [21,34,32,67,89,43,22,13]
    
        content_h2 = '<h2>今天你们真帅</h2>'
        content_h3 = '   <h3>今天你们真帅</h3>   '
    
        return render_template('scores.html',
                               scores=scores_list,
                               content_h2=content_h2,
                               content_h3=content_h3)
    

    (该视图函数,在下面讲解的过滤器中任然使用其返回的content_h2等参数)

    首先: 在页面中进行解析scores的列表。题目要求:第一个成绩展示为红色,最后一个成绩展示为绿色,其他的不变

    <ul>
       {% for score in scores %}
            {% if loop.first %}
                <li style="color:red;">{{ loop.revindex }}:{{ loop.index }}:{{ score }}</li>
            {% elif loop.last %}
                <li style="color:green;">{{ loop.revindex }}:{{ loop.index }}:{{ score }}</li>
            {% else %}
                <li> {{ loop.revindex }}:{{ loop.index }}:{{ score }}</li>
            {% endif %}
        {% endfor %}
    </ul>
    

    2.4 过滤器

    语法:

    {{ 变量|过滤器|过滤器... }}
    

    capitalize 单词首字母大写

    lower 单词变为小写

    upper 单词变为大写

    title

    trim 去掉字符串的前后的空格

    reverse 单词反转

    format

    striptags 渲染之前,将值中标签去掉

    safe 讲样式渲染到页面中

    default

    last 最后一个字母

    first

    length

    sum

    sort

    例子:

    <ul>
        <li>{{ content_h2 }}</li>
        <li>{{ content_h2|safe }}</li>
        <li>{{ content_h2|striptags }}</li>
    
        <li>{{ content_h3 }}</li>
        <li>{{ content_h3|length }}</li>
        <li>{{ content_h3|trim|safe }}</li>
        <li>{{ content_h3|trim|length }}</li>
    </ul>
    

    3. 定义模板

    3.1 定义基础模板base.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>
            {% block title %}
            {% endblock %}
        </title>
        <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
    
        {% block extCSS %}
        {% endblock %}
    </head>
    <body>
    
    {% block header %}
    {% endblock %}
    
    {% block content%}
    {% endblock %}
    
    {% block footer%}
    {% endblock %}
    
    {% block extJS %}
    {% endblock %}
    
    </body>
    </html>
    

    3.2 定义基础模板base_main.html

    {% extends 'base.html' %}
    
    {% block extCSS %}
        <link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}">
    {% endblock %}
    

    4. 静态文件信息配置

    <b>django</b>:

    第一种方式:

    {% load static %}
    <link rel="stylesheet" href="{% static 'css/index.css' %}">
    

    第二种方式:

    <link rel="stylesheet" href="/static/css/index.css">
    

    <b>flask</b>:

    第一种方式:

    <link rel="stylesheet" href="/static/css/index.css">
    

    第二种方式:

    <link rel="stylesheet" href="{{ url_for('static', filename='css/index.css') }}">
    

    flask使用操作指南之模型1


    1. Flask模型

    Flask默认并没有提供任何数据库操作的API

    我们可以选择任何适合自己项目的数据库来使用

    Flask中可以自己的选择数据,用原生语句实现功能,也可以选择ORM(SQLAlchemy,MongoEngine)

    SQLAlchemy是一个很强大的关系型数据库框架,支持多种数据库后台。SQLAlchemy提供了高层ORM,也提供了使用数据库原生SQL的低层功能。

    ORM:

    将对对象的操作转换为原生SQL
    优点
        易用性,可以有效减少重复SQL
        性能损耗少
        设计灵活,可以轻松实现复杂查询
        移植性好
    

    针对于Flask的支持,官网地址

    pip install flask-sqlalchemy
    

    安装驱动

    pip install pymysql
    

    2. 定义模型

    使用SQLALchemy的对象去创建字段

    其中tablename指定创建的数据库的名称

    创建models.py文件,其中定义模型
    
    from flask_sqlalchemy import SQLAlchemy
    
    db = SQLAlchemy()
    


    class Student(db.Model):

        s_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        s_name = db.Column(db.String(16), unique=True)
        s_age = db.Column(db.Integer, default=1)
    
        __tablename__ = "student"
    

    其中:

    Integer表示创建的s_id字段的类型为整形,

    primary_key表示是否为主键

    String表示该字段为字符串

    unique表示该字段唯一

    default表示默认值

    autoincrement表示是否自增

    3. 创建数据表

    在视图函数中我们引入models.py中定义的db

    from App.models import db
    
    @blue.route("/createdb/")
    def create_db():
        db.create_all()
        return "创建成功"
    
    @blue.route('/dropdb/')
    def drop_db():
        db.drop_all()
        return '删除成功'
    

    其中: db.create_all()表示创建定义模型中对应到数据库中的表

    db.drop_all()表示删除数据库中的所有的表

    4. 初始化SQLALchemy

    在定义的init.py文件中使用SQLALchemy去整合一个或多个Flask的应用

    有两种方式:

    第一种:
    
    from flask_sqlalchemy import SQLALchemy
    
    app = Flask(__name__)
    db = SQLAlchemy(app)
    
    第二种:
    
    from App.models import db
    
    def create_app():
        app = Flask(__name__)
        db.init_app(app)
        return app
    

    5. 配置数据库的访问地址

    官网配置参数

    数据库连接的格式:

    dialect+driver://username:password@host:port/database
    
    dialect数据库实现
    
    driver数据库的驱动
    

    例子:
    访问mysql数据库,驱动为pymysql,用户为root,密码为123456,数据库的地址为本地,端口为3306,数据库名称HelloFlask

    设置如下: "mysql+pymysql://root:123456@localhost:3306/HelloFlask"

    在初始化init.py文件中如下配置:

    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
    
    app.config['SQLALCHEMY_DATABASE_URI'] = "mysql+pymysql://root:123456@localhost:3306/HelloFlask"
    

    6. 对学生数据进行CRUD操作

    语法:

    类名.query.xxx
    

    获取查询集:

    all()
    
    filter(类名.属性名==xxx)
    
    filter_by(属性名=xxx)
    

    数据操作:

    在事务中处理,数据插入
    
    db.session.add(object)
    
    db.session.add_all(list[object])
    
    db.session.delete(object)
    
    db.session.commit()
    
    修改和删除基于查询
    

    6.1 想学生表中添加数据

    @blue.route('/createstu/')
    def create_stu():
    
        s = Student()
        s.s_name = '小花%d' % random.randrange(100)
        s.s_age = '%d' % random.randrange(30)
    
        db.session.add(s)
        db.session.commit()
    
        return '添加成功'
    

    提交事务,使用commit提交我们的添加数据的操作

    6.2 获取所有学生信息

    将学生的全部信息获取到,并且返回给页面,在页面中使用for循环去解析即可

    @blue.route("/getstudents/")
    def get_students():
        students = Student.query.all()
        return render_template("StudentList.html", students=students)
    

    6.3 获取s_id=1的学生的信息

    写法1:

    students = Student.query.filter(Student.s_id==1)
    

    写法2:

    students = Student.query.filter_by(s_id=2)
    

    注意:filter中可以接多个过滤条件

    写法3:

    sql = 'select * from student where s_id=1'
    students = db.session.execute(sql)
    

    6.4 修改学生的信息

    写法1:

    students = Student.query.filter_by(s_id=3).first()
    students.s_name = '哈哈'
    db.session.commit()
    

    写法2:

    Student.query.filter_by(s_id=3).update({'s_name':'娃哈哈'})
     
    db.session.commit()
    

    6.5 删除一个学生的信息

    写法1:

    students = Student.query.filter_by(s_id=2).first()
    db.session.delete(students)
    db.session.commit()
    

    写法2:

    students = Student.query.filter_by(s_id=1).all()
    db.session.delete(students[0])
    db.session.commit()
    

    注意:filter_by后的结果是一个list的结果集

    <b>重点注意:在增删改中如果不commit的话,数据库中的数据并不会更新,只会修改本地缓存中的数据,所以一定需要db.session.commit()</b>

    flask使用操作指南之模型2


    1. 深入数据库增删改查

    定义模型,并定义初始化的函数:

    class Student(db.Model):
    
        s_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        s_name = db.Column(db.String(16), unique=True)
        s_age = db.Column(db.Integer, default=1)
    
        __tablename__ = "student"
    
        def __init__(self, name, age):
            self.s_name = name
            self.s_age = age
    

    1.1 增--批量增加

    第一种方式:

    @blue.route('/createstus/')
    def create_users():
        stus = []
        for i in range(5):
            # 实例化Student的对象
            s = Student()
            # 对象的属性赋值
            s.s_name = '张三%s' % random.randrange(10000)
            s.s_age = '%d' % random.randrange(100)
            stus.append(s)
        # 添加需要创建的数据
        db.session.add_all(stus)
        # 提交事务到数据库
        db.session.commit()
    
        return '创建成功'
    

    注:在创建单条数据的时候使用db.session.add(),在创建多条数据的时候使用db.session.add_all()

    第二种方式:

    @blue.route('/createstus/')
    def create_users():
        stus = []
        for i in range(5):
            # 使用类的初始化去创建Student对象
            s = Student('张三%s' % random.randrange(10000),
                        '%d' % random.randrange(100))
            stus.append(s)
    
        db.session.add_all(stus)
        db.session.commit()
    
        return '创建成功'
    

    1.2 查--使用运算符

    获取查询集

    filter(类名.属性名.运算符(‘xxx’))
    
    filter(类名.属性 数学运算符  值)
    

    运算符:

    contains: 包含
    startswith:以什么开始
    endswith:以什么结束
    in_:在范围内
    like:模糊
    __gt__: 大于
    __ge__:大于等于
    __lt__:小于
    __le__:小于等于
    

    筛选:

    offset()
    
    limit()
    
    order_by()
    
    get()
    
    first()
    
    paginate()
    

    逻辑运算:

    与
        and_
        filter(and_(条件),条件…)
    
    或
        or_
        filter(or_(条件),条件…)
    
    非
        not_
        filter(not_(条件),条件…)
    

    例子1:

    1. 查询学生的id为3,4,5,6,16的的学生信息,使用<b>in_逻辑运算</b>
        @blue.route('/getstubyids/')
        def get_stu_by_ids():
    
            students = Student.query.filter(Student.s_id.in_([3,4,5,6,16]))
            return render_template('StudentList.html', students=students)
    
    1. 查询学生的年龄小于18岁的学生的信息

       Student.query.filter(Student.s_age < 18)
      
    2. 查询学生的年龄小于18岁的学生的信息,<b>__lt__小于</b>

       students = Student.query.filter(Student.s_age.__lt__(15))
      
    3. 查询学生的年龄小于等于18岁的学生的信息,<b>__le__小于等于</b>

       students = Student.query.filter(Student.s_age.__le__(15))
      
    4. 查询学生的姓名以什么开始或者以什么结尾的学生的信息<b>startswith和endswith</b>

       students = Student.query.filter(Student.s_name.startswith('张'))
       students = Student.query.filter(Student.s_name.endswith('2'))
      
    5. 查询id=4的学生的信息

       Student.query.get(4)
       获取的结果是学生的对象
      
    6. 模糊搜索like

       %:代表一个或者多个
       _:代表一个
       
       Student.query.filter(Student.s_name.like('%张%')) 
      
    7. 分页,查询第二页的数据4条

       第一个参数是那一页,第二个参数是一页的条数,第三个参数是是否输出错误信息
       students = Student.query.paginate(2, 4, False).items
      

    例子2:

    跳过offset几个信息,截取limit结果的几个值

    # 按照id降序排列
    stus = Student.query.order_by('-s_id')
    
    # 按照id升序排列
    stus = Student.query.order_by('s_id')
    stus = Student.query.order_by(asc('s_id'))
    stus = Student.query.order_by('s_id asc')
    
    # 按照id降序获取三个
    stus = Student.query.order_by('-s_id').limit(3)
    stus = Student.query.order_by('s_id desc').limit(3)
    
    from sqlalchemy import desc
    stus = Student.query.order_by(desc('s_id')).limit(3)
    
    # 获取年龄最大的一个
    stus = Student.query.order_by('-s_age').first()
    
    # 跳过3个数据,查询5个信息
    stus = Student.query.order_by('-s_age').offset(3).limit(5)
    
    # 跳过3个数据
    stus = Student.query.order_by('-s_age').offset(3)
    
    # 获取id等于24的学生
    stus = Student.query.filter(Student.s_id==24)
    stus = Student.query.get(24)
    

    例子3:

    1. 查询

      from sqlalchemy import and_, or_, not_

      <b>查询多个条件</b>

      stus = Student.query.filter(Student.s_age==18, Student.s_name=='雅典娜')

      <b>and_ 并且条件</b>

      stus = Student.query.filter(and_(Student.s_age==18, Student.s_name=='雅典娜'))

      <b>or_ 或者条件</b>

      stus = Student.query.filter(or_(Student.s_age==18, Student.s_name=='火神'))

      <b>not_ 非</b>

      stus = Student.query.filter(not_(Student.s_age==18), Student.s_name=='火神')

      查询姓名不包含'可爱‘,并且年龄不等于12的学生

      stus = Student.query.filter(not_(Student.s_name.contains('可爱')),
      not_(Student.s_age == 12))

    例子4:

    <b>分页:</b>


    image.png

    后端数据处理:

    # 方法一:手动实现分页,使用offset和limit
    page = int(request.args.get('page', 1))
    stus = Student.query.offset((page-1)*5).limit(5)
    
    # 方法二: 使用切片[:]
    s_page = (page - 1)*5
    e_page = page * 5
    stus = Student.query.all()[s_page: e_page]
    
    # 方法三:使用paginate
    # 查询第几页的数据  
    page = int(request.args.get('page', 1))
    
    # 每一页的条数多少,默认为10条
    per_page = int(request.args.get('per_page', 10))
    
    # 查询当前第几个的多少条数据
    paginate = Student.query.order_by('-s_id').paginate(page, per_page, error_out=False)
    
    stus = paginate.items
    

    前端数据展示:

    <h2>学生信息</h2>
    {% for stu in stus %}
        id:{{ stu.s_id }}
        姓名:{{ stu.s_name }}
        年龄:{{ stu.s_age }}
        <br>
    {% endfor %}
    <br>
    总页数: {{ paginate.pages }}
    <br>
    一共{{ paginate.total }}条数据
    <br>
    当前页数:{{ paginate.page }}
    <br>
    {% if paginate.has_prev %}
        <a href="/stupage/?page={{ paginate.prev_num }}">上一页</a>:{{ paginate.prev_num }}
    {% endif %}
    
    {% if paginate.has_next %}
        <a href="/stupage/?page={{ paginate.next_num }}">下一页</a>:{{ paginate.next_num }}
    {% endif %}
    <br>
    
    <br>
    页码:{% for i in  paginate.iter_pages() %}
            <a href="/stupage/?page={{ i }}">{{ i }}</a>
        {% endfor %}
    

    2. 关联关系

    2.1 一对多建立模型

    学生模型:

    class Student(db.Model):
    
        s_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        s_name = db.Column(db.String(20), unique=True)
        s_age = db.Column(db.Integer, default=18)
        s_g = db.Column(db.Integer, db.ForeignKey('grade.g_id'), nullable=True)
    
        __tablename__ = 'student'
    

    班级模型:

    class Grade(db.Model):
    
        g_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        g_name = db.Column(db.String(10), unique=True)
        g_desc = db.Column(db.String(100), nullable=True)
        g_time = db.Column(db.Date, default=datetime.now)
        students = db.relationship('Student', backref='stu', lazy=True)
    
        __tablename__ = 'grade'
    

    官网解释有如下几个lazy的参数:

    lazy 决定了 SQLAlchemy 什么时候从数据库中加载数据:,有如下四个值:

    <b>select</b>/True: (which is the default) means that SQLAlchemy will load the data as necessary in one go using a standard select statement.

    <b>joined</b>/False: tells SQLAlchemy to load the relationship in the same query as the parent using a JOIN statement.

    <b>subquery</b>: works like ‘joined’ but instead SQLAlchemy will use a subquery.

    <b>dynamic</b>: is special and useful if you have many items. Instead of loading the items SQLAlchemy will return another query object which
    you can further refine before loading the items. This is usually what you want if you expect more than a handful of items for this relationship

    select就是访问到属性的时候,就会全部加载该属性的数据。
    
    joined则是在对关联的两个表进行join操作,从而获取到所有相关的对象。
    
    dynamic则不一样,在访问属性的时候,并没有在内存中加载数据,而是返回一个query对象, 需要执行相应方法才可以获取对象,
    

    2.2

    1. 通过班级查询学生信息
        @grade.route('/selectstubygrade/<int:id>/')
        
        def select_stu_by_grade(id):
            grade = Grade.query.get(id)
            # 通过班级对象.定义的relationship变量去获取学生的信息
            stus = grade.students
        
            return render_template('grade_student.html',
                                   stus=stus,
                                   grade=grade
                                   )
    
    1. 通过学生信息查询班级信息
        @stu.route('/selectgradebystu/<int:id>/')
    
        def select_grade_by_stu(id):
    
            stu = Student.query.get(id)
            # 通过学生对象.定义的backref参数去获取班级的信息
            grade = stu.stu
        
            return render_template('student_grade.html',
                                   grade=grade,
                                   stu=stu)
    

    注意:表的外键由db.ForeignKey指定,传入的参数是表的字段。db.relations它声明的属性不作为表字段,第一个参数是关联类的名字,backref是一个反向身份的代理,相当于在Student类中添加了stu的属性。例如,有Grade实例dept和Student实例stu。dept.students.count()将会返回学院学生人数;stu.stu.first()将会返回学生的学院信息的Grade类实例。一般来讲db.relationship()会放在一这一边。

    3. 数据库迁移

    在django中继承了makemigrations,可以通过migrate操作去更新数据库,修改我们定义的models,然后在将模型映射到数据库中。

    在flask中也有migrate操作,它能跟踪模型的变化,并将变化映射到数据库中

    2.1 安装migrate

    pip install flask-migrate
    

    2.2 配置使用migrate

    2.2.1 初始化,使用app和db进行migrate对象的初始化
    from flask_migrate import Migrate
    
    #绑定app和数据库
    Migrate(app=app, db=db)
    
    2.2.2 安装了flask-script的话,可以在Manager()对象上添加迁移指令
    from flask_migrate import Migrate, MigrateCommand
    
    app = Flask(__name__)
    
    manage = Manager(app=app)
    
    manage.add_command('db', MigrateCommand)
    

    操作:

    python manage.py db init  初始化出migrations的文件,只调用一次
    
    python manage.py db migrate  生成迁移文件
    
    python manage.py db upgrade 执行迁移文件中的升级
    
    python manage.py db downgrade 执行迁移文件中的降级
    
    python manage.py db --help 帮助文档
    

    flask使用操作指南之模型3


    1. 关联关系---多对多

    定义模型:

    引入SLALchemy

    from flask_sqlalchemy import SQLAlchemy
    
    db = SQLAlchemy()
    

    创建中间表

    sc = db.Table('sc',
        db.Column('s_id', db.Integer, db.ForeignKey('student.s_id'), primary_key=True),
        db.Column('c_id', db.Integer, db.ForeignKey('courses.c_id'), primary_key=True)
    )
    

    创建学生类Student

    class Student(db.Model):
    
        s_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        s_name = db.Column(db.String(20), unique=True)
        s_age = db.Column(db.Integer, default=18)
        s_g = db.Column(db.Integer, db.ForeignKey('grade.g_id'), nullable=True)
    
        __tablename__ = 'student'
    
        def __init__(self, name, age):
    
            self.s_name = name
            self.s_age = age
            self.s_g = None
    

    创建课程表的模型,Course类

    class Course(db.Model):
    
        c_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        c_name = db.Column(db.String(20), unique=True)
        students = db.relationship('Student',
                                   secondary=sc,
                                   backref='cou')
    
        __tablename__ = 'courses'
    
        def __init__(self, name):
    
            self.c_name = name
    

    sc表由<font style="color:red;">db.Table声明</font>,我们不需要关心这张表,因为这张表将会由SQLAlchemy接管,它唯一的作用是作为students表和courses表关联表,所以必须在db.relationship()中指出<font style="color:red;">sencondary关联表参数</font>。lazy是指查询时的惰性求值的方式,这里有详细的参数说明,而db.backref是声明反向身份代理,其中的lazy参数是指明反向查询的惰性求值方式.

    2. 添加学生和课程之间的关系

    通过页面中传递学生的id和课程的id,分别获取学生的对象和课程的对象,在使用关联关系append去添加学生对象,并且add以后再commit后,就可以在中间表sc中查看到新增的关联关系了。

        userid = request.form.get('userid')
        courseid = request.form.get('courseid')
    
        stu = Student.query.get(userid)
        cou = Course.query.get(courseid)
    
        cou.students.append(stu)
        db.session.add(cou)
        db.session.commit()
    

    3. 删除学生和课程之间的关系

    通过页面获取传递的学生的id和课程的id,分别获取学生对象和课程对象,在使用关联关系remove去删除学生对象,并commit将事务提交到数据库中

    stu = Student.query.get(s_id)
    cou = Course.query.get(c_id)
    
    cou.students.remove(stu)
    db.session.commit()
    

    4. 通过课程查询学生的信息

    以下定义在课程course的模型中,所以通过课程查询学生的信息,<font style="color:red;">语法为课程的对象.studengs</font>。如果知道学生的信息反过来找课程的信息,则使用backref的反向关联去查询,<font style="color:red;">语语法为学生的对象.cou(反向)</font>

    students = db.relationship('Student',secondary=sc,backref='cou')

    cou = Course.query.get(2)
    stus = cou.students
    

    5. 通过学生去查询课程的信息

    stu = Student.query.get(id)
    cous = stu.cou
    

    flask使用操作指南之插件


    1. 开发,页面调试工具debugtoolbar

    1.1 安装

    pip install flask-debugtoolbar
    

    1.2 配置

    from flask import Flask
    
    from flask_debugtoolbar import DebugToolbarExtension
    
    app = Flask(__name__)
    
    app.debug = True
    
    app.config['SECRET_KEY'] = '<replace with a secret key>'
    
    toolbar = DebugToolbarExtension(app)
    

    2. restful

    Flask-RESTful 提供的最主要的基础就是资源(resources)。资源(Resources)是构建在 Flask 可拔插视图 之上,只要在你的资源(resource)上定义方法就能够容易地访问多个 HTTP 方法

    官网上描述了一个最简单的restful风格的api,如下:

    from flask import Flask
    from flask.ext import restful
    
    app = Flask(__name__)
    api = restful.Api(app)
    
    class HelloWorld(restful.Resource):
        def get(self):
            return {'hello': 'world'}
    
    api.add_resource(HelloWorld, '/')
    
    if __name__ == '__main__':
        app.run(debug=True) 
    

    2.1 安装

    pip install flask_restful
    

    2.2 配置

    在create_app()获取Flask(name)对象中,设置如下配置

    from flask_restful import Api
    
    api = Api()
    
    api.init_app(app=app)
    

    在views中需要引入配置的api还有Resource

    # 导入包和restful中的Api对象
    from flask_restful import Resource
    from utils.functions import api
    
    # 定义类,启动包含了对数据处理的GET,POST,PATCH,PUT,DELETE请求
    class CreateCourse(Resource):
    
    def get(self, id):
        course = Course.query.get(id)
        return course.to_dict()
    
    def post(self):
    
        courses = ['大学英语', '大学物理', '线性代数', '高数',
                   'VHDL', 'ARM', '马克思主义', '农场劳动']
        course_list = []
        for course in courses:
            c = Course()
            c.c_name = course
            course_list.append(c)
        db.session.add_all(course_list)
        db.session.commit()
    
        courses = Course.query.all()
        return [course.to_dict() for course in courses]
    
    def patch(self, id):
        c_name = request.form.get('c_name')
        course = Course.query.get(id)
        course.c_name = c_name
        db.session.commit()
        return {'code': 200, 'data': course.to_dict(), 'msg': '请求成功'}
    
    def delete(self, id):
        course = Course.query.get(id)
        db.session.delete(course)
        db.session.commit()
        return {'code': 200, 'msg': '删除成功'}
    
    
    # 绑定处理url
    api.add_resource(CreateCourse, '/api/course/<int:id>/', '/api/course/')
    

    2.3 端点(Endpoints)

    在一个 API 中,你的资源可以通过多个 URLs 访问。你可以把多个 URLs 传给 Api 对象的 Api.add_resource() 方法。每一个 URL 都能访问到你的 Resource

    如:

    api.add_resource(CreateCourse, '/api/course/<int:id>/', '/api/course/')
    

    2.4 返回响应

    Flask-RESTful 支持视图方法多种类型的返回值。同 Flask 一样,你可以返回任一迭代器,它将会被转换成一个包含原始 Flask 响应对象的响应。Flask-RESTful 也支持使用多个返回值来设置响应代码和响应头

    如:

    def get(self, id):
        course = Course.query.get(id)
        return course.to_dict(), 200
    

    Django和Flask区别:


    1. jiaji2和DjangoTemplates模板引擎相比,jiaja2语法更简单

    比如: loop.index 和 forloop.counter
           loop.revindex 和 forloop.revcounter
    jiaja2中没有ifequal
    

    2. 耦合

    Django: 大而全,但是耦合性高。Auth,Permission,admin基本没用
            开发快,符合MVC模式
    
    Flask: 微框架,很小巧。需要哪些功能,自己装。
            需要熟悉MVC模式
    

    3. 模型

    3.1 模型定义

    1. 模型中不定义数据库的表名:
        在django中默认表名为:'应用app名_模型名小写'
        在flask中默认的表名为:'模型名小写'
    
    2. 自增id字段:
        在django中默认会创建自增的主键id
        在flask中需要自己写自增的主键id:
            id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    
    3. 查询所有数据的结果,all()方法
        在django中查询的结果为QuerySet
        在Flask中查询结果为List
    
    4. 查询满足条件的数据的结果,filter(), filter_by()方法
        在django中查询的结果为QuerySet
        在Flask中查询结果为BaseQuery objects
    

    3.2 模型数据查询

    Django:

    一对多:
    
      模型1: u 字段为 FOREIGN_KEY,关联到模型2
        模型1.u = u对象
        模型1.u_id = u对象.id
    
    
      模型1查找模型2的数据
        模型2对象=模型1对象.u
        模型1对象=模型2对象.模型1_set.all()
    
    一对一:
    
      模型1查找模型2的数据
        模型2对象=模型1对象.u
        模型1对象=模型2对象.模型1.all()
    

    Flask:

    一对多:
    
        模型1: u字段为FOREIGN KEY,关联到模型2
        模型2: yy字段,定义relationship字段, backref=‘uu’
    
        模型1查找模型2:
            模型2对象 = 模型1对象.uu
            模型1对象 = 模型2对象.yy

    相关文章

      网友评论

        本文标题:Flask

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