10.Flask

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:cfx123@localhost: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 '创建班级数据成功'

数据筛选

abc
__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
  • 效果图

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框架,但是所有功能都需要自己安装相应插件,如果程序员没有布局好会导致结构凌乱,代码重复利用率低:为此我们必须掌握相关优化

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

首先我们创建一个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补充:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值