Flask养生

FLASK框架

简介:

flask是一个非常小的python web框架 只提供了一个强劲的核心 其它都是通过第三方扩展库来实现

安装:

sudo pip install flask

虚拟环境

安装虚拟环境 pip install virtualenv

创建自己的虚拟环境 virtualenv --no-site-packages 虚拟环境的名称

启动虚拟环境 source venv/bin/activate

退出虚拟环境 deactivate (退出以后 进入到的是全局的·python环境)

虚拟环境迁移

pip freeze
pip list
pip freeze > requirment.txt
pip freeze
pip list
pip install -r requirment.txt

解释:虚拟环境 就是相当于自己新装了一个python的解释器 不受全局解释器 中的包的影响(独立) 如果在虚拟环境中 安装了flask,在退出虚拟环境以后 使用不了 flask的 因为你的全局的python解释器 中没有安装flask

一、完整的启动

from flask import Flask #导入Flask

app = Flask(__name__) #__name__是必传参数  flask类的实例化

#路由地址
#127.0.0.1:5000/
@app.route('/') #装饰器路由
def index(): #视图函数名称
    return 'hello flask'


#只在当前主文件中启动 flask
if __name__ == '__main__':
    app.run()  #运行flask

二、app.run() 启动参数

参数 参数说明
debug 调试模式 默认为False 需要启动改为True
port 端口号 默认5000
host 主机地址 默认127.0.0.1 ~ 127.255.255.254
threaded 线程 默认False True开启多线程

实例:

#只在当前主文件中启动 flask
if __name__ == '__main__':
    app.run(host='127.0.0.1',port=5001,debug=True,threaded=True)  #运行flask
    app.run(debug=True)  #开启调试模式

三、视图函数

(1) 无参路由

@app.route('/')
def index():
    return '首页'

(2) 带参路由

#访问地址 127.0.0.1:5000/welcome/zhangsan/
@app.route('/welcome/<name>/')
def welcome(name):
    # return '必须有响应'
    return '欢迎'+name

(3) 传递多个参数

#带多个参数
#http://127.0.0.1:5000/args/zhansan/18/
# @app.route('/args/<name>/<age>/')
# http://127.0.0.1:5000/args/zhansan_18/
@app.route('/args/<name>_<age>/')
def args(name,age):
    return '我叫{} 我今年{}岁了'.format(name,age)

(4) 限制参数的类型

@app.route('/demo/<int:age>/') #限制传参类型为int
@app.route('/demo/<float:age>/') #限制传参类型为float
@app.route('/demo/<string:age>/') #默认就是字符串
@app.route('/demo/<path:age>/') #把参数age位置的参数中的/不再认为是路由地址的分隔符,而是参数的一部分  属于字符串类型的一部分
#/demo/zhangsan/a/b/c/
@app.route('/demo/<name>/<path:age>/') #把参数age位置的参数中的/不再认为是路由地址的分隔符,而是参数的一部分  属于字符串类型的一部分
# http://127.0.0.1:5000/demo/zhangsan/a/b/c/
@app.route('/demo/<path:age>/<name>/') #把参数age位置的参数中的/不再认为是路由地址的分隔符,而是参数的一部分  属于字符串类型的一部分
def demo(age,name):
    print(age,name)
    # print(type(age))
    return '我是测试的视图'

注意:

  1. 每个路由地址结尾的/ 建议都加上 如果路由地址存在结尾的/ 浏览器会自动加上 如果没有结尾的 / 请求时 存在/ 则请求失败
  2. 传参的格式 <参数名称>
  3. 传递多个参数 使用/分隔符 或者_进行多个参数的拼接
  4. 参数传递进来都为字符串类型(默认)
  5. 限制参数类型 int/float/string/path

四、重定向

从一个视图跳转到另外一个视图的操作

from flask import redirect,url_for

(1) redirect 通过路由地址跳转

实例

@app.route('/')
def index():
    return 'index.html'

@app.route('/args/<name>/<age>/')
def args(name,age):
    return name+age

@app.route('/redirect/')
def myRedirect():
    return redirect('/') #重定向到首页
    return redirect(/args/zhansgan/18/)

(2) url_for 通过视图函数名 反向解析出路由地址

实例

@app.route('/')
def index():
    return 'index.html'

@app.route('/args/<name>/<age>/')
def args(name,age):
    return name+age

@app.route('/redirect/')
def myRedirect():
    print(url_for('index')) # / 
    print(url_for('indexx')) #  报错 当前视图函数不存在
    print(url_for('args',name='zhangsan',age=18) #/args/zhangsan/18/
    return redirect(url_for('index')) #重定向到首页
    return redirect(url_for('args'name='zhangsan',age=18)) 

注意:

当url_for 构造不存在的视图函数时 则报错

当url_for 构造需要传参的路由地址的时候 不存参则报错

五、abort(http标准状态码) 终止

from flask import abort

  1. 消息
  2. 成功
  3. 重定向
  4. 请求错误
  5. 服务器错误

实例

@app.route('/')
def index():
    print('abort上方')
    # abort(404)
    abort(500)
    print('abort下方')
    # return 'index.html'
 
参数e接受错误的信息
    
#捕获404状态码的错误信息
@app.errorhandler(404)
def page_not_found(e):
    print(e)
    # return 'page_not_found'
    return e
#捕获500状态码的错误信息
@app.errorhandler(500)
def server_error(e):
    return 'server_error'

注意:

abort上面的代码正常执行 下面的代码不再执行 和raise一样

六、响应 response

概述: 我们响应 可以给浏览器响应文字内容,html页面的代码 和 状态码 创建cookie

(1) 使用return 文字内容

@app.route('/')
def index():
    return 'index.html'

(2) 响应内容并指定响应的状态码

@app.route('/')
def index():
    return 'index.html',404

(3) 通过make_response 构造响应

from flask imoprt make_response

@app.route('/make_response/')
def makeResponse():
    # res = make_response('make_response响应的内容')
    res = make_response('make_response响应的内容',404)
    return res

七、cookie 会话控制

协议:http

http:超文本传输协议 无状态协议

(1) 设置cookie 不设置过期时间

注意: 默认存活时间为 浏览会话结束(关闭浏览器)

主体结构

Response.set_cookie(

​ key,

​ value,

​ max_age=None #设置过期时间 单位为秒数

​ expires =None, #设置过期时间 为时间戳的秒数

​ path = '/' #设置当前cookie存储为全局

)

实例

#cookie的操作
@app.route('/set_cookie/')
def set_cookie():
    res = make_response('设置cookie')
    res.set_cookie('name','zhangsan')
    return res

(2) 获取cookie

@app.route('/get_cookie/')
def get_cookie():
    # print(request.cookies['name']) #获取请求过来的cookie
    # print(request.cookies.get('name')) #获取请求过来的cookie
    return 'cookie中name的值为{}'.format(request.cookies.get('name','没有值'))

(3) 删除cookie

@app.route('/del_cookie/')
def del_cookie():
    res = make_response('删除name的cookie')
    res.delete_cookie('name')
    return res

(4) 设置cookie 并设置过期时间

#cookie的操作
@app.route('/set_cookie/')
def set_cookie():
    res = make_response('设置cookie')
    # res.set_cookie('name','zhangsan') #不舍值过期时间
    # res.set_cookie('name','zhangsan',max_age=60) #max_age 存活的秒数
    lift_time = time.time()+40 #设置40秒以后的时间戳
    res.set_cookie('name','zhangsan',expires=lift_time) #expires 值为存活的时间戳
    return res

八、session

概述:服务器要识别 不同的用户的请求 根据cookie携带着的唯一session_id 值 来进行区分不同用户的请求

所以session基于cookie 如果cookie被禁用了 session也不能使用

session加密需要加密种子 secret_key 进行加密

(1) 设置session

注意: 不设置过期时间 默认为浏览会话结束

@app.route('/set_session/')
def index():
    session['username'] = '张三'
    return '设置set_session'

(2) 获取session

@app.route('/get_session/')
def get_session():
    return '获取session 值为{}'.format(session.get('username'))

(3) 删除session

@app.route('/del_session/')
def del_session():
    session.pop('username') #删除key为username的数据
    # session.clear() #删除全部
    return '删除session'

(4) 设置session及过期时间

@app.route('/set_session/')
def index():
    session.permanent = True #持久化存储
    app.permanent_session_lifetime = timedelta(minutes=5) #设置存活时间为5分钟
    session['username'] = '张三'
    return '设置set_session'

day2

一、request对象

浏览器带着用户的请求 经由flask框架创建出 request对象 request对象 包含当前用户的所有的请求信息

导入

from flask import request

request对象属性

  1. url 完整的url请求

  2. base_url 去掉get传参的url

  3. host_url 只有主机地址和端口号

  4. path 装饰中的路由地址

  5. method 请求方法

  6. args 获取get传参

    request.args.get(key) 只能获取唯一key的值 如果key存在重复 则获取不到其它值

    request.args.getlist(key) 获取key以列表形式返回(拿到所有的value)

  7. form 获取表单post传递过来的数据

  8. files 获取文件上传的数据

  9. headers 获取请求头信息

  10. cookies 获取所有的cookie

  11. json 获取json数据

二、flask-script扩展

概念: 就是一个flask终端运行的解析器 因为在项目完成以后 就不应该有任何的改动 所有通过终端 添加不同的参数 来进行不同的启动项

安装

sudo pip3 install flask-script

使用

from flask_script import Manager
app = Flask(__name__)
manager= Manager(app)

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

启动命令

python3 文件名称.py runserver -d -r -h127.0.0.1 -p5000 --threaded

python3 manage.py runserevr -d -r #开启debug和自动加载

三、蓝本 blueprint

概念: 将视图拆分到不同的文件中 蓝本就是解决这个问题的

导入

from flask import Blueprint

user.py

from flask import Blueprint,request #导入蓝本
user = Blueprint('user',__name__)

@user.route('/login/')
def login():
    return '登陆'

manage.py

from user import user #导入user蓝本对象
#注册蓝本
app.register_blueprint(user) #127.0.0.1:5000/login/
#设置当前蓝本的前缀
# app.register_blueprint(user,url_prefix='/user') #127.0.0.1:5000/user/login/

蓝本中的重定向

index->login

@app.route('/')
def index():
    # return redirect(url_for('login')) #错误写法 报错
    return redirect(url_for('user.login')) #user蓝本中的login视图函数

四、请求钩子函数

类Django框架的中间件

在主程序中使用

钩子函数 函数说明
before_first_request 第一次请求之前
before_request 每次请求之前
after_request 每次请求之后
teardown_request 每次请求之后 即使有异常出现

实例

#第一次请求之前
@app.before_first_request
def before_first_request():
    print('before_first_request')
#每次请求之前
@app.before_request
def before_request():
    if request.path == '/form/' and request.method == 'GET':
        abort(404)
    print('before_request')
#每次请求之后
@app.after_request
def after_request(res):
    print('after_request')
    return res
#每次请求之后
@app.teardown_request
def teardown_request(res):
    print('teardown_request')
    return res

在蓝本中使用

钩子函数 函数说明
before_app_first_request 第一次请求之前
before_app_request 每次请求之前
after_app_request 每次请求之后
teardown_app_request 每次请求之后 即使有异常出现

实例

#在蓝本中

#第一次请求之前
@user.before_app_first_request
def before_first_request():
    print('before_first_request')
#每次请求之前
@user.before_app_request
def before_request():
    if request.path == '/form/' and request.method == 'GET':
        abort(500)
    print('before_request')
#每次请求之后
@user.after_app_request
def after_request(res):
    print('after_request')
    return res
#每次请求之后
@user.teardown_app_request
def teardown_request(res):
    print('teardown_request')
    return res

模板引擎

模板引擎: 就是一定规则 书写 和 替换 最终展示成完整的html文件 响应给用户

模板引擎: jinja2

目录结构

project/
    templates/ 模板目录
    manage.py

(1) flask中渲染模板的函数

导入

from flask import render_template,render_template_string

render_template() #将模板渲染后响应给用户

@app.route('/')
def index():
    # return render_template('index.html')
    print(render_template('index.html')) #render_templates将模板代码渲染以后 响应给用户
    return 'index.html'

render_template_string() #渲染字符串 响应给用户

@app.route('/')
def index():
    return render_template_string('世界上最牛的字是哪一个?<span style="font-size:16px;color:red;">昊</span>')

(2) 模板中 分为俩大类

  1. 变量

    {{ 变量名称 }}

    注意:

    ​ 如果在模板中存在变量 且视图函数没有传递 则插入空白字符(什么都没有) 不报错

    实例

    @app.route('/')
    def index():
        return render_template('index.html',title='首页')
    
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>{{ title }}</title>
    </head>
    <body>
    <h1>首页</h1>
    </body>
    </html>
    
  1. 标签

    {% 标签名称 %}

(3) 过滤器

根据管道符 | 对传递的值进行过滤

  1. abs 绝对值

    val|abs
    
  2. default 默认值

    默认情况 只有给定的变量不存在则执行 默认值

    {{ var|default('默认值') }}

    {{ False|default('默认值') }} 不走

    {{ 不存在的变量|default('默认值') }} 执行默认值

    设置default执行flase的默认值

    {{ var|default('默认值',boolean=True) }}

  3. first 返回序列中的第一个

  4. last 返回序列中的最后一个值

  5. format 格式化字符串的输出

    <li>{{ "我叫%s 我今年%d岁了 存款为%.2f元"|format('张三',20,1234) }}</li>
    
  6. length 长度

  7. join 拼接字符串

  8. safe 不转义代码

  9. int 转换为整形

  10. float 转换为浮点形

  11. list 转换为列表

  12. lower 转换小写

  13. upper 转换为大写

  14. replace 替换

  15. trim 去除俩测空白字符

  16. striptags 去除html标签

使用的安装

sudo pip3 install 模块名称

  1. flask
  2. flask-script
  3. flask-moment
  4. flask-cache
  5. flask-uploads
  6. flask-wtf
  7. flask-bootstrap
  8. flask-sqlalchemy
  9. pillow
  10. pymysql
  11. flask-mail
  12. flask-migrate

(4) 标签

格式{% tag %}

if 标签

if...elif..else

{% if 90<=data.grade<=100 %}
<p>优</p>
{% elif 80<=data.grade %}
<p>良</p>
{% elif 70<=data.grade %}
<p>中</p>
{% elif 60<=data.grade %}
<p>合格</p>
{% else %}
<p>不及格</p>
{% endif %}

for 标签

{% for i in ... %}

​ 循环体

{% else %}

​ ...

{% endfor %}

<ol>
{#    {% for i in range(10) %}#}
    {% for i in xx %}
     {% for k,v in data.items() %}  #迭代字典
        <li>{{ i }}</li>
    {% else %}
        <li>你是否能看到我</li>
    {% endfor %}
</ol>

注意:

当迭代的变量 不存在 则执行else

获取迭代的状态

变量 描述
loop.index 当前迭代的索引 从1开始
loop.index0 当前迭代的索引 从0开始
loop.first 是否为第一次迭代
loop.last 是否为最后一次迭代
loop.length 迭代的长度

(5) 文件的包含 include

格式

{% include "路径/文件.html" %}

把文件引入到当前的位置 实现模板代码的复用

实例

header.html

<header>
    头部
</header>

footer.html

<footer>
    尾部
</footer>

test.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{#{% include 'index.html' %}#}
{% include 'common/header.html' %}
<div>content内容部分</div>
{% include 'common/footer.html' %}
</body>
</html>

注意:

include 会将导入的代码 相当于粘贴到了 你导入的位置上 导入文件的所有代码都会在导入的位置显示出来

(6) 宏 macro

概述

宏相当于 python中的函数 需要定义 调用 传参

主体结构

{% macro 宏名称([参数...]) %}
    内容
{% endmacro %}

调用

{{ 宏的名称([参数...]) }}

导入

在common下创建pubulicform.html文件

在form.html中导入

实例

from ... import ...

{% from 'common/publicform.html' import form %}
{{ form('用户名','text','username') }}
{{ form('密码','password','userpass') }}
{{ form('','submit','','提交') }}

import ...

{% import 'common/publicform.html' as publicform %}
{{ publicform.form('用户名','text','username') }}
{{ publicform.form('密码','password','userpass') }}
{{ publicform.form('','submit','','提交') }}

注意

  1. 宏的调用只能在宏的下方来调用
  2. 宏如果有形参 且没有默认值 则可以不传实参
  3. 宏如果没有形参 则不可以传实参
  4. 宏的形参默认值 和python一样
  5. 宏的默认值 遵循默认值规则 有默认值的放在右侧 没有默认值的 放在左侧
  6. 关键字的参数使用 同python函数一样

day3

一、模板的继承 extends

概念: flask中的模板继承 可以通过继承 将很多重复的元素 抽离出来 放在父模板中 通过模板中 的extends,block 实现父模板继承和替换的使用

语法

{% extends '父模板的名称.html' %}

{% block 替换的名称 %} 要替换的内容部分 {% endblock %}

自定义一个base.html模板
<!DOCTYPE html>
<html lang="en">
{% block header %}
<head>
    {% block meta %}
        <meta charset="UTF-8">
    {% endblock %}
    <title>{% block title %}base{% endblock %}</title>
    <style>
    {% block headercss %}
        div{
            font-size: 18px;
            color: aqua;
        }
    {% endblock %}
    </style>

    {% block linkscript %}
    {% endblock %}
</head>
{% endblock %}
<body>
<header>
    头部
</header>
<div id="content">
    {% block content %}
        主体内容部分
    {% endblock %}
</div>
<footer>
    底部
</footer>
</body>
</html>

子模板 index.html

{% extends 'common/base.html' %}
{% block title %}
首页
{% endblock %}
{% block headercss %}
    {{ super() }}
    ul,li{
       list-style:none;
        margin:0;
        padding:0;
    }
    li{
        width:100px;
        height:100px;
        border-radius:50px;
        background-color:yellow;
    }
     #content{
        color:red;
    }
{% endblock %}
{% block content %}
    <h1>首页内容</h1>
    <ul>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
    </ul>
{% endblock %}
{# 以下代码不会被加载 #}
{#{% block newblock %}#}
{#    <h1>首页内容</h1>#}
{#{% endblock %}#}
{#sdasjdjsaldjaslkdalksjlksajdlasjdalskdjalkdsaskl#}

注意:

不可以在base模板block 以外的位置 添加任何的代码 因为都不会被加载

{{ super() }} 将base.html模板中替换的代码在调用(复用)

二、flask-bootstrap flask的bootstrap第三方扩展库

安装:

sudo pip3 install flask-bootstrap

使用

自定义base.html

{% extends 'bootstrap/base.html' %}
{% block title %}
    boot-base 更改自己的base
{% endblock %}
{% block navbar %}
    <nav class="navbar navbar-inverse" style="border-radius:0px;">
        <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                        data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="#"><span class="glyphicon glyphicon-eye-open"
                                                       aria-hidden="true"></span></a>
            </div>

            <!-- Collect the nav links, forms, and other content for toggling -->
            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                <ul class="nav navbar-nav">
                    <li class="active"><a href="#">首页<span class="sr-only">(current)</span></a></li>
                    <li><a href="#">发表帖子</a></li>

                </ul>

                <ul class="nav navbar-nav navbar-right">
                    <form class="navbar-form navbar-left">
                        <div class="form-group">
                            <input type="text" class="form-control" placeholder="Search">
                        </div>
                        <button type="submit" class="btn btn-default">Submit</button>
                    </form>
                    <li><a href="#">登录</a></li>
                    <li><a href="#">注册</a></li>
                    <li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                           aria-expanded="false">个人中心 <span class="caret"></span></a>
                        <ul class="dropdown-menu">
                            <li><a href="#">Action</a></li>
                            <li><a href="#">Another action</a></li>
                            <li><a href="#">Something else here</a></li>
                            <li role="separator" class="divider"></li>
                            <li><a href="#">Separated link</a></li>
                        </ul>
                    </li>
                </ul>
            </div><!-- /.navbar-collapse -->
        </div><!-- /.container-fluid -->
    </nav>
{% endblock %}
{% block content %}
    <div class="container">
        {% block pagecontent %}
            <p>少小离家老大回</p>
            <p>安能辩我是雌雄</p>
        {% endblock %}
    </div>
{% endblock %}

三、错误页面定制

manage.py

#错误页面定制
@app.errorhandler(404)
def page_not_found(e):
    return render_template('error.html',code=404,e=e,info='您访问的页面被外星人抓走了')

@app.errorhandler(500)
def page_not_found(e):
    return render_template('error.html',code=500,e=e,info='我们内部有些事情需要解决 请稍后访问')

error.html

{% extends 'common/boot-base.html' %}
{% block title %}
{{ code }}页面
{% endblock %}
{% block pagecontent %}
    <h3>{{ info }}</h3>
    <div class="alert alert-warning" role="alert"><h4>{{ code }}</h4>{{ e }}</div>
{% endblock %}

四、静态资源

创建static静态资源目录

目录结构

project/
    templates/
        common/
        main/
            index.html
        ....
    static/
        img/
        css/
        js/
        upload/
    manage.py

实例

{% block pagecontent %}
    <h3>{{ info }}</h3>
    <div class="alert alert-warning" role="alert"><h4>{{ code }}</h4>{{ e }}</div>
    <img src="{{ url_for('static',filename='img/timg.jpg',_external=True) }}" alt="">
{% endblock %}

五、视图传递多个参数

(1) 原始传参

@app.route('/')
def index():
    return render_template('index.html',arg1=v1,arg2=v2...)

(2) 使用字典

@app.route('/')
def index():
    return render_template('index.html',data={'arg':'v1','arg2':'v2'...})

(3) 使用**

@app.route('/')
def index():
    data={'arg':'v1','arg2':'v2'...}
    return render_template('index.html',**data)

(4) locals()

以字典的形式获取局部变量

@app.route('/')
def index():
    return render_template('index.html',**locals())变量

表单

六、flask原生表单

form1.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>原生表单</title>
</head>
<body>
<form action="{{ url_for('check_form',_external=True) }}" method="post">
    <p>用户名 <input type="text" name="username"></p>
    <p>密码 <input type="password" name="userpass"></p>
    <p><input type="submit" value="submit"></p>
</form>
</body>
</html>

manage.py

#跳转到登录的表单页
@app.route('/login/')
def login():
    return render_template('form1.html')

#表单提交过来处理的视图函数
@app.route('/check_form/',methods=['POST'])
def check_form():
    print(request.form.get('username'))
    print(request.form.get('userpass'))
    return '提交过来了'

俩个合并为同一个

#表单提交过来处理的视图函数
@app.route('/login/',methods=['GET','POST'])
def check_form():
    if request.method == 'POST':
        print(request.form.get('username'))
        print(request.form.get('userpass'))
    return render_template('form1.html')

七、flask-wtf

概述: 是一个用于处理flask表单的扩展库 提供了表单校验 csrf等功能

安装

sudo pip3 install flask-wtf

csrf_token 的生成 需要依赖flask的secret_key

python中常量:一般用于配置文件的设置 变量的大写 在代码运行过程中 不能被修改和销毁的量 称之为常量(python中没有)

(1) 字段属性

字段类型 字段说明
StringField 普通文本字段
SubmitField 提交按钮
PasswordField 密码框
HiddenField 隐藏域
TextAreaField 多行文本域
DateField 日期
DateTimeFIeld 时间和日期
IntegerField 整数
FloatField 浮点数
BooleanField 布尔字段
RadioFIeld 单选框
SelectField 下拉
FileField 文件上传

(2) 验证器

验证器 验证器说明
DataRequired 必填
Email 邮箱
IPAddress ip地址
Length 长度
NumberRang 值的范围
EqualTo 验证俩个字段的值是否相同
URL 验证url地址
Regexp 正则验证

实例

form2.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="" method="post">
    {#  添加csrf——token验证  #}
    <p>{{ form.csrf_token }}</p>
    <p>{{ form.username.label() }}</p>
    <p>{{ form.username() }}
        {#    显示错误信息的判断    #}
        {% if form.username.errors %}
            {{ form.username.errors.0 }}
        {% endif %}
    </p>
    <p>{{ form.userpass.label() }}</p>
    <p>{{ form.userpass() }}
        {#    显示错误信息的判断    #}
        {% if form.userpass.errors %}
            {{ form.userpass.errors.0 }}
        {% endif %}
    </p>
    <p>{{ form.submit() }}</p>
</form>
</body>
</html>

manage.py

from flask import Flask,render_template,request
from flask_script import Manager
from flask_bootstrap import Bootstrap
from flask_wtf import FlaskForm # 导入表单类的基类
from wtforms import StringField,PasswordField,SubmitField #导入字段类型
from wtforms.validators import DataRequired,Length #导入验证器

app = Flask(__name__)
# app.secret_key = 'abcd'
app.config['SECRET_KEY'] = 'abcd'
app.config['BOOTSTRAP_SERVE_LOCAL'] = True #加载本地样式
bootstrap = Bootstrap(app)
manager = Manager(app)

#自定义表单类
class Login(FlaskForm):
    #username 为 name=username 用户名label标签和内容  validators验证器
    username = StringField('用户名',validators=[DataRequired(message='用户名不能为空')])
    userpass = PasswordField('密码',validators=[DataRequired(message='密码不能为空'),Length(min=6,max=12,message='密码长度为6~12位')])
    submit = SubmitField('登录')

@app.route('/')
def index():
    return render_template('index.html')


@app.route('/login/',methods=['GET','POST'])
def login():
    form = Login() #实例化自定义表单类
    if form.validate_on_submit(): #如果csrf_token和数据验证都通过则为真
        return '数据提交过来了'
    return render_template('form2.html',form=form)


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

day5 MODEL

flask作为一款MVT框架 也有自己的ORM flask-sqlalchemy

安装: sudo pip3 install flask-sqlalchemy

使用ORM原因:

当项目越来越大的时候 如果采用原生SQL的方式 就会有如下问题

  1. 出现大量的原生SQL,如果条件越多 代码越长 并且重复使用率低
  2. 很多的sql语句 都是在业务逻辑中拼接出来的 如果数据库需要更改 就要去修改这些sql的逻辑 很容易漏掉某些sql的修改
  3. 写原生sql容易忽略安全问题

使用ORM好处:

  1. 易用性 使用ORM做数据库的开发 可以有效的减少重复SQL的概率 写出来的代码 也更加的清晰直观
  2. 设计灵活 可以轻松的写出sql语句
  3. 可移植性

一、原生sql

(1) 创建一个库

create database if not exists bj1805 character set utf8;

(2) 创建表

create table if not exists user(

id int primary key auto_increment,

username varchar(20) not null default 'xxx',

sex tinyint default 1

)

(3) 导入 sqlalchemy

from sqlalchemy import create_engine

(4) 配置 URI 链接地址

DB_URI = 'mysql+pymysql:// 用户名:密码@主机名:端口号/数据库名称'

实例

from sqlalchemy import create_engine

HOSTNAME = '127.0.0.1'
USERNAME = 'root'
PASSWORD = '123456'
PORT = 3306
DATABASE = 'bj1805'

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

engine = create_engine(DB_URI)

#链接数据库
with engine.connect() as con:
    #创建表
    con.execute('create table test(username varchar(20))')
    con.execute('insert into test values("xxxx")')

二、flask-sqlalchemy的使用

(1) 导入

from flask_sqlalchemy import SQLalchemy

(2) 配置

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:123456@127.0.0.1:3306/bj1805'
#是否追踪数据发生的改变 占用额外资源  将值改为True或者Flase进行禁用
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

(3) 实例化

db = SQLalchemy(app)

三、设计模型

字段类型

类型名 python中的类型 类型说明
Integer int 存储整形
SmallInteger int 小整形
BigInteger int 长整形
Float float 浮点形
String str varchar类型
Text str text长文本类型
Boolean bool tinyint 整形
Date datetime.date 日期
Time datetime.time 时间
DateTime datetime.datetime 日期和时间

可选约束条件

选项 说明
primary_key 默认False True创建主键索引
unique 唯一索引
index 常规索引
nullable 是否可以为null 默认True
default 默认值

实例:

#创建模型
class User(db.Model):
    __tablename__ = 'user' #表名默认为类名 给当前模型起表名
    #id 字段名称 db.Column  整形 主键索引
    id = db.Column(db.Integer,primary_key=True)
    username = db.Column(db.String(20),index=True)
    age = db.Column(db.Integer)
    sex = db.Column(db.Boolean,default=True) #default默认值 并不是修改表结构 而是在插入数据的时候 没有给值 则将默认值插入
    info = db.Column(db.String(100))
    #添加构造方法
    def __init__(self,username='',age=18,sex=True,info='默认值'):
        self.username = username
        self.age = age
        self.sex = sex
        self.info = info

创建模型

@app.route('/create_all/')
def create_all():
    db.create_all()
    return '创建表'

删除模型

#删除表  只删除和模型类重名的表
@app.route('/drop_all/')
def drop_all():
    db.drop_all()
    return '删除表'

四、增删改查

(1) 添加一条数据 add

#添加数据
@app.route('/insert_one/')
def insert_one():
    # u = User(username='张三',age=18,info='张三的个人简介')
    try:
        u = User('李四',20,False,'李四的个人简介')
        db.session.add(u)
        db.session.commit() #提交
    except:
        db.session.rollback() #回滚
    return '添加一条数据'

(2) 添加多条数据 add_all

#添加多条数据
@app.route('/add_many/')
def add_many():
    u1 = User('王五',30)
    u2 = User('赵六',40)
    db.session.add_all([u1,u2])
    db.session.commit() #提交
    return '添加多条数据'

(3) 修改

#修改1
@app.route('/update/')
def update():
    u = User.query.get(1) #查询id为1的数据对象
    # print(u)
    u.info = '坚持到无能为力'
    db.session.add(u)
    db.session.commit()
    return '修改'

(4) 删除 delete

#删除
@app.route('/delete/')
def delete():
    u = User.query.get(5)
    db.session.delete(u) #删除
    db.session.commit()
    return '删除'

五、拆分MVT(简单的拆分)

project/
    App/
        __init__.py
        views/
        models/
    settings.py #配置文件
    exts.py #extensions 加载第三方扩展库
    manage.py

六、设置提交方式

(1) 设置为自动提交

app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True

不需要在db.session.commit()

(2) 自定义增删的基础类

class Base:
    #自定义添加一条数据的方法
    def save(self):
        try:
            db.session.add(self)
            db.session.commit()
        except:
            db.session.rollback()

    #自定义添加多条的方法
    @staticmethod
    def save_all(*args):
        try:
            db.session.add_all(args)
            db.session.commit()
        except:
            db.session.rollback()
    #自定义删除方法
    def delete(self):
        try:
            db.session.delete(self)
            db.session.commit()
        except:
            db.session.rollback()

使用

#创建模型
class User(Base,db.Model):

views.py

@view.route('/insert_one/')
def insert_one():
    # u = User('JQKA',16,False)
    # db.session.add(u)
    # db.session.commit()
    sexList = [True,False]
    username = random.randrange(11111, 99999)
    u1 = User(username, random.randint(1, 70), sexList[random.randint(0, 1)], str(username) + '的个人说明')
    u1.save() #添加
    return '添加数据'


@view.route('/insert_many/')
def insert_many():
    sexList = [True,False]
    # sexList[random.randint(0,2)]
    username = random.randrange(11111,99999)
    u1 = User(username,random.randint(1,70),sexList[random.randint(0,1)],str(username)+'的个人说明')
    username = random.randrange(11111, 99999)
    u2 = User(username, random.randint(1, 70), sexList[random.randint(0,1)], str(username) + '的个人说明')
    # db.session.add_all([u1,u2])

    User.save_all(u1,u2) #添加多条
    return '添加多条数据'

@view.route('/delete/')
def delete():
    User.query.get(12).delete()
    return '删除'

七、数据库操作

查询集

查询数据的集合

分类

  1. 原始查询集

    就是通过类名.query得到的就是原始查询集

  2. 数据查询集

    通过过滤的方法 最终拿到的数据就三数据查询集

(1) all() 得到所有数据查询集 返回列表

类名.query.all()

@view.route('/all/')
def all():
    data = User.query.all()
    return render_template('show.html',data = data)

(2) filter() 过滤 默认没有条件 返回所有

类名.query.filter([类名.属性名 比较运算符 值。。。])

@view.route('/filter/')
def filter():
    # data = User.query.filter() #返回所有
    data = User.query.filter(User.age<60,User.age>10) #查询 年龄在59-11之间
    return render_template('show.html',data=data)

(3) filter_by() 只支持单条件查询

没有条件 则返回所有

属性=值,,,,,,

类名.query.filter_by(username='张三'....)

@view.route('/filter_by/')
def filter_by():
    # data = User.query.filter_by() #返回所有
    data = User.query.filter_by(sex=True) #查询性别为True的所有数据
    return render_template('show.html',data=data)

(4) offset(num) 偏移量

@view.route('/offset/')
def offset():
    data = User.query.offset(5) #偏移5条数据 从第六条开始取
    return render_template('show.html',data=data)

(5) limit(num) 取值

@view.route('/limit/')
def limit():
    data = User.query.limit(5) #取出5条数据
    return render_template('show.html',data=data)

(6) order_by() 排序

默认升序

-类名.属性名

@view.route('/order_by/')
def order_by():
    # data = User.query.order_by(User.age) #按照年龄升序
    data = User.query.order_by(-User.age) #按照年龄降序
    return render_template('show.html',data=data)

(7) first() 第一条

类名.query.first()

@view.route('/first/')
def first():
    # data = User.query.first() #取出第一条数据
    data = User.query.order_by(-User.age).first() #取出第一条数据
    print(data)
    return 'first'

(8) get(id值) 根据ID值进行查询

查询成功返回 对象

查询失败 返回None

@view.route('/get/')
def Get():
    data = User.query.get(1)
    print(data)
    return 'get'

(9) contains() 包含关系

@view.route('/contains/')
def contains():
    data = User.query.filter(User.username.contains('9')) #查询包含数字9的数据
    return render_template('show.html',data=data)

(10) like 模糊查询

@view.route('/like/')
def like():
    # data = User.query.filter(User.username.like('%9%')) #包含9的数据
    # data = User.query.filter(User.username.like('%4')) #4作为结尾
    data = User.query.filter(User.username.like('5%')) #5作为开头
    return render_template('show.html',data=data)

(11) startswith 以...开头 endswith 以...结尾

@view.route('/with/')
def With():
    # data = User.query.filter(User.username.startswith('9')) #以9作为开头
    data = User.query.filter(User.username.endswith('4')) #以4作为结尾
    return render_template('show.html',data=data)

(12) 比较运算符

  1. __gt__大于
  2. __lt__ 小于
  3. __ge__ 大于等于
  4. __le__ 小于等于
  5. >=
  6. <=
  7. ==
  8. !=
  9. >
  10. <

实例

@view.route('/bj/')
def bj():
    #年龄 大于30  and  年龄小于50
    data = User.query.filter(User.age.__gt__(30),User.age.__lt__(50))
    return render_template('show.html',data=data)

(13) in 和 not in 在...范围内 不再 ...范围内

@view.route('/in/')
def In():
    #查询id在 1,2,3,4,12,13,14范围内的数据
    # data = User.query.filter(User.id.in_([1,2,3,4,12,13,14]))
    # 查询id不在 1,2,3,4,12,13,14范围内的数据
    data = User.query.filter(~User.id.in_([1,2,3,4,12,13,14]))
    return render_template('show.html',data=data)

(14) null 空

@view.route('/null/')
def null():
    #查询username为null的数据
    # data = User.query.filter(User.username.is_(None))
    # data = User.query.filter(User.username==None)

    #查询部位null的数据
    # data = User.query.filter(User.username.isnot(None))
    # data = User.query.filter(~User.username.is_(None))
    data = User.query.filter(User.username!=None)

(15) and_ 逻辑与查询

from sqlalchemy import and_

默认为and查询

@view.route('/and/')
def And():
    # data = User.query.filter(User.sex==True,User.id<10)
    data = User.query.filter(and_(User.sex==True,User.id<10))
    return render_template('show.html',data=data)

(16) or_ 逻辑或

from sqlalchemy import or_

@view.route('/or/')
def Or():
    data = User.query.filter(or_(User.sex==True,User.id<10))
    return render_template('show.html',data=data)

(17) and和 or的操作

@view.route('/or/')
def Or():
    data = User.query.filter(and_(User.username.contains('9')),or_(User.sex==True,User.id<10))
    #select * from user where username like '%9%' and (sex=1 or id<10)
    return render_template('show.html',data=data)

(17) not_ 逻辑非

from sqlalchemy import not_

@view.route('/not/')
def Not():
    #只能一个条件
    data = User.query.filter(not_(User.sex==True))
    return render_template('show.html',data=data)

(18) count 统计

@view.route('/count/')
def count():
    #统计数据条数
    data = User.query.count()
    print(data)
    return 'count'
    # return render_template('show.html',data=data)

一、文件迁移

(1) 安装

sudo pip3 install flask-migrate

sudo pip3 install flask-script

(2) 使用

from flask_script import Manager
from flask_migrate import Migrate,MigrateCommand

migrate = Migrate(app) #实例化迁移对象
manager = Manager(app)
manager.add_command('db',MigrateCommand) #添加迁移命令
...
if __name__ == '__main__':
    manager.run()

(3) 生成迁移目录

python3 manage.py db init

此刻会在项目中生成一个叫migrations的目录

(4) 生成迁移文件

python3 manage.py db migrate

(5) 执行迁移文件(更新数据库)

python3 manage.py db upgrade

三、拆分MVT的

blog/
    App/
        static/     #静态目录
            img/
            css/
            js/
            upload/
        templates/  #模板目录
            common/
                base.html
        models/     #模型目录
            __init__.py
        views/      #视图目录
            __init.py   
        forms/      #表单目录
            __init__.py
        settings.py #配置文件
        extensions.py   #加载第三方扩展库
        email.py    #邮件
        __init__.py #App包的初始化
    migrations/     #迁移目录
    venv/           #虚拟环境
    manage.py       #启动文件

分页显示博客

paginate 分页类  返回一个分页对象 pagination
paginate实例化传参
    page    必须参数 当前页码
    per_page  每页显示数据的条数  默认20
    error_out 当查询出错的时候  是否报404的错误 默认为True
    
pagination对象
    属性:
        items   当页的所有数据
        page    当前页码
        pages   总页码是
        total   总数据条数
        per_page    每页多少条
        prev_num    上一页的页码
        next_num    下一页的页码
        has_prev    是否有上一页
        has_next    是否有下一页
    方法:
        prev    上一页的分页对象
        next    下一页的分页对象
        iter_pages  是一个迭代器 返回所有的页码数 显示不下则显示 ...

缓存

安装:

sudo pip3 install flask-cache

使用

from flask_cache import Cachce

cache = Cache(app,config={"CACHE_TYPE":"缓存类型"})

实例

extension.py

from flask_cache import Cache
cache = Cache(config={"CACHE_TYPE":'simple'})#实例化缓存 简单的缓存
cache.init_app(app=app) #缓存

main.py

from App.extensions import cache

@main.route('/')
@cache.cached(timeout=120) #timeout 过期的时间
def index():
    print('你能看到我几次。。。')

缓存类型

  1. @cache.cached(timeout=120) #只是缓存页面
  2. @cache.memoize(timeout=120) #会根据不同传参进行不同的缓存

改正之后的缓存

from flask import Blueprint,render_template,request,current_app,redirect,url_for
from App.models import Posts
from App.extensions import cache

main = Blueprint('main',__name__)

@main.route('/')
# @cache.cached(timeout=120)
def index():
    return redirect(url_for('main.main_cache',page=1))


@main.route('/show/<int:page>/')
@cache.memoize(timeout=120) #根据不同参数进行数据的缓存
def main_cache(page):
    print('你能看到我几次。。。')
    # try:
    #     page = int(request.args.get('page', 1))
    # except:
    #     page = 1
    p = Posts.query.filter_by(pid=0).order_by(Posts.timestamp.desc()).paginate(page, current_app.config['CON_NUM'],False)
    data = p.items  # 当前页的数据
    return render_template('main/index.html', data=data, pagination=p)

清除缓存的办法

(1) 在settings.py中 配置缓存时间

CACHE_DEFAULT_TIME

(2) 给装饰器添加timeout参数 时间秒

@cache.cached(timeout=120)
@cache.memoize(timeout=120) #根据不同参数进行数据的缓存

(3) 清除所有缓存

cache.clear()

(4) 只清除 memoize的缓存

cache.delete_memoized(缓存的视图函数名称)

(5) 只清除 cached的缓存

cache.delete(key_prefix 缓存前缀)

表多对多的使用

添加多对多数据

u1 = User.query.get(1)
p1 = Posts.query.get(1)
#1号用户 收藏 1号帖子
u1.favorites.append(p1)

查看多对多数据

u1.favorites.all() #查看用户1收藏了那些帖子
p1.users.all() #查看帖子被哪些用户收藏

删除多对多数据

u1 = User.query.get(1)
p1 = Posts.query.get(1)
u1.favorites.remove(p1)

上线部署

(1) 安装

  1. 在要安装项目的目录创建虚拟环境

    virtualenv venv

  2. source activate # 开启虚拟开发环境模式

  3. pip3 install uwsgi # 安装uwsgi

(2) 配置

uwsgi配置文件支持很多格式,我采用.ini格式,命名为uconfig.ini具体内容如下:

[uwsgi]

# 外部访问地址,可以指定多种协议,现在用http便于调试,之后用socket
socket = 0.0.0.0:8000 # uwsgi的监听端口

# 指向项目目录
chdir =  /home/xlg/test/

# flask启动程序文件
wsgi-file = manage.py

# flask在manage.py文件中的app名
callable = app

plugins = python# 这行一定要加上,不然请求时会出现-- unavailable modifier requested: 0 --错误提示

# 处理器数
processes = 1

# 线程数
threads = 2

#状态检测地址
stats = 127.0.0.1:9191

(3) 安装 nginx

Nginx:sudo apt-get install nginx

Nginx是轻量级、性能强、占用资源少,能很好的处理高并发的反向代理软件。Ubuntu 上配置 Nginx 也是很简单,不要去改动默认的 nginx.conf 只需要将/etc/nginx/sites-available/default文件替换掉就可以了。
新建一个 default 文件:

server{
listen  80; # 服务器监听端口
        server_name 192.168.100.136; # 这里写你的域名或者公网IP
        location / {
                uwsgi_pass      127.0.0.1:8000; # 转发端口,需要和uwsgi配置当中的监听端口一致
                include uwsgi_params; # 导入uwsgi配置
                #uwsgi_param UWSGI_PYTHON /home/自己创建的目录/venv; # Python解释器所在的路径(这里为虚拟环境)
                uwsgi_param UWSGI_PYTHON /usr/bin/python3;  
                uwsgi_param UWSGI_CHDIR  /home/xlg/test/;# # 自己创建的目录 项目根目录
                uwsgi_param UWSGI_SCRIPT manage:app; # 指定启动程序
                #比如你测试用test.py文件,文件中app = Flask(name),那么这里就填 test:app
        }
}

服务启动

  1. sudo service start
  2. sudo service stop
  3. sudo service restart

指定配置文件,后台运行 uwsgi, 这时再刷新一下之前打开的页面,就可以看到应用正常运行了。

uwsgi uconfig.ini

访问地址

192.168.100.136

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容