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: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
-
效果图
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:
- 更新ubuntu的源
sudo apt-get update
- 安装mysql
sudo apt install mysql-server mysql-client
- 修改mysql配置
cd /etc/mysql/mysql.conf.d
修改mysqld.conf 讲bind_address注释
- 修改配置
use mysql;
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '123456' WITH GRANT OPTION;
flush privileges;
- 重启mysql
service mysql restart
- 安装Nginx:
sudo apt-get install nginx
- 安装pip3
apt install python3-pip
- 安装uWSGI以及uWSGI对于Python的支持:
pip3 install uwsgi
- 修改总的nginx的配置的文件
vim /etc/nginx/nginx.conf
- 配置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的实例
}
}
- 配置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补充:
上一篇: NumberFormatException异常为什么不需要捕获?
下一篇: 解决:ClassNotFoundException:org.apache.commons.logging.LogFactory
推荐阅读