Flask框架(数据库联表操作, 批量生成数据,数据迁移,session保存的位置,蓝图)
关联查询
常用的SQLAlchemy关系选项
一对一
# 主表 uselist表示是否多条,false为单个,backref为从附加表读取主表数据作过渡字段
info = db.relationship("StudentInfo",uselist=False,backref="own")
# 附加表
uid = db.Column(db.Integer, db.ForeignKey(Student.id),comment="外键")
一对多
# 关联属性,一的一方添加模型关联属性, lazy一般选用dynamic懒查询即可
course = db.relationship("Course", uselist=True, backref="teacher",lazy='dynamic')
# 外键,多的一方模型中添加外键
teacher_id = db.Column(db.Integer, db.ForeignKey(Teacher.id))
模型之间的关联
一对一
class Student(db.Model):
"""个人信息主表"""
# 关联属性,这个不会被视作表字段,只是模型的属性。
# 因为StudentInfo和Student是一对一的关系,所以uselist=False表示关联一个数据
# 查询的时候,直接用学生对象.info.StudentInfo的字段名
info = db.relationship("StudentInfo",uselist=False,backref="own")
class StudentInfo(db.Model):
"""个人信息附加表"""
# 外键,
# 如果是一对一,则外键放在附加表对应的模型中
# 如果是一对多,则外键放在多的表对象的模型中
uid = db.Column(db.Integer, db.ForeignKey(Student.id),comment="外键")
测试代码:
run.py,代码:
from flask import Flask,request,jsonify,render_template
from config import Config
from models2 import db,Student,Course,Teacher,StudentInfo
# 初始化
app = Flask(import_name=__name__,template_folder='templates')
app.config.from_object(Config)
db.init_app(app) # 初始化数据库链接
@app.route(rule='/')
def index():
"""1对1模型操作"""
# 获取数据[从主表读取数据,获取附加表数据]
# student = Student.query.get(3)
# print( student.info.address )
# print( student.info.edu )
# 获取数据[从附加表读取数据,获取主表数据]
# student_info = StudentInfo.query.filter(StudentInfo.address=="北京市昌平区沙河地铁站对面").first()
# print(student_info.own.name)
# 添加数据[添加数据,把关联模型的数据也一并添加]
# student = Student(name="liu", sex=True, age=22, email="[email protected]", money=100)
# student.info = StudentInfo(address="深圳市宝安区创业2路103号", edu="本科")
# db.session.add(student)
# db.session.commit()
# 修改数据[通过主表可以修改附加表的数据,也可以通过附加表模型直接修改主表的数据]
# student = Student.query.get(4)
# student.info.address = "广州市天河区天河东路103号"
# db.session.commit()
return "ok"
if __name__ == '__main__':
# 运行flask
app.run(debug=True)
models.py,代码:
# 初始化SQLAlchemy
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy() # 初始化数据库操作对象
class Student(db.Model):
__tablename__ = "tb_student"
id = db.Column(db.Integer, primary_key=True, comment="主键")
name = db.Column(db.String(64), index=True, comment="姓名")
sex = db.Column(db.Boolean, default=True, comment="性别")
age = db.Column(db.SmallInteger, nullable=True, comment="年龄")
email = db.Column(db.String(128), unique=True, comment="邮箱地址")
money = db.Column(db.Numeric(8,2), default=0, comment="钱包")
# 关联属性,这个不会被视作表字段,只是模型的属性。
# 因为StudentInfo和Student是一对一的关系,所以uselist=False表示关联一个数据
info = db.relationship("StudentInfo",uselist=False,backref="own")
# 自定义方法
def __repr__(self):
return 'Student:%s' % self.name
class StudentInfo(db.Model):
__tablename__ = "tb_student_info"
id = db.Column(db.Integer, primary_key=True, comment="主键")
address = db.Column(db.String(299), comment="住址")
edu = db.Column(db.Enum("高中以下","大专高技","本科","硕士","博士以上"))
# uid = db.Column(db.Integer, db.ForeignKey("tb_student.id"),comment="外键")
# 外键,
# 如果是一对一,则外键放在附加表对应的模型中
# 如果是一对多,则外键放在多的表对象的模型中
uid = db.Column(db.Integer, db.ForeignKey(Student.id),comment="外键")
class Teacher(db.Model):
# 表结构声明
__tablename__ = 'tb_teacher'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
option = db.Column(db.Enum("讲师","助教","班主任"), default="讲师")
def __repr__(self):
return 'Teacher:%s' % self.name
class Course(db.Model):
# 定义表名
__tablename__ = 'tb_course'
# 定义字段对象
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
price = db.Column(db.Numeric(6,2))
# repr()方法类似于django的__str__,用于打印模型对象时显示的字符串信息
def __repr__(self):
return 'Course:%s'% self.name
config.py,代码:
# 声明和加载配置
class Config():
DEBUG = True
# 数据库链接配置 = 数据库名称://登录账号:登录密码@数据库主机IP:数据库访问端口/数据库名称?charset=编码类型
SQLALCHEMY_DATABASE_URI = "mysql://root:[email protected]:3306/students?charset=utf8"
# 动态追踪修改设置,如未设置只会提示警告
SQLALCHEMY_TRACK_MODIFICATIONS = False
# 显示原始SQL语句
SQLALCHEMY_ECHO = True
# 调整json数据转换中文的配置
JSON_AS_ASCII=False
测试数据:
INSERT INTO students.tb_student (id, name, sex, age, email, money) VALUES (3, 'li', 1, 17, '[email protected]', 300.00);
INSERT INTO students.tb_student (id, name, sex, age, email, money) VALUES (4, 'wang', 0, 15, '[email protected]', 300.00);
INSERT INTO students.tb_student (id, name, sex, age, email, money) VALUES (5, 'zhao', 0, 17, '[email protected]', 300.00);
INSERT INTO students.tb_student (id, name, sex, age, email, money) VALUES (6, 'long', 0, 18, '[email protected]', 300.00);
INSERT INTO students.tb_student (id, name, sex, age, email, money) VALUES (7, 'zhang', 1, 21, '[email protected]', 300.00);
INSERT INTO students.tb_student (id, name, sex, age, email, money) VALUES (8, 'liu', 1, 22, '[email protected]', 100.00);
INSERT INTO students.tb_student_info (id, address, edu, uid) VALUES (1, '天津市静海区静海路2号', '本科', 3);
INSERT INTO students.tb_student_info (id, address, edu, uid) VALUES (2, '广州市天河区天河东路103号', '高中以下', 4);
INSERT INTO students.tb_student_info (id, address, edu, uid) VALUES (3, '天津市静海区静海路2号', '本科', 5);
INSERT INTO students.tb_student_info (id, address, edu, uid) VALUES (4, '北京市昌平区沙河地铁站对面', '本科', 6);
INSERT INTO students.tb_student_info (id, address, edu, uid) VALUES (5, '天津市静海区静海路2号', '本科', 7);
INSERT INTO students.tb_student_info (id, address, edu, uid) VALUES (6, '深圳市宝安区创业2路103号', '本科', 8);
一对多
class Teacher(db.Model):
# 关联属性,一的一方添加模型关联属性
course = db.relationship("Course", uselist=True, backref="teacher",lazy='dynamic')
class Course(db.Model):
# 外键,多的一方模型中添加外间
teacher_id = db.Column(db.Integer, db.ForeignKey(Teacher.id))
- 其中realtionship描述了Course和Teacher的关系。第一个参数为对应参照的类"Course"
- 第二个参数backref为类Teacher申明新属性的方法
- 第三个参数lazy决定了什么时候SQLALchemy从数据库中加载数据
- lazy='subquery',查询当前数据模型时,采用子查询(subquery),把外键模型的属性也瞬间查询出来了。
- lazy=True或lazy='select',查询当前数据模型时,不会把外键模型的数据查询出来,只有操作到外键关联属性时,才进行连表查询数据[执行SQL]
- lazy='dynamic',查询当前数据模型时,不会把外键模型的数据查询出来,只有操作到外键关联属性并操作外键模型具体属性时,才进行连表查询数据[执行SQL]
测试代码:
models2.py,代码:
# 初始化SQLAlchemy
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy() # 初始化数据库操作对象
class Student(db.Model):
__tablename__ = "tb_student"
id = db.Column(db.Integer, primary_key=True, comment="主键")
name = db.Column(db.String(64), index=True, comment="姓名")
sex = db.Column(db.Boolean, default=True, comment="性别")
age = db.Column(db.SmallInteger, nullable=True, comment="年龄")
email = db.Column(db.String(128), unique=True, comment="邮箱地址")
money = db.Column(db.Numeric(8,2), default=0, comment="钱包")
# 关联属性,这个不会被视作表字段,只是模型的属性。
# 因为StudentInfo和Student是一对一的关系,所以uselist=False表示关联一个数据
info = db.relationship("StudentInfo",uselist=False,backref="own")
# 自定义方法
def __repr__(self):
return 'Student:%s' % self.name
class StudentInfo(db.Model):
__tablename__ = "tb_student_info"
id = db.Column(db.Integer, primary_key=True, comment="主键")
address = db.Column(db.String(299), comment="住址")
edu = db.Column(db.Enum("高中以下","大专高技","本科","硕士","博士以上"))
# uid = db.Column(db.Integer, db.ForeignKey("tb_student.id"),comment="外键")
# 外键,
# 如果是一对一,则外键放在附加表对应的模型中
# 如果是一对多,则外键放在多的表对象的模型中
uid = db.Column(db.Integer, db.ForeignKey(Student.id),comment="外键")
class Teacher(db.Model):
# 表结构声明
__tablename__ = 'tb_teacher'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
option = db.Column(db.Enum("讲师","助教","班主任"), default="讲师")
# 关联属性,一的一方添加模型关联属性
course = db.relationship("Course", uselist=True, backref="teacher",lazy='dynamic')
def __repr__(self):
return 'Teacher:%s' % self.name
class Course(db.Model):
# 定义表名
__tablename__ = 'tb_course'
# 定义字段对象
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
price = db.Column(db.Numeric(6,2))
# 外键,多的一方模型中添加外间
teacher_id = db.Column(db.Integer, db.ForeignKey(Teacher.id))
# repr()方法类似于django的__str__,用于打印模型对象时显示的字符串信息
def __repr__(self):
return 'Course:%s'% self.name
run.py代码:
from flask import Flask,request,jsonify,render_template
from config import Config
from models2 import db,Student,Course,Teacher,StudentInfo
# 初始化
app = Flask(import_name=__name__,template_folder='templates')
app.config.from_object(Config)
db.init_app(app) # 初始化数据库链接
@app.route(rule='/more')
def more():
"""一对多/多对一模型操作"""
# 从1的一方的模型中获取多的一方模型的数据
# teacher = Teacher.query.get(1)
# print(teacher)
# # ret = teacher.course
# for course in teacher.course:
# print(course.name,course.price)
# 从多的一方获取1的一方数据
# course = Course.query.get(1)
# print(course.teacher)
# print(course.teacher.name)
# 添加数据
# 从1的一方添加数据,同时给多的一方也添加
# 一对多添加数据必须先存在主键,才可以添加数据,比如要先添加老师,然后添加课程
# 的时候才可以添加teacher_id, 添加多的一方的时候,格式必须是列表类型
# teacher = Teacher(name="蓝老师",option="讲师")
# teacher.course = [Course(name="插画入门",price=199.00),Course(name="素描入门",price=129.00),]
# db.session.add(teacher)
# db.session.commit()
return "ok"
if __name__ == '__main__':
# 运行flask
app.run(debug=True)
测试数据:
INSERT INTO students.tb_teacher (id, name, `option`) VALUES (1, '王老师', '讲师');
INSERT INTO students.tb_teacher (id, name, `option`) VALUES (2, '许老师', '讲师');
INSERT INTO students.tb_teacher (id, name, `option`) VALUES (3, '徐老师', '讲师');
INSERT INTO students.tb_teacher (id, name, `option`) VALUES (4, '张老师', '讲师');
INSERT INTO students.tb_teacher (id, name, `option`) VALUES (5, '留老师', '讲师');
INSERT INTO students.tb_course (id, name, price, teacher_id) VALUES (1, 'JAVA入门', 299.00, 1);
INSERT INTO students.tb_course (id, name, price, teacher_id) VALUES (2, 'Python入门', 399.00, 1);
INSERT INTO students.tb_course (id, name, price, teacher_id) VALUES (3, 'linux入门', 199.00, 2);
INSERT INTO students.tb_course (id, name, price, teacher_id) VALUES (4, 'django入门', 399.00, 3);
INSERT INTO students.tb_course (id, name, price, teacher_id) VALUES (5, 'flask入门', 299.00, 3);
INSERT INTO students.tb_course (id, name, price, teacher_id) VALUES (6, '数据分析入门', 199.00, 3);
INSERT INTO students.tb_course (id, name, price, teacher_id) VALUES (7, '爬虫入门', 299.00, 4);
INSERT INTO students.tb_course (id, name, price, teacher_id) VALUES (8, '前端入门', 199.00, 5);
INSERT INTO students.tb_course (id, name, price, teacher_id) VALUES (9, 'python项目', 199.00, 5);
多对多
添加数据的时候,append是添加一条,extend是多条,其实就是操作列表
通过db.Table创建关联模型
# 多对多。必须有关系表进行记录
student_course = db.Table("tb_student_course",
db.Column("id", db.Integer, primary_key=True, comment="主键ID"),
db.Column("sid", db.Integer, db.ForeignKey('tb_student.id'), comment="学生ID"),
db.Column("cid", db.Integer, db.ForeignKey('tb_course.id'), comment="课程ID"),
class Student(db.Model):
course_list = db.relationship("Course",secondary=student_course,backref="student_list", lazy='dynamic')
class Course(db.Model):
# 上下2个模型只需要声明一个关系属性即可,可以通过backref反向调用。
# student_list = db.relationship("Student",secondary=student_course,backref="course_list", lazy='dynamic')
测试代码
model.py代码:
# 初始化SQLAlchemy
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy() # 初始化数据库操作对象
# 针对多对多。
# 我们可以以db.Table创建关系表的方式进行关联
# 也可以创建一个中间表模型,进行关联[就是把原来2个模型之间的多对多转换成3个模型之间的一对多]
student_course = db.Table("tb_student_course",
db.Column("id", db.Integer, primary_key=True, comment="主键ID"),
db.Column("sid", db.Integer, db.ForeignKey('tb_student.id'), comment="学生ID"),
db.Column("cid", db.Integer, db.ForeignKey('tb_course.id'), comment="课程ID"),
class Student(db.Model):
__tablename__ = "tb_student"
id = db.Column(db.Integer, primary_key=True, comment="主键")
name = db.Column(db.String(64), index=True, comment="姓名")
sex = db.Column(db.Boolean, default=True, comment="性别")
age = db.Column(db.SmallInteger, nullable=True, comment="年龄")
email = db.Column(db.String(128), unique=True, comment="邮箱地址")
money = db.Column(db.Numeric(8,2), default=0, comment="钱包")
# 关联属性,这个不会被视作表字段,只是模型的属性。
# 因为StudentInfo和Student是一对一的关系,所以uselist=False表示关联一个数据
info = db.relationship("StudentInfo",uselist=False,backref="own")
course_list = db.relationship("Course",secondary=student_course,backref="student_list", lazy='dynamic')
# 自定义方法
def __repr__(self):
return 'Student:%s' % self.name
class StudentInfo(db.Model):
__tablename__ = "tb_student_info"
id = db.Column(db.Integer, primary_key=True, comment="主键")
address = db.Column(db.String(299), comment="住址")
edu = db.Column(db.Enum("高中以下","大专高技","本科","硕士","博士以上"))
# uid = db.Column(db.Integer, db.ForeignKey("tb_student.id"),comment="外键")
# 外键,
# 如果是一对一,则外键放在附加表对应的模型中
# 如果是一对多,则外键放在多的表对象的模型中
uid = db.Column(db.Integer, db.ForeignKey(Student.id),comment="外键")
class Teacher(db.Model):
# 表结构声明
__tablename__ = 'tb_teacher'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
option = db.Column(db.Enum("讲师","助教","班主任"), default="讲师")
# 关联属性,一的一方添加模型关联属性
course = db.relationship("Course", uselist=True, backref="teacher",lazy='dynamic')
def __repr__(self):
return 'Teacher:%s' % self.name
class Course(db.Model):
# 定义表名
__tablename__ = 'tb_course'
# 定义字段对象
id
= db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
price = db.Column(db.Numeric(6,2))
# 外键,多的一方模型中添加外间
teacher_id = db.Column(db.Integer, db.ForeignKey(Teacher.id))
# repr()方法类似于django的__str__,用于打印模型对象时显示的字符串信息
def __repr__(self):
return 'Course:%s'% self.name
视图代码:
from flask import Flask,request,jsonify,render_template
from config import Config
from models import db,Student,Course,Teacher,StudentInfo
# 初始化
app = Flask(import_name=__name__,template_folder='templates')
app.config.from_object(Config)
db.init_app(app) # 初始化数据库链接
@app.route(rule='/')
def index():
"""多对多"""
# 查询学生数据,关联查询课程
# student = Student.query.get(3)
# course_list = student.course_list.filter(Course.price<=300).all()
# print( course_list )
# 查询课程数据,关联查询学生
# course = Course.query.get(3)
# student_list = course.student_list
# print(student_list)
# 给学生报读课程[已经存在的课程]
# student = Student.query.get(6)
# course1 = Course.query.get(4)
# course2 = Course.query.get(5)
# student.course_list.append(course1)
# # student.course_list.extend([course1,course2])
# db.session.commit()
# 给学生报读新课程[没有存在的课程,添加关系之前会自动创建模型]
# student = Student.query.get(7)
# student.course_list.extend([
# Course(name="python高级1",price=199.00,teacher_id=3),
# Course(name="python高级2", price=199.00, teacher_id=3),
# Course(name="python高级3", price=199.00, teacher_id=3),
# db.session.commit()
return "ok"
if __name__ == '__main__':
# 运行flask
app.run(debug=True)
测试数据,代码:
INSERT INTO students.tb_student_course (id, sid, cid) VALUES (1, 3, 1);
INSERT INTO students.tb_student_course (id, sid, cid) VALUES (2, 3, 3);
INSERT INTO students.tb_student_course (id, sid, cid) VALUES (3, 3, 4);
INSERT INTO students.tb_student_course (id, sid, cid) VALUES (4, 4, 2);
INSERT INTO students.tb_student_course (id, sid, cid) VALUES (5, 4, 3);
INSERT INTO students.tb_student_course (id, sid, cid) VALUES (6, 5, 5);
INSERT INTO students.tb_student_course (id, sid, cid) VALUES (7, 5, 3);
INSERT INTO students.tb_student_course (id, sid, cid) VALUES (8, 6, 3);
INSERT INTO students.tb_student_course (id, sid, cid) VALUES (9, 6, 4);
多对多第二种方式:通过中间表模型创建关联模型
把学生和课程之间的多对多拆分成,学生与学生课程记录的一对多和课程与学生课程记录的一对多。
class Student(db.Model):
# 学生和学生课程
to_course = db.relationship("StudentCourse", uselist=True, backref="to_student", lazy='dynamic')
class Course(db.Model):
to_student = db.relationship("StudentCourse", uselist=True, backref="to_course", lazy='dynamic')
# 针对多对多。
# 创建一个中间表模型,进行关联[就是把原来2个模型之间的多对多转换成3个模型之间的一对多]
class StudentCourse(db.Model):
__tablename__ = 'tb_student_course'
id = db.Column(db.Integer, primary_key=True)
sid = db.Column(db.Integer, db.ForeignKey(Student.id))
cid = db.Column(db.Integer, db.ForeignKey(Course.id))
created_time = db.Column(db.DateTime, nullable=True, )
achievement = db.Column(db.Numeric(5,2), nullable=True )
def __repr__(self):
return 'Student:%s, Course:%s'% (self.to_student.name,self.to_course.name)
测试代码:
models2.py,代码:
# 初始化SQLAlchemy
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy() # 初始化数据库操作对象
class Student(db.Model):
__tablename__ = "tb_student"
id = db.Column(db.Integer, primary_key=True, comment="主键")
name = db.Column(db.String(64), index=True, comment="姓名")
sex = db.Column(db.Boolean, default=True, comment="性别")
age = db.Column(db.SmallInteger, nullable=True, comment
="年龄")
email = db.Column(db.String(128), unique=True, comment="邮箱地址")
money = db.Column(db.Numeric(8,2), default=0, comment="钱包")
# 关联属性,这个不会被视作表字段,只是模型的属性。
# 因为StudentInfo和Student是一对一的关系,所以uselist=False表示关联一个数据
info = db.relationship("StudentInfo",uselist=False,backref="own")
# 学生和学生课程
to_course = db.relationship("StudentCourse", uselist=True, backref="to_student", lazy='dynamic')
# 自定义方法
def __repr__(self):
return 'Student:%s' % self.name
class StudentInfo(db.Model):
__tablename__ = "tb_student_info"
id = db.Column(db.Integer, primary_key=True, comment="主键")
address = db.Column(db.String(299), comment="住址")
edu = db.Column(db.Enum("高中以下","大专高技","本科","硕士","博士以上"))
# uid = db.Column(db.Integer, db.ForeignKey("tb_student.id"),comment="外键")
# 外键,
# 如果是一对一,则外键放在附加表对应的模型中
# 如果是一对多,则外键放在多的表对象的模型中
uid = db.Column(db.Integer, db.ForeignKey(Student.id),comment="外键")
class Teacher(db.Model):
# 表结构声明
__tablename__ = 'tb_teacher'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
option = db.Column(db.Enum("讲师","助教","班主任"), default="讲师")
# 关联属性,一的一方添加模型关联属性
course = db.relationship("Course", uselist=True, backref="teacher",lazy='dynamic')
def __repr__(self):
return 'Teacher:%s' % self.name
class Course(db.Model):
# 定义表名
__tablename__ = 'tb_course'
# 定义字段对象
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
price = db.Column(db.Numeric(6,2))
# 外键,多的一方模型中添加外间
teacher_id = db.Column(db.Integer, db.ForeignKey(Teacher.id))
to_student = db.relationship("StudentCourse", uselist=True, backref="to_course", lazy='dynamic')
# repr()方法类似于django的__str__,用于打印模型对象时显示的字符串信息
def __repr__(self):
return 'Course:%s'% self.name
# 针对多对多。
# 创建一个中间表模型,进行关联[就是把原来2个模型之间的多对多转换成3个模型之间的一对多]
class StudentCourse(db.Model):
__tablename__ = 'tb_student_course'
id = db.Column(db.Integer, primary_key=True)
sid = db.Column(db.Integer, db.ForeignKey(Student.id))
cid = db.Column(db.Integer, db.ForeignKey(Course.id))1
created_time = db.Column(db.DateTime, nullable=True, )
achievement = db.Column(db.Numeric(5,2), nullable=True )
def __repr__(self):
return 'Student:%s, Course:%s'% (self.to_student.name,self.to_course.name)
run.py,代码:
from flask import Flask,request,jsonify,render_template
from config import Config
from models2 import db,Student,Course,Teacher,StudentInfo
# 初始化
app = Flask(import_name=__name__,template_folder='templates')
app.config.from_object(Config)
db.init_app(app) # 初始化数据库链接
@app.route(rule='/')
def index():
"""多对多[中间表作为模型创建使用]"""
# 查询学生数据,关联查询课程
# 查询学生3的课程
stu = Student.query.get(3)
stuend_course_list = stu.to_course.all() # 这里的to_course是relationship
print(stuend_course_list) # 到了第三张表[<StudentCourse 1>, <StudentCourse 2>, <StudentCourse 3>]
for cou in stuend_course_list:
print(cou.to_course.name) # 这里的to_course是backref
# 添加数据
demo1 = StudentCourse(sid=6, cid=5)
demo2 = StudentCourse(sid=6, cid=6)
db.session.add_all([demo1, demo2])
db.session.commit()
return "ok"
if __name__ == '__main__'
:
# 运行flask
app.run(debug=True)
数据迁移
- 在开发过程中,需要修改数据库模型,而且还要在修改之后更新数据库。最直接的方式就是删除旧表,但这样会丢失数据。
- 更好的解决办法是使用数据库迁移框架,它可以追踪数据库模式的变化,然后把变动应用到数据库中。
- 在Flask中可以使用Flask-Migrate扩展,来实现数据迁移。并且集成到Flask-Script中,所有操作通过命令就能完成。
- 为了导出数据库迁移命令,Flask-Migrate提供了一个MigrateCommand类,可以附加到flask-script的manager对象上。
首先要在虚拟环境中安装Flask-Migrate。
pip install flask-migrate -i https://pypi.doubanio.com/simple
模型采用之前的models2.py,把数据库所有的表删除,run.py内容:
from flask import Flask,request,jsonify,render_template
from config import Config
from models2 import db,Student,Course,Teacher,StudentInfo
# 初始化
app = Flask(import_name=__name__,template_folder='templates')
app.config.from_object(Config)
db.init_app(app) # 初始化数据库链接
from flask_script import Manager
manager = Manager(app)
from flask_migrate import Migrate,MigrateCommand
#第一个参数是Flask的实例,第二个参数是Sqlalchemy数据库实例
migrate = Migrate(app,db)
#manager是Flask-Script的实例,这条语句在flask-Script中添加一个db命令
manager.add_command('db',MigrateCommand)
@app.route(rule='/')
def index():
return "ok"
if __name__ == '__main__':
# 运行flask
manager.run()
创建迁移版本仓库
#这个命令会创建migrations文件夹,所有迁移文件都放在里面。
python main.py db init
创建迁移版本
- 自动创建迁移版本有两个函数
- upgrade():函数把迁移中的改动应用到数据库中。
- downgrade():函数则将改动删除。一般表示把upgrade的操作进行恢复/删除/还原。
- 自动创建的迁移脚本会根据模型定义和数据库当前状态的差异,生成upgrade()和downgrade()函数的内容。
- 对比不一定完全正确,有可能会遗漏一些细节,需要进行检查
python main.py db migrate -m 'initial migration'
# 这里等同于django里面的 makemigrations,生成迁移版本文件
升级版本库的版本
python main.py db upgrade
降级版本库的版本
python main.py db downgrade
版本库的历史管理
可以根据history命令找到版本号,然后传给downgrade命令:
python manage.py db history
输出格式:<base> -> 版本号 (head), initial migration
回滚到指定版本
python manage.py db downgrade # 默认返回上一个版本
python manage.py db downgrade 版本号 # 返回到指定版本号对应的版本
数据迁移的步骤:
1. 初始化数据迁移的目录【项目最开始的时候需要执行,后面不需要了。】
python manage.py db init
2. 数据库的数据迁移版本的创建和添加版本的描述
python manage.py db migrate -m 'initial migration'
3. 升级版本,把migrate命令记录到模型相关修改操作同步到数据库中。
python manage.py db upgrade
4. 降级版本,把upgrade执行的操作进行还原/恢复/删除
python manage.py db downgrade
模块推荐: Faker生成仿真测试数据
文档: https:// faker.readthedocs.io/en /master/locales/zh_CN.html
批量生成测试数据: https:// github.com/joke2k/faker
pip install faker -i https://pypi.doubanio.com/simple
comands.py代码:
from flask_script import Command
from models2 import Student,db
from faker import Faker
import random
class FakerCommand(Command):
"""数据种子生成器"""
def run(self):
data = []
faker = Faker(locale="zh_CN")
for _ in range(300):
sex = bool(random.randint(0,1))
data.append(Student(
name=faker.last_name()+(faker.first_name_male() if sex else faker.first_name_female()),
sex=sex,
age=random.randint(12,30),
email=faker.company_email(),
money=random.randint(1,20) * 100 + 100
db.session.add_all(data)
db.session.commit()
run.py,代码:
from flask import Flask,request,jsonify,render_template
from config import Config
from models2 import db,Student,Course,Teacher,StudentInfo
# 初始化
app = Flask(import_name=__name__,template_folder='templates')
app.config.from_object(Config)
db.init_app(app) # 初始化数据库链接
from flask_script import Manager
manager = Manager(app)
from flask_migrate import Migrate,MigrateCommand
#第一个参数是Flask的实例,第二个参数是Sqlalchemy数据库实例
migrate = Migrate(app,db)
#manager是Flask-Script的实例,这条语句在flask-Script中添加一个db命令
manager.add_command('db',MigrateCommand)
from comands import FakerCommand
manager.add_command("faker",FakerCommand)
@app.route(rule='/')
def index():
return "ok"
if __name__ == '__main__':
# 运行flask
manager.run()
终端下输入
python run.py faker
flask-session
允许设置session到指定存储的空间中, 文档:
安装命令: https:// pythonhosted.org/Flask- Session/
pip install flask-Session -i https://pypi.douban.com/simple
使用session之前,必须配置一下配置项:
SECRET_KEY = "*(%#4sxcz(^(#$#8423" # session秘钥
redis保存session的基本配置
先安装配置flask-redis模块到项目中
pip install flask-redis -i https://pypi.douban.com/simple
run.py注册redis模块
config.py,代码:
# 声明和加载配置
class Config():
DEBUG = True
# 数据库链接配置 = 数据库名称://登录账号:登录密码@数据库主机IP:数据库访问端口/数据库名称?charset=编码类型
SQLALCHEMY_DATABASE_URI = "mysql://root:[email protected]:3306/students?charset=utf8"
# 动态追踪修改设置,如未设置只会提示警告
SQLALCHEMY_TRACK_MODIFICATIONS
= False
# 显示原始SQL语句
SQLALCHEMY_ECHO = False
# 调整json数据转换中文的配置
JSON_AS_ASCII=False
# session秘钥
SECRET_KEY = "L>PG#5394&()%#34"
# redis的链接配置
# REDIS_URL = "redis://:密码@IP地址:端口/数据库下标"
REDIS_URL = "redis://@127.0.0.1:6379/0"
run.py,代码:
from flask import Flask,request,jsonify,render_template
from config import Config
from models2 import db,Student,Course,Teacher,StudentInfo
# 初始化
app = Flask(import_name=__name__,template_folder='templates')
app.config.from_object(Config)
# ...
# redis初始化
redis.init_app(app)
@app.route(rule='/')
def index():
return "ok"
if __name__ == '__main__':
# 运行flask
manager.run()
把session配置到redis中进行保存
config.py 配置文件信息:
from flask_redis import FlaskRedis
redis = FlaskRedis()
# 声明和加载配置
class Config():
DEBUG = True
# 数据库链接配置 = 数据库名称://登录账号:登录密码@数据库主机IP:数据库访问端口/数据库名称?charset=编码类型
SQLALCHEMY_DATABASE_URI = "mysql://root:[email protected]:3306/students?charset=utf8"
# 动态追踪修改设置,如未设置只会提示警告
SQLALCHEMY_TRACK_MODIFICATIONS = False
# 显示原始SQL语句
SQLALCHEMY_ECHO = False
# 调整json数据转换中文的配置
JSON_AS_ASCII=False
# session秘钥
SECRET_KEY = "L>PG#5394&()%#34"
# redis的链接配置
# REDIS_URL = "redis://:密码@IP地址:端口/数据库下标"
REDIS_URL = "redis://@127.0.0.1:6379/0"
# session存储方式为redis
SESSION_TYPE = "redis"
# 如果设置session的生命周期是否是会话期, 为True,则关闭浏览器session就失效
SESSION_PERMANENT = False
# 是否对发送到浏览器上session的cookie值进行加密
SESSION_USE_SIGNER = False
# 保存到redis的session数的名称前缀
SESSION_KEY_PREFIX = "session:"
# session保存数据到redis时启用的链接对象
SESSION_REDIS = redis # 用于连接redis的配置
run.py,代码:
from flask import Flask,session
from config import Config,redis
from models2 import db,Student,Course,Teacher,StudentInfo
# 初始化
app = Flask(import_name=__name__,template_folder='templates')
app.config.from_object(Config)
db.init_app(app) # 初始化数据库链接
from flask_script import Manager
manager = Manager(app)
from flask_migrate import Migrate,MigrateCommand
#第一个参数是Flask的实例,第二个参数是Sqlalchemy数据库实例
migrate = Migrate(app,db)
#manager是Flask-Script的实例,这条语句在flask-Script中添加一个db命令
manager.add_command('db',MigrateCommand)
from comands import FakerCommand
manager.add_command("faker",FakerCommand)
from flask_session import Session
Session(app)
redis.init_app(app)
@app.route(rule='/')
def index():
session["uname"] = "hehehe"
return "ok"
@app.route(rule="/get_session")
def get_session():
print(session.get("uname"))
return "ok"
if __name__ == '__main__':
# 运行flask
manager.run()
SQLAlchemy存储session的基本配置
需要手动创建session表,在项目第一次启动的时候,使用
db.create_all()
来完成创建。
config.py,代码:
from flask_redis import FlaskRedis
redis = FlaskRedis()
from models2 import db
# 声明和加载配置
class Config():
DEBUG = True
# 数据库链接配置 = 数据库名称://登录账号:登录密码@数据库主机IP:数据库访问端口/数据库名称?charset=编码类型
SQLALCHEMY_DATABASE_URI = "mysql://root:[email protected]:3306/students?charset=utf8"
# 动态追踪修改设置,如未设置只会提示警告
SQLALCHEMY_TRACK_MODIFICATIONS = False
# 显示原始SQL语句
SQLALCHEMY_ECHO = False
# 调整json数据转换中文的配置
JSON_AS_ASCII=False
# session秘钥
SECRET_KEY = "L>PG#5394&()%#34"
# redis的链接配置
# REDIS_URL = "redis://:密码@IP地址:端口/数据库下标"
REDIS_URL = "redis://@127.0.0.1:6379/0"
"""session存储方式为redis"""
# SESSION_TYPE = "redis"
# # 如果设置session的生命周期是否是会话期, 为True,则关闭浏览器session就失效
# SESSION_PERMANENT = False
# # 是否对发送到浏览器上session的cookie值进行加密
# SESSION_USE_SIGNER = False
# # 保存到redis的session数的名称前缀
# SESSION_KEY_PREFIX = "session:"
# # session保存数据到redis时启用的链接对象
# SESSION_REDIS = redis # 用于连接redis的配置
"""session存储方式为SQLAlchemy"""
# session类型为sqlalchemy
SESSION_TYPE = "sqlalchemy"
# SQLAlchemy数据库链接对象
SESSION_SQLALCHEMY = db
# session要保存的表名称
SESSION_SQLALCHEMY_TABLE = "tb_session"
# 如果设置为True,则关闭浏览器session就失效
SESSION_PERMANENT = "True"
# 对发送到浏览器上的session_id进行加密
SESSION_USE_SIGNER=True
# 保存到session中的值的前缀
SESSION_KEY_PREFIX = "session:"
run.py,代码:
from flask import Flask,session
from config import Config,redis
from models2 import db,Student,Course,Teacher,StudentInfo
# 初始化
app = Flask(import_name=__name__,template_folder='templates')
app.config.from_object(Config)
db.init_app(app) # 初始化数据库链接
from flask_script import Manager
manager = Manager(app)
from flask_migrate import Migrate,MigrateCommand
#第一个参数是Flask的实例,第二个参数是Sqlalchemy数据库实例
migrate = Migrate(app,db)
#manager是Flask-Script的实例,这条语句在flask-Script中添加一个db命令
manager.add_command('db',MigrateCommand)
from comands import FakerCommand
manager.add_command("faker",FakerCommand)
from flask_session import Session
Session(app)
redis.init_app(app)
@app.route(rule='/')
def index():
session["uname"] = "hehehe"
return "ok"
@app.route(rule="/get_session")
def get_session():
print(session.get("uname"))
return "ok"
if __name__ == '__main__':
# 运行flask
manager.run()
蓝图 Blueprint
模块化
随着flask程序越来越复杂,我们需要对程序进行模块化的处理,之前学习过python的模块化管理,于是针对一个简单的flask程序进行模块化处理
简单来说,Blueprint 是一个存储视图方法的容器,这些操作在这个Blueprint 被注册到一个应用之后就可以被调用,Flask 可以通过Blueprint来组织URL以及处理请求。
Flask使用Blueprint让应用实现模块化,在Flask中,Blueprint具有如下属性:
- 一个项目可以具有多个Blueprint
- 可以将一个Blueprint注册到任何一个未使用的URL下比如 “/”、“/sample”或者子域名
- 在一个应用中,一个模块可以注册多次
- Blueprint可以单独具有自己的模板、静态文件或者其它的通用操作方法,它并不是必须要实现应用的视图和函数的
- 在一个应用初始化时,就应该要注册需要使用的Blueprint
但是一个Blueprint并不是一个完整的应用,它不能独立于应用运行,而必须要注册到某一个应用中。
Blueprint对象用起来和一个应用/Flask对象差不多,最大的区别在于一个 蓝图对象没有办法独立运行,必须将它注册到一个应用对象上才能生效
使用蓝图可以分为四个步骤
-
创建一个蓝图的包,例如
users
,并在
__init__.py文件中创建蓝图对象
from flask import Blueprint
home = Blueprint('home',__name__)
- 在这个蓝图目录下, 创建views.py文件,保存当前蓝图使用的视图函数
def index():
return "ok"
-
在
home/__init__.py中引入views.py中所有的视图函数和给视图配置路由
from flask import Blueprint
home_blu = Blueprint('home',__name__)
from . import views
home_blu.add_url_rule("/index",endpoint="index", view_func=views.index) # 子路由
- 在主程序run.py文件中的app对象上注册这个 users 蓝图对象
from flask import Flask
app = Flask(import_name=__name__)
from config import Config
app.config.from_object(Config)
# 注册蓝图
from user import home_blu
app.register_blueprint(home_blu,url_prefix='/home') # url_prefix='' 总路由
if __name__ == '__main__':
app.run()
当这个应用启动后,通过
http://127.0.0.1:5000/home/index
可以访问到蓝图中定义的视图函数
运行机制
- 蓝图是保存了一组将来可以在应用app对象上执行的操作,注册路由就是一种操作
- 当在app对象上调用 route 装饰器注册路由时,这个操作将修改对象的url_map路由表
- 然而,蓝图对象根本没有路由表,当我们在蓝图对象上调用route装饰器注册路由时,它只是在内部的一个延迟操作记录列表defered_functions中添加了一个项
- 当执行app对象的 register_blueprint() 方法时,app应用对象将从蓝图对象的 defered_functions 列表中取出每一项,并以自身作为参数执行该匿名函数,即调用app应用对象的 add_url_rule() 方法,将蓝图的路由信息全部注册到应用对象app的url_map路由表
蓝图的url前缀
- 当我们在应用对象上注册一个蓝图时,可以指定一个url_prefix关键字参数(这个参数默认是/)
- 在应用最终的路由表 url_map中,在蓝图上注册的路由URL自动被加上了这个前缀,这个可以保证在多个蓝图中使用相同的URL规则而不会最终引起冲突,只要在注册蓝图时将不同的蓝图挂接到不同的自路径即可
- url_for在使用时,如果要生成一个蓝图里面的视图对应的路由地址,则需要声明当前蓝图名称+视图名称
url_for('users.home') # /users/home 格式:蓝图名称.视图函数名
注册蓝图中的静态文件的相关路由
和app应用对象不同,蓝图对象创建时不会默认注册静态目录的路由。需要我们在 创建时指定 static_folder 参数。
下面的示例将蓝图所在目录下的static_home目录设置为静态目录
# home/__init__.py,代码:
from flask import Blueprint
home_blu = Blueprint('home',__name__,static_folder="static_home")
from . import views
home_blu.add_url_rule("/index",endpoint="index", view_func=views.index)
# 主程序文件 run.py,代码:
# 注册蓝图
from home import home_blu
app.register_blueprint(home_blu,url_prefix='/home')
现在就可以使用/home/static_home/图片名称,访问static_home目录下的静态文件了,定制静态目录URL规则 :可以在创建蓝图对象时使用 static_url_path 来改变静态目录的路由。
下面的示例将为 static_home 文件夹的路由设置为 /lib
from flask import Blueprint
# static_url_path="/lib" 接下面一行
# 设置静态文件访问的子路由,如果不设置,则默认蓝图的静态文件子路由是static_folder的值
# 静态文件的访问路径规则: http://127.0.0.1:5000/总路由/子路由/文件名
home_blu = Blueprint('home',__name__,static_folder="static_home",static_url_path="/lib")
from . import views
home_blu.add_url_rule("/index",endpoint="index", view_func=views.index)
有了蓝图的静态目录以后,并不会影响原来项目中提供的static总静态文件目录的使用。
设置蓝图中模版的目录
蓝图对象默认的模板目录为系统的模版目录,可以在创建蓝图对象时使用 template_folder 关键字参数设置模板目录
创建蓝图中的模板目录template_home:
from flask import Blueprint
# static_url_path="/lib" 设置静态文件访问的子路由,如果不设置,则默认蓝图的静态文件子路由是static_folder的值
# 静态文件的访问路径规则: http://127.0.0.1:5000/总路由/子路由/文件名
home_blu = Blueprint('home',__name__,