欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

10.Flask

程序员文章站 2022-07-15 13:09:57
...

Flask

常用包

Flask==1.0.2                    # 主程序
flask-blueprint==1.2.2           # 蓝图:管理url
Flask-Script==2.0.6             # 提供向Flask插入外部脚本的功能,例如Manager.py

Flask-SQLAlchemy==2.3.2          # 链接数据库(postgresql,MySQL,Oracle,SQLite)
PyMySQL==0.8.1                  # mysql数据库驱动
redis==2.10.6                   # redis数据库驱动
Flask-Session==0.3.1            # redis数据存储用

Flask-DebugToolbar==0.10.1       # 网页调试工具
Flask-RESTful==0.3.6             # api接口
flask-marshmallow==0.9.0         # 序列化数据

环境搭建

  • 创建虚拟环境:virtualenv –no-site-packages flaskenv
    cd flaskenv
    cd Script
    启动虚拟环境:activate
  • 安装flask:pip install flask

第一个小程序

# hello.py

from flask import Flask
# from flask_script import Manager # 方法2启动用到


# 初始化主模块名或者包
app = Flask(__name__)

# manager = Manager(app=app)     # 方法2启动用到


#  路由(127.0.0.1:5000)
@app.route('/')
def hello():
    # 视图函数
    return 'hello world'


@app.route('/hello/<name>/')
# 默认从url获取的值为str类型(可以匹配任意字符)
def hello_man(name):
    print(type(name))
    return 'hello ' + str(name)


@app.route('/helloint/<int:id>/')
# 指定从url匹配int数据(否则报错)
def hello_int(id):
    print(id)
    print(type(id))
    return 'hello int:%s' % id

if __name__ == '__main__':
    app.run(debug=True, port=5200, host='0.0.0.0')
    # manager.run()              # 方法2启动用到

启动

方法1

直接启动hello.py文件:python hello.py(虚拟环境中)

启动默认地址及端口:127.0.0.1:5000

  • 自定义启动地址及方式:(在app.run()中加入参数)
    • 是否打开调试:debug=True/False
    • 指定其他端口:port=xxx
    • 指定其他IP:host=x.x.x.x
    • eg:app.run(debug=True, port=5200, host=’0.0.0.0’)

方法 2

  • 安装flask-script(虚拟环境中)

    pip install flask-script

  • 导入:

    from flask_script import Manager

  • 指定启动app

    manager = Manager(app=app)

  • 把运行条件中的app.run()替换:

    manager.run()

  • 在虚拟环境中启动项目:

    python hello.py runserver -p 8080 -h 0.0.0.0 -d

    • -p:定义启动端口
    • -h:定义启动IP
    • -d:打开调试功能
    • Debugger PIN:网页调试功能PIN

更改成MTV模式(附加传参)

  • 安装blueprint库

    blueprint库(蓝图)是一个url管理Flask库

    pip install flask-blueprint
  • 更改hello.py文件为manage.py,并拆分代码

    
    # manage.py
    
    
    
    # 把原来的hello.py文件改为manage.py,删除视图函数和app定义代码,并把剩余代码改造成如下代码
    
    from flask_script import Manager
    from app import create_app
    
    blue = create_app()
    manager = Manager(app=blue)
    
    if __name__ == '__main__':
    
      manager.run()
    
    
    # 视图函数代码将放入views.py中
    
    
    # app定义代码放入__init__.py中
    
  • 创建一个文件夹app,作为一个项目的app

    • 在app下创建__init__.py文件,定义app文件夹为一个python包
    
    # __init__.py
    
    
    # 把初始化的内容写入__init__.py中
    
    from flask import Flask
    from app.views import blue
    
    def create_app():
        app = Flask(__name__)
        app.register_blueprint(blueprint=blue)  # 路由注册
        return app
    • 创建views.py,并写入如下代码
    
    # views.py
    
    
    from flask import Blueprint # 管理url
    from flask import send_file # 一个返回超文本(如:html)给浏览器的方法
    
    
    blue = Blueprint('first', __name__) # 定义app名,把原来@app改成@blue
                                # 这个'first'在页面跳转时用到{redirect(url_for('first.xxx'))}
    
    #  路由(127.0.0.1:5000)
    
    @blue.route('/')
    def hello():
        # 视图函数
        return 'hello world'
    
    
    @blue.route('/hello/<name>/')        # 注意路由书写中不要遗漏前斜线“/”
    def hello_man(name):
        print(type(name))
        return 'hello ' + str(name)
    
    
    @blue.route('/helloint/<int:id>/')                   {#获取固定类型的参数#}
    def hello_int(id):                                {# int 整形 #}
        print(id)                                     {# float 浮点数 #}
        print(type(id))                               {# uuid  #}
        return 'hello int:%s' % id                     {# string 默认 #}
                                                     {# path 会取到‘/’ #}
    
    @blue.route('/index/')
    def index():
        return send_file('../templates/hello.html') # 模板文件访问方法
    
  • 模板templates

    Flask中模板不需要额外设置,在工程目录下创建templates文件夹,里面放入HTML文件,在用下面代码就可以访问:

    注意:再使用MTV模式前后,调用templates方法不一样!

    
    # 在利用MTV模式之前
    
    from flask import render_template
    
    @app.route('/index/')
    def index():
      return render_template('hello.html')
    
    
    # 在利用MTV模式之后
    
    @blue.route('/index/')
    def index():
      return send_file('../templates/hello.html')
    
    
    ## 注意:如果要给HTML样式,则在项目下创建一个放置css/js的文件夹,然后再HTML文件中,利用link和script标签引入css/js文件
    

定义templates和static目录

在__init__.py文件中定义项目文件夹,再用join连接自定义目录

 # __init__.py中

from flask import Flask
from app.views import blue
import os


BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


def create_app():
    templates_dirs = os.path.join(BASE_DIR, 'templates')
    static_dirs = os.path.join(BASE_DIR, 'static')
    app = Flask(__name__, template_folder=templates_dirs, static_folder=static_dirs)
    app.register_blueprint(blueprint=blue)
    return app

请求与响应

请求

args—-GET请求参数存放位置

form—-POST请求参数存放位置

files—–上传的file文件存放位置

method——获取请求方式

@blue.route('/getrequest/', methods=['GET', 'POST'])
def get_request():
    if request.method == 'GET':
        args = request.args
    else:
        form = request.form
    return '获取request'
  • 传参格式为特殊的字典,key可以相同,用getlist方法可以获取相应的值,获取结果为一个列表

  • app的url前缀:app.register_blueprint(blueprint=blue, url_prefix=’/hello’)

    加上这句话后再访问blue这个app种的连接时,url前面必须加上/hello

    • 注意:每个url中必须加上前斜杠”/“

响应

自定义响应状态:

@blue.route('/makeresponse/')
def makeresponse():
    response = make_response('<p>贵人</p>')
    return response, 200                            # 自定义响应成功的状态码

页面跳转

@blue.route('/redirect/')
def make_redirect():
    # return redirect('/hello/index/')             # 跳转 第一种方法
    return redirect(url_for('first.index'))        # 跳转 第二种方法 通过别名

异常捕捉及处理

@blue.route('/makeabort/')
def make_abort():
    abort(404)                                     # 引发一个404错误
    return '终结'


@blue.errorhandler(404)                          # 捕获404错误,可以通过这个方法自定义错误页面
def get_error(exception):
    return '捕捉异常%s' % exception

redis连接配置

配置连接如下

# __init__.py

## 准备工作:
# pip install redis
# pip install flask_session

import os
from flask import Flask
from flask_session import Session
import redis
from app.views import blue


def creat_app():
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    templates_dir = os.path.join(BASE_DIR, 'templates')
    static_dir = os.path.join(BASE_DIR, 'static')

    app = Flask(__name__,template_folder=templates_dir,static_folder=static_dir)

    app.register_blueprint(blueprint=blue, url_prefix='/app')

    # **
    app.config['SECRET_KEY'] = 'secret_key'

    # 使用redis存储信息,默认访问redis了,127.0.0.1:6379
    # 需要pip install redis
    app.config['SESSION_TYPE'] = 'redis'

    # 访问redis
    app.config['SESSION_REDIS'] = redis.Redis(host='127.0.0.1', port='6379')

    # 定义session前缀
    app.config['SESSION_KEY_PREFIX'] = 'flask'

    # 第一种初始化app
    Session(app)

    # 第二种初始化方法
    # se = Session()    
    # se.init_app(app)

    return app

session和cookie

  • session例子

    @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('app.login'))                     # 页面跳转
  • cookie例子

    @blue.route('/getresponse/')
    def get_response():
    
      response = make_response('<h2>你是大帅逼</h2>')
      ticket = ''
      s = 'abcdefghijklmnopqrlstuvwxyz'
      for i in range(20):
          ticket += random.choice(s)
      response.set_cookie('ticket',ticket, max_ages='', expire='')  # 设置ticket
      return response
    
    
    @blue.route('/deletecookie/')
    def del_cookie():
      response = make_response('<h2>你是大帅逼</h2>')
      response.delete_cookie('ticket')                              # 删除ticket
      return response

jinja2语言补充

{% block content %}
    <ul>
    {% for cc in scores_list %}                        {# scores_list = [1.2, 2, 6, 7] #}
        {% if loop.first %}                                   {# 第一个循环 #}
            <li style="color: blue">
                {{ loop.revindex }}:{{ loop.index }}:{{ cc }}  {# loop.revindex:倒序序号#}
            </li>                                          {# loop.index:正序序号#}
        {% elif loop.last %}                                 {# 最后一个循环 #}
            <li style="color: red">
                {{ loop.revindex }}:{{ loop.index }}:{{ cc }}
            </li>
        {% else %}
            <li>{{ loop.revindex }}:{{ loop.index }}:{{ cc }}</li>
        {% endif %}
    {% endfor %}
    </ul>
    <hr>
    <ul>                                      {# content_p='<p>今天你很叼</p>' #}
        <li>{{ content_p }}</li>                            {# 原始数据 #}
        <li>{{ content_p |safe}}</li>                       {# 渲染并加样式 #}
        <li>{{ content_p |striptags}}</li>                  {# 渲染但不加样式 #}
        <li></li>                               
        <li>{{ content_h3 }}</li>               {# content_h3 = '  <h3>你是大帅哥</h3>  ' #}
        <li>{{ content_h3|length }}</li>                    {# 计算长度,空格也算 #}
        <li>{{ content_h3|safe }}</li>                     {# 渲染,但是前后的空格将不被显示 #}
        <li>{{ content_h3|trim|safe }}</li>                {# 去掉前后空格后,渲染 #}
        <li>{{ content_h3|trim|length }}</li>              {# 去掉前后空格后,计算长度 #}
    </ul>
    <hr>
    <ul>
        {% for i in config %}                    {# config 是一个全局的变量 #}
            <li>
                第一个字母{{ i|first }},
                最后一个字母{{ i|last }},
                小写{{ i|lower }},
                大写{{ i|upper }},
                首字母大写{{ i|capitalize }}
            </li>
        {% endfor %}
    </ul>
    <hr>                                     {# 定义的函数引用 #}
    {% from 'func.html' import show_goods %}
    {% from 'func.html' import say %}

    {{ show_goods('可爱多', '1') }}
    <br>
    {{ show_goods('梦龙','2') }}
    <hr>
    {{ say() }}
{% endblock %}


{# func.html #}                             {# 定义函数 (宏操作)#}

{% macro say() %}
    <h3>你是真骚</h3>
    <h2>没无敌骚</h2>
{% endmacro %}

{% macro show_goods(goods_name, goods_id) %}
    商品的名称:{{ goods_id }}
    商品的名称:{{ goods_name }}
{% endmacro %}

连接mysql数据库及简单操作

  • 提前安装的包
    • pip install flask-sqlalchemy
    • pip install pymysql

配置

# __init__.py

import os
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from stu.views import stu


def create_app():
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    templates_dir = os.path.join(BASE_DIR, 'templates')
    static_dir = os.path.join(BASE_DIR, 'static')
    app = Flask(__name__, template_folder=templates_dir, static_folder=static_dir)
    app.register_blueprint(blueprint=stu, url_prefix='/stu')

    # 配置mysql数据库连接:'mysql+pymysql://mysql用户:mysql密码@mysql地址:mysql端口/具体数据库名'
    app.config['SQLALCHEMY_DATABASE_URI'] = 
                                    'mysql+pymysql://root:aaa@qq.com:3306/flask3'

    # 追踪对象的修改并且发送信号(需要额外的内存)且为必须写
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

    # 初始化app
    SQLAlchemy(app=app)             
    return app

定义数据表

# 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(20), unique=True)
    s_age = db.Column(db.Integer, default=18)

    __tablename__ = 'student'

对数据CRUD

# views.py
import random
from flask import render_template, redirect, url_for
from stu.models import db, Student


@stu.route('/createtable/')                      # 创建所有定义的数据表
def create_table():
    db.create_all()
    return '创建成功'


@stu.route('/dorptable/')                        # 删除所有创建的数据表 
def dorp_table():
    db.drop_all()
    return '删除成功'


@stu.route('/creatstu/')                         # 在数据表种创建数据
def create_stu():
    stu = Student()
    stu.s_name = '香茗%d' % random.randrange(1000)
    stu.s_age = '%d' % random.randrange(20)
    db.session.add(stu)
    try:
        db.session.commit()                     # 数据库种的数据如果没有创建成功, 则回滚前面操作
    except:
        db.session.rollback()
    return '创建成功'


@stu.route('/stulist/')                           # 查询所有数据
def stu_all():
    # 第一种方法
    # stus = Student.query.all()

    # 第二种方法(原生sql)
    sql = 'select * from student'
    stus = db.session.execute(sql)

    return render_template('stulist.html', stus=stus)


@stu.route('/stutail/')                                          # 筛选数据
def stu_detail():
    # # 使用原生sql
    # sql = 'select * from student where s_name="香茗139"'
    # stus = db.session.execute(sql)

    # # 使用filter
    # stus = Student.query.filter(Student.s_name=="香茗139")

    # 使用filter_by
    stus = Student.query.filter_by(s_name="香茗511")

    return render_template('stulist.html', stus=stus)


@stu.route('/updatestu/')                                       # 更新数据
def update_stu():

    # # 第一种方式
    # stu = Student.query.filter_by(s_id=5).first()
    # stu.s_name = '你滚'
    # db.session.commit()

    # 第二种方式
    # Student.query.filter(Student.s_id==5).update({'s_name':'王大锤'})
    Student.query.filter_by(s_id = 5).update({'s_name': '王二锤'})
    db.session.commit()  # 不加commit不会更新到数据库,只修改了缓存的变量值

    return redirect(url_for('stu.stu_all'))


@stu.route('/deletestu/')                                       # 删除数据
def delete_stu():

    stu = Student.query.filter_by(s_id=5).first()
    db.session.delete(stu)
    db.session.commit()

    return redirect(url_for('stu.stu_all'))

mysql操作

数据创建

  • 随机创建
@stu.route('/addstus/')
def addstus():
    stus_list = []
    for i in range(10):
        s_name = '小米%s' % i
        s_age = random.randrange(1,39)
        stu = Student(s_name, s_age)
        stus_list.append(stu)
    try:
        db.session.add_all(stus_list)
        db.session.commit()
        return '成功'
    except:
        db.session.rollback()
        return '失败'
  • 前端输入创建
@stu.route('/createstus/', methods=['GET', 'POST'])
def create_stus():

    if request.method == 'GET':
        return render_template('create_stus.html')

    if request.method == 'POST':
        stus_list = []

        username1 = request.form.get('username1')
        age1 = request.form.get('age1')

        username2 = request.form.get('username2')
        age2= request.form.get('age2')

        stu1 = Student(username1, age1)
        stu2 = Student(username2, age2)

        stus_list.append(stu1)
        stus_list.append(stu2)

        db.session.add_all(stus_list)
        db.session.commit()

        return '创建成功'
  • 利用键值对的元组创建
@grade.route('/addgrade/')
def addgrade():
    name= {'python':'人生苦短我用python',
            'h5':'我是\(^o^)/~',
            'java':'加瓦',
            'go':'狗狗'
            }
    grade_list = []
    for key in name:
        grade = Grade(key, name[key])
        grade_list.append(grade)
    db.session.add_all(grade_list)
    db.session.commit()

    return '创建班级数据成功'

数据筛选

a b c
__lt__ 小于 in_ 在范围内 order_by 排序
__le__ 小于等于 and_ 并且条件 limit 截取几个信息
__gt__ 大于 or_ 或者条件 offset 跳过几个信息
__ge__ 大于等于 not_ 非 get 获取主键对应的信息
from sqlalchemy import and_, or_, not_

@stu.route('/selectstu/')
def selectstu():
    # __lt__小于, __le__小于等于, __gt__大于, __ge__大于等于
    stus = Student.query.filter(Student.s_age < 30)
    stus = Student.query.filter(Student.s_age.__lt__(30))

    # 筛选年龄再列表中的学生 in_()
    stus = Student.query.filter(Student.s_age.in_([22, 32, 15, 1]))

    # 获取所有学生信息
    sql = 'select * from student;'
    stus = db.session.execute(sql)

    stus = Student.query.all()

    # 按照id降序排 order_by()
    stus = Student.query.order_by('-s_id')

    # 获取年龄最大的一个 limit()
    stus = Student.query.order_by('-s_age').limit(1)
    stus = Student.query.order_by('-s_age').first()         # 不可以迭代

    # 跳过3个数据,查询5个信息 offset()
    stus = Student.query.order_by('-s_age').offset(3).limit(5)

    # 返回去掉前面3个数据的剩余数据
    stus = Student.query.order_by('-s_age').offset(3)

    # 获取id 等于25的学生信息 get()
    stus = Student.query.filter(Student.s_id == 25)
                        # 后面可以跟上all(),不加all()返回的是一个flask类,加all()返回一个列表
    stus = Student.query.get(25)                        # 不可迭代

    # 查询多个条件
    stus = Student.query.filter(Student.s_age == 12, Student.s_name == 'ccc')

    # and_ 并且条件 and_()
    stus = Student.query.filter(and_(Student.s_age == 12, Student.s_name == 'ccc'))

    # or_ 或者条件 or_()
    stus = Student.query.filter(or_(Student.s_name == '小米5', Student.s_age == 12))

    # not_ 非 not_()
    stus = Student.query.filter(not_(Student.s_age == 12), Student.s_name == '小米7')

    return render_template('student_list.html', stus=stus)

一对多(one to many)

数据表创建

many方:学生表

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(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

one方:班级表

from _datetime import datetime
from stu.models import db


class Grade(db.Model):

    g_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    g_name = db.Column(db.String(20), 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='grade', lazy=True)

    __tablename__= 'grade'

    def __init__(self, name, desc):
        self.g_name = name
        self.g_desc = desc

利用外键查询数据

  • 利用班级查询学生(one—->many)
@grade.route('/selectstubygrade/<int:id>/')
def select_stu(id):

    grade = Grade.query.get(id)
    students = grade.students                   # 本表关系定义:students

    return render_template('getstubygrade.html', stus=students, grade=grade)
  • 利用学生查班级(many—->one)
@stu.route('/selectgradebystu/<int:id>/')
def select_grade(id):

    stu = Student.query.get(id)
    grade = stu.grade                        # 关联表中关系定义中的:backref='grade'

    return render_template('selectgradebystu.html', stu=stu, grade=grade)

详细分析relationship

  • 在many一方表中写外键:

    s_g = db.Column(db.Integer, db.ForeignKey('grade.g_id'), nullable=True)
    • 必须最先定义类型:db.Integer
    • 必须用小写表示外键关联的字段,并用引号包括:db.ForeignKey(‘grade.g_id’)
    • 设置为空值的方法和django不同:nullable=True
  • 在one一方写关联关系:

    students = db.relationship('Student', backref='grade', lazy=True)
    • 第一个为关联表的类名(注意大小写):‘Student’
    • 第二个参数为从many查询one时调用的方法:backref=’grade’
    • 第三个参数为延迟加载:表示只有在调用关系方法时才加载:lazy=True

多对多(many to many)

数据表创建

学生表:和一对一种的学生表一样

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

中间表:

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('course.c_id'), primary_key=True)
              )

课程表:

class Course(db.Model):
    c_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    c_name = db.Column(db.String(10), unique=True)
    students = db.relationship('Student', secondary=sc, backref='cou', )

    __tablename__ = 'course'

    def __init__(self, name):

        self.c_name = name
  • 多对多的关系定义中:
    • 只定需声明多对多的关系和创建一个存储这个关系的中间表
    • 声明关系可以在任意一个表中进行
    • 声明多对多关系比声明一对多关系多一个参数:secondary=sc(声明中间表)

课程表数据创建

@grade.route('/addcourse/')
def add_course():
    courses = ['高数', '线性代数', '大学物理', '单片机', '计算机']
    sourse_liste = []
    for course in courses:
        cc = Course(course)
        sourse_liste.append(cc)
    db.session.add_all(sourse_liste)
    db.session.commit()
    return '添加课程数据成功'

中间表数据创建

@stu.route('/stucourse/', methods=['GET', 'POST'])  # 这里两种请求都允许进入
def stu_cou():
    if request.method == 'GET':
        stus = Student.query.all()
        cous = Course.query.all()
        return render_template('stu_cou.html', stus = stus, cous = cous)
    else:
        s_id = request.form.get('student')
        c_id = request.form.get('course')

        # # 第一种方法 原生sql方法
        # sql = 'insert into sc (s_id, c_id) value (%s, %s);' % (s_id, c_id)
        # db.session.execute(sql)
        # db.session.commit()
        # return '插入成功'

        # # 第二种方式 一次只能插入一组数据
        # stu = Student.query.get(s_id)
        # cou = Course.query.get(c_id)
        #
        # cou.students.append(stu)  # 删除 把append改成remove,这里只是删除关系,只移除中间表数据
        # db.session.add(cou)       # 注意:这里添加关系时,反过来写也可以
        # db.session.commit()
        # return '插入成功'

        # 第三种方法 :插入多条 利用了form存储的数据中允许有相同key值,也可以说是一个key可以有多个值
        c_ids = request.form.getlist('course')
        stu = Student.query.get(s_id)
        for c in c_ids:
            cou = Course.query.get(c)
            cou.students.append(stu)
        db.session.commit()
        return '插入成功'

前端页面:stu_cou.html(插入多头数据)

{% extends 'base_main.html' %}


{% block title %}学生选课系统{% endblock %}
{% block content %}
    <form action="" method="post">                {# 不要遗漏method#}
        <h3>学生信息</h3>
        <br>
        <select name="student" id="">
            <option>请选择学生信息</option>        {# 选择学生 #}
            {% for stu in stus %}
                <option value="{{ stu.s_id }}">{{ stu.s_name }}</option>
            {% endfor %}
        </select>
        <br>
        <h3>课程信息</h3>
        <select name="course" id="">
            <option >请选择课程信息</option>       {# 选择课程 ,select可以有多个但name必须一样#}
            {% for cou in cous %}
                <option value="{{ cou.c_id }}">{{ cou.c_name }}</option>
            {% endfor %}
        </select>
        <br>
        <select name="course" id="">
            <option >请选择课程信息</option>       {# 选择课程 #}
            {% for cou in cous %}
                <option value="{{ cou.c_id }}">{{ cou.c_name }}</option>
            {% endfor %}
        </select>
        <br>
        <input type="submit" value="提交">
    </form>
{% endblock %}

利用数据表查询数据

  • 方法同一对多
@stu.route('/selectcoursebystu/<int:id>/')                 # 通过学生id查询课程
def select_course_by_stu(id):

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

    return render_template('stucourse.html', cous = cous, stu=stu)


@stu.route('/delectcoursebyid/<int:s_id>/<int:c_id>/')   # 通过学生id删除指定id的课程
def delete_cou(s_id, c_id):

    stu  = Student.query.get(s_id)
    cou = Course.query.get(c_id)

    cou.students.remove(stu)
    db.session.commit()

    return redirect(url_for('stu.all_stu'))

一对一(one to one)

  • 一对一的方法就是把一对多的外键设置成唯一

    s_g = db.Column(db.Integer, db.ForeignKey('grade.g_id'), nullable=True, unique=True)

分页

paginate对象
pages 总页数 has_next 是否有下页 has_prev 是否有上页 total 总条数
prev_num下一页页码 next_num上一页页码 iter_pages() 当前一共多少页[1,2,3]
@stu.route('/stupage/')
def stu_page():
    page = int(request.args.get('page', 1))                       # 获取要显示页的页码
    per_page = int(request.args.get('per_page', 5))               # 每页数据个数

    paginate = Student.query.order_by('-s_id').paginate(page,per_page,error_out=False)
    stus = paginate.items                                      # 列出所有当前页的数据

    return render_template('stupage.html', paginate=paginate, stus=stus)
{% block content %}

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

    {% if paginate.has_next %}
        <a href="/stupage/?page={{ paginate.next_num }}">下一页</a>
    {% endif %}
    <br>
    页码:{% for i in paginate.iter_pages() %}
            <a href="/stupage/?page={{ i }}">{{ i }}</a>
        {% endfor %}

{% endblock %}

常用插件

Flask-DebugToolbar

Setting up the debug toolbar is simple:

from flask import Flask
from flask_debugtoolbar import DebugToolbarExtension

app = Flask(__name__)


# the toolbar is only enabled in debug mode:

app.debug = True


# set a 'SECRET_KEY' to enable the Flask session cookies

app.config['SECRET_KEY'] = '<replace with a secret key>'

toolbar = DebugToolbarExtension(app)

The toolbar will automatically be injected into Jinja templates when debug mode is on. In production, setting app.debug = False will disable the toolbar.

  • pip install flask-debugtoolbar
  • 
    # func.py中
    
    from flask_debugtoolbar import DebugToolbarExtension
    
    debugtoolbar = DebugToolbarExtension()
    
    def init_ext(app):                                        # 初始化app函数
    
      db.init_app(app)
      debugtoolbar.init_app(app)
  • 
    # app.py中
    
    
    from utils.func import init_ext
    
    def create_app():
    
      app = Flask(__name__, template_folder=templates_dir, static_folder=static_dir)
    
      app.debug = True                            # debug设置成True,才能启动debugtoolbar
    
      app.register_blueprint(blueprint=stu, url_prefix='/stu')
      app.register_blueprint(blueprint=grade, url_prefix='/grade')
      app.config['SQLALCHEMY_DATABASE_URI'] = SQLALCHEMY_DATABASE_URI
      app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
      app.config['SECRET_KEY'] = 'asd'
    
      init_ext(app)                         # 初始化app
    
      return app
  • 效果图

    10.Flask

Flask-RESTful

 for Flask that adds support for quickly building REST APIs.

pip install Flask-RESTful
# func.py中

from flask_restful import Api

api = Api()

def init_ext(app):

    db.init_app(app)
    debugtoolbar.init_app(app)
    api.init_app(app)
# views.py中

from utils.func import api
from flask_restful import Resource

class HelloStudent(Resource):

    def get(self, id):
        stu = Student.query.get(id)
        data = {
            'name':stu.s_name,
            'age':stu.s_age
        }
        return data

api.add_resource(HelloStudent, '/api/stu/<int:id>/')            # 访问api接口不用加蓝图前缀

flask-marshmallow

Flask + marshmallow for beautiful APIs

pip install flask-marshmallow
# func.py中

from flask_marshmallow import Marshmallow

ma = Marshmallow()

def init_ext(app):
    db.init_app(app)
    debugtoolbar.init_app(app)
    api.init_app(app)
    ma.init_app(app)
# stuMarshmallow.py中

from utils.func import ma


class StuMarsh(ma.Schema):

    class Meta:
        fields = ('s_name', 's_age')                # 序列化对象

stumarsh = StuMarsh()
# view.py中

from utils.stuMarshmallow import stumarsh

class HelloStudent(Resource):

    def get(self, id):
        stu = Student.query.get(id)

        return stumarsh.jsonify(stu)              # 直接返回json数据

    def post(self):
        pass

    def patch(self):
        pass

    def put(self):
        pass

    def delete(self):
        pass

api.add_resource(HelloStudent, '/api/stu/<int:id>/')

优化

flask很灵活,也很高性能,属于异步web框架,但是所有功能都需要自己安装相应插件,如果程序员没有布局好会导致结构凌乱,代码重复利用率低:为此我们必须掌握相关优化

首先了解下我整个项目的结构:

10.Flask

首先我们创建一个utils包,里面用来装项目的设置、重复利用代码和自定义函数:

  • __init__.py里面没有代码,只仅仅代表utils是一个包

  • app.py用来创建app,并初始化app

    from flask import Flask
    from stu.views import stu
    from grade.views import grade
    from utils.setting import static_dir, templates_dir, SQLALCHEMY_DATABASE_URI
    from utils.func import init_ext
    
    
    def create_app():
    
      app = Flask(__name__, template_folder=templates_dir, static_folder=static_dir)
    
      app.debug = True                      # debugtoolbar 打开命令
    
      app.register_blueprint(blueprint=stu, url_prefix='/stu')
      app.register_blueprint(blueprint=grade, url_prefix='/grade')
      app.config['SQLALCHEMY_DATABASE_URI'] = SQLALCHEMY_DATABASE_URI
      app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
      app.config['SECRET_KEY'] = 'asd'      # 连接redis、使用debugtoolbar必须要安全**
    
      init_ext(app)                        # 初始化app,详情进func.py查看
    
      return app
  • func.py用来自定义函数

    from flask_sqlalchemy import SQLAlchemy
    from flask_debugtoolbar  import DebugToolbarExtension
    from flask_restful import Api
    from flask_marshmallow import Marshmallow
    
    
    db = SQLAlchemy()
    debugtoolbar = DebugToolbarExtension()
    api = Api()
    ma = Marshmallow()
    
    
    def get_db_url(DATABASE):                         # 拆分mysql连接url
    
      user = DATABASE.get('USER', 'root')
      password = DATABASE.get('PASSWORD','')
      host = DATABASE.get('HOST', 'localhost')
      port = DATABASE.get('PORT', '3306')
      name = DATABASE.get('NAME','')
      db = DATABASE.get('DB','')
      driver = DATABASE.get('DRIVER','')
    
      return '{}+{}://{}:{}@{}:{}/{}'.format(
          db, driver, user, password, host, port, name
      )
    
    
    def init_ext(app):                            # 初始化app
    
      db.init_app(app)                      # 连接数据库
      debugtoolbar.init_app(app)                 # web调试工具
      api.init_app(app)                     # restful启用api
      ma.init_app(app)                      # 序列化字段
  • setting.py用来设置项目

    import os
    from utils.func import get_db_url
    
    
    
    # 基础路径
    
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    
    
    # 页面模板路径
    
    templates_dir = os.path.join(BASE_DIR, 'templates')
    
    
    # 静态模板路径
    
    static_dir = os.path.join(BASE_DIR, 'static')
    
    DATABASE = {
      # 用户
      'USER':'root',
      # 密码
      'PASSWORD':'cfx123CFX',
      # 地址
      'HOST':'127.0.0.1',
      # 端口
      'PORT':'3306',
      # 数据库
      'DB':'mysql',
      # 驱动
      'DRIVER':'pymysql',
      # 数据库名称
      'NAME':'flask3'
    }
    
    # 连接数据库
    
    SQLALCHEMY_DATABASE_URI = get_db_url(DATABASE)
  • stuMarshmallow.py 用来选择序列化字段

    from utils.func import ma
    
    
    class StuMarsh(ma.Schema):
    
      class Meta:
    
          fields = ('s_name', 's_age')
    
    
    stumarsh = StuMarsh()

优化补充

config配置信息优化

  • config配置信息可以单独写一个文件

    
    # config.py
    
    import redis
    
    from utils.setting import SQLALCHEMY_DATABASE_URI
    
    
    class Config():
    
      # MySQL配置
      SQLALCHEMY_DATABASE_URI = SQLALCHEMY_DATABASE_URI
      SQLALCHEMY_TRACK_MODIFICATIONS = False
    
      # redis配置
      SESSION_TYPE = 'redis'
      SECRET_KEY = 'userlogin'
      SESSION_REDIS = redis.Redis(host='127.0.0.1', port='6379')
      SESSION_KEY_PREFIX = 'USER_ID:'
  • config配置在app中得写法:

    def create_app(config):
    
      app = Flask(__name__, template_folder=TEMPLATES_DIR, static_folder=STATIC_DIR)
      app.register_blueprint(blueprint=user, url_prefix='/user')
      app.register_blueprint(blueprint=house, url_prefix='/house')
      app.register_blueprint(blueprint=order, url_prefix='/order')
    
      app.config.from_object(config)   # 把配置信息作为参数
    
      init_ext(app)
    
      return app
  • 导入app

    from flask_script import Manager
    from utils.app import create_app
    from utils.config import Config
    
    
    app = create_app(Config) # 配置config,把Config类作为参数
    manager = Manager(app=app)
    
    
    if __name__ == '__main__':
      manager.run()

创建数据库优化

  • 在models中定义函数,方便数据更新

    class BaseModel(object):
      """定义基础model"""
      create_time = db.Column(db.DATETIME, default=datetime.now())
      update_time = db.Column(db.DATETIME, default=datetime.now(), onupdate=datetime.now())
    
      def add_update(self):
          db.session.add(self)
          db.session.commit()
    
      def delete(self):
          db.session.delete(self)
          db.session.commit()
  • 在models中定义函数,方便添加数据

    class Grade(db.Model):
    
      g_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
      g_name = db.Column(db.String(20), 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='grade', lazy=True)
    
      __tablename__= 'grade'
    
      def __init__(self, name, desc):
          self.g_name = name
          self.g_desc = desc
  • 在models中定义函数,利用jsonify()返回json数据

    class Area(BaseModel, db.Model):
    
      __tablename__='ihome_area'
    
      id = db.Column(db.Integer, primary_key=True) # 区域编号
      name = db.Column(db.String(32), nullable=False)# 区域名字
      houses = db.relationship('House', backref='area') # 区域的房屋
    
      def to_dict(self):
          return {
              'id':self.id,
              'name':self.name
          }
    • 应用方式:
    @house.route('/hindex/', methods=['GET'])
    def house_index():
        user_name = ''
        if 'user_id' in session:
            user = User.query.get(session['user_id'])
            user_name = user.name
    
        houses = House.query.order_by(House.id.desc()).all()[:5]
        hlist = [house.to_dict() for house in houses]
    
        areas = Area.query.all()
        alist = [area.to_dict() for area in areas]
    
        return jsonify(code=status_code.OK,
                       hlist=hlist,
                       alist=alist,
                       user_name=user_name)
  • 在models中定义一个字段可以选则的信息:枚举

    class Order(BaseModel, db.Model):
    
      __tablename__ = 'ihome_order'
    
      id = db.Column(db.Integer, primary_key=True)
      user_id = db.Column(db.Integer, db.ForeignKey('ihome_user.id'), nullable=False)
      house_id = db.Column(db.Integer, db.ForeignKey('ihome_house.id'), nullable=False)
      begin_date = db.Column(db.DateTime, nullable=False)
      end_data = db.Column(db.DateTime, nullable=False)
      days = db.Column(db.Integer, nullable=False)
      house_price = db.Column(db.Integer, nullable=False)
      amount = db.Column(db.Integer, nullable=False)
      status = db.Column(
          db.Enum(
              'WALL_ACCEPT',# 待结单
              'WALL_PAYMENT',# 待支付
              'PAID',# 已支付
              'WALL_COMMENT',# 待评价
              'COMPLETE',# 已完成
              'CANCELED',# 已取消
              'REJECTED'# 已拒单
          ),
          default='WALL_ACCEPT', index=True
      )
      comment = db.Column(db.Text)

最后的manager.py文件

from flask_script import Manager
from utils.app import create_app

app = create_app()
manage = Manager(app=app)


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

项目部署

部署flask:

  1. 更新ubuntu的源
sudo apt-get update
  1. 安装mysql
sudo apt install mysql-server mysql-client
  1. 修改mysql配置
cd /etc/mysql/mysql.conf.d
修改mysqld.conf 讲bind_address注释
  1. 修改配置
use mysql;

GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '123456' WITH GRANT OPTION;

flush privileges; 
  1. 重启mysql
service mysql restart
  1. 安装Nginx:
sudo apt-get install nginx
  1. 安装pip3
apt install python3-pip
  1. 安装uWSGI以及uWSGI对于Python的支持:
pip3 install uwsgi
  1. 修改总的nginx的配置的文件
vim  /etc/nginx/nginx.conf
  1. 配置nginx的文件
server {
    listen       80;
    server_name 47.92.73.20 localhost;
    access_log /home/app/logs/acc:ess.log;
    error_log /home/app/logs/error.log;
    location / {
    include uwsgi_params;
    uwsgi_pass 127.0.0.1:8890;
    uwsgi_param UWSGI_CHDIR /home/app/src/s_aj;
    uwsgi_param UWSGI_SCRIPT manage:app;   # 启动flask的文件:Flask的实例
    }
}
  1. 配置uwsgi的文件
[uwsgi]

socket=127.0.0.1:8890

pythonpath=/home/app/src/s_aj;  #项目所在目录

callable=app;  # 回调的flask实例

logto = /home/app/logs/uwsgi.log  # 存uwsgi日志的文件地址

6.查看文档,并随时刷新

tail -f uwsgi.log

ajax补充:

相关标签: flask

推荐阅读