SQLAlchemy是Python编程语言下的一款开源软件。提供了SQL工具包及对象关系映射(ORM)工具。
而Flask-SQLAlchemy是Flask对SQLAlchemy进行了一个封装,目的于简化在Flask中SQLAlchemy的使用,
提供了有用的默认值和额外的助手来更简单地完成日常任务。
orm作用
1.省去自己拼写SQL,保证SQL语法的正确性一次编写可以适配多个数据库.
2.防止注入攻击.
3.在数据库表名或字段名发生变化时,只需修改模型类的映射,无需修改数据库操作的代码.
#安装Flask-SQLAlchemy和MySQL的Python客户端库以及数据库迁移工具
pip install flask-sqlalchemy
pip install mysqlclient
pip install flask_script
pip install flask_migrate ---2.7.0
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_script import Manager
from flask_migrate import Migrate,MigrateCommand#MigrateCommand在flask_migrate3.0以上已经去掉了
import pymysql
pymysql.install_as_MySQLdb()
#配置对象
class DatabaseConfig():
#连接的数据库 URI
#mysql://用户名:密码@host:port/database
SQLALCHEMY_DATABASE_URI =
'mysql://root:[email protected]:3306/sqlalchemyTest'
#是否追踪数据的修改
SQLALCHEMY_TRACK_MODIFICATIONS = False
#是否显示生成sql语句
SQLALCHEMY_ECHO = False
#实例化一个flask对象
app = Flask(__name__)
#实例化一个Manager对象,flask脚本管理对象
manager = Manager(app)
#从配置对象中加载
app.config.from_object(DatabaseConfig)
#创建SQLAlchemy对象
db = SQLAlchemy(app)
Migrate(app,db)
#将db添加进manager中
manager.add_command("db",MigrateCommand)
#或者下面这种方法创建sqlalchemy对象,但是单独运行调试时,对数据库操作需要在Flask的应用上下文中进行
#db = SQLAlchemy()
#db.init_app(app)
#with app.app_context():
# database.query.all()
类型名 | mysql中类型 | 说明 |
---|---|---|
Integer | int | 普通整数 |
SmallInteger | tinyint | 范围小的整数 |
BigInteger | bigint | 不限精度的整数 |
Float | float | 浮点数 |
Numeric | decimal | 普通整数 |
String | varchar | 边长字符串 |
Date | date | 时间 |
Time | datetime | 日期时间 |
Enum | enum | 枚举值 |
优点:简单快捷,不用写sql
缺点:表结构发生变化的时候,容易发生迁移错误
sql_test.py
from main import db
from main import manager
class Student(db.Model):
#设置数据库中真实表名
__tablename__ = "student"
sid = db.Column("sid",db.Integer,primary_key=True,doc="学生id")
sname = db.Column("sname",db.String(4),nullable=False,doc="姓名")
gender = db.Column(db.Enum("男","女"),doc="性别")
phone = db.Column(db.String(11),nullable=False,doc="手机号")
#第一种关联表:relationship关联表,backref添加反向引用,通过Grade对象.stu获得student表信息
1. grades = db.relationship("Grade",backref="stu")
#第二种关联方式:primaryjoin
2. grades =db.relationship("Grade",primaryjoin="Student.sid==foreign(Grade.student)",backref="stu")
#多对多关系中中间表的数据库名字,可通过student对象.courses获取课程表信息,backref同上述一样。
courses = db.relationship("Course",secondary="student_course",backref="stu")
#将对象转化为字符串
def __repr__(self):
sname = self.sname
phone = self.phone
return f"{sname}:{phone}"
class StudentCourse(db.Model):
id = db.Column(db.Integer,primary_key=True,doc="学生课程id")
sid = db.Column(db.Integer,db.ForeignKey("student.sid"),doc="学生id")
cid = db.Column(db.Integer,db.ForeignKey("course.cid"),doc="课程id")
class Course(db.Model):
__tablename__ = "course"
cid = db.Column("cid",db.Integer,primary_key=True,doc="课程id")
cname = db.Column("cname",db.String(4),nullable=False,doc="课程名")
teacher = db.Column("teacher",db.Integer,db.ForeignKey("teacher.tid"),doc="老师id")
grade = db.relationship("Grade",backref="cour")
class Teacher(db.Model):
__tablename__ = "teacher"
tid = db.Column(db.Integer,primary_key=True,doc = "老师id")
tname = db.Column(db.String(4),nullable=False,doc="老师姓名")
gender = db.Column(db.Enum("男", "女"), doc="性别")
phone = db.Column(db.String(11), nullable=False, doc="手机号")
# course = db.relationship("Course",backref="tea")
# uselist如果为False,不使用列表,而使用标量值,使用在一对一表中
course = db.relationship("Course",primaryjoin="Teacher.tid==foreign(Course.teacher)", backref="tea",uselist=False)
class Grade(db.Model):
__tablename__ = "grade"
id = db.Column(db.Integer,primary_key=True,doc="分数id")
score = db.Column(db.Float,default=0,doc="分数")
student = db.Column("student",db.Integer,db.ForeignKey("student.sid"),doc="学生id")
course = db.Column("course",db.Integer,db.ForeignKey("course.cid"),doc="课程id")
if __name__ == '__main__':
#第一种更新和创建数据库,这种会清空数据数据
db.create_all() #创建表
#db.drop_all() #删除表
#第二种方法执行命令
#manager.run()
# 第二种利用flask_migrate数据库迁移,终端上,当前文件目录下执行:
# python sql_test.py db init 创建migrations文件夹,所有迁移文件放在这文件夹内
#python sql_test.py db migrate 生成迁移脚本,类似django的makemigrations
#python sql_test.py db upgrade 更新数据库 , 类似django的migrate
优点:可以很好的控制数据库表结构,避免发生迁移错误
缺点:需要编写sql语句
先创建数据库表,然后编写模型类
create table student(
id int primary key auto_increment comment "学生id",
sname varchar(4) not null comment "姓名",
gender enum("男","女") default "男" comment "性别",
phone char(11) not null comment "手机号"
) engine="innodb",charset="utf8",comment="学生表";
create table teacher(
id int primary key auto_increment comment "老师id",
tname varchar(4) not null comment "老师姓名",
gender enum("男","女") default "男" comment "性别",
phone char(11) not null comment "手机号"
) engine="innodb" default charset=utf8 comment="教师表";
create table course(
id int primary key auto_increment comment "课程id",
cname varchar(10) not null comment "课程名",
teacher int default null comment "老师id",
constraint c_t_key foreign key(teacher) references teacher(id)
) engine="innodb" default charset=utf8 comment="课程表";
create table grade(
id int primary key auto_increment comment "分数id",
score float default 0 comment "分数",
student int default null comment "学生id",
course int default null comment "课程id",
constraint g_s_key foreign key(student) references student(id),
constraint g_c_key foreign key(course) references course(id)
) engine="innodb" default charset=utf8 comment "分数表";
create table student_course(
id int primary key auto_increment comment "学生课程id",
sid int default null comment "学生id",
cid int default null comment "课程id",
constraint s_s_c foreign key(sid) references student(id),
constraint c_s_c foreign key(cid) references course(id)
) engine="innodb" default charset=utf8 comment="学生课程表";
# 单个新增
student = Student(sname = "张三",gender="男",phone="19971251765")
db.session.add(student)
db.session.commit()
# 通过add_all([])批量新增
s1 = Student(sname = "李四",gender="男",phone="19971251766")
s2 = Student(sname = "王五",gender="男",phone="19971251767")
s3 = Student(sname = "赵六",gender="女",phone="19971251768")
s4 = Student(sname = "刘七",gender="男",phone="19971251769")
db.session.add_all([s1,s2,s3,s4])
db.session.commit()
#返回所有student对象
s = Student.query.all()
#根据主键ID获取对象,若主键不存在返回None
s = Student.query.get(1)
# 返回第一个学生对象
s = Student.query.first()
#条件查询 filter 和 filter_by
s = Student.query.filter_by(gender="男").first()
ss = Student.query.filter_by(gender="男").all()
s = Student.query.filter(Student.gender=="女").first()
# 逻辑或查询
from sqlalchemy import or_
User.query.filter(or_(Student.sname=='张三', Student.age==20)).all()
# 逻辑与查询
from sqlalchemy import and_
User.query.filter(and_(Student.sname=='张三', Student.age==20)).all()
# 逻辑非查询
from sqlalchemy import not_
User.query.filter(not_(Student.sname=='张三', Student.age==20)).all()
Student.query.offset(2).all()
s = Student.query.limit(3).all()
s = Student.query.order_by(Student.sid).all() # 正序
s = Student.query.order_by(Student.sid.desc()).all() # 倒序
from sqlalchemy.orm import load_only
User.query.options(load_only(Student.sname, Student.age)).filter_by(sid=1).first() # 查询特定字段
from sqlalchemy import func
# 对分数表的学生id进行分组, 求学生个数
db.session.query(Grade.student, func.count(Grade.student)).filter(Grade.score>=60).group_by(Grade.student).all()
#查询学生年龄大于20岁,并按照id降序排列,从第二个开始,查出5个
Student.query.filter(Student.age > 20).order_by(Student.sid.desc()).offset(2).limit(5).all()
# 一(课程表)对一(教师)
class Course(db.Model):
teacher = db.Column("teacher",db.Integer,db.ForeignKey("teacher.tid"),doc="老师id")
class Teacher(db.Model):
course = db.relationship("Course",backref="tea",uselist=False)
#通过课程表查询老师信息
c = Course.query.first()
t = c.tea # tea 由backref指定
#通过老师表查询课程信息
t = Teacher.query.first()
c = t.course
# 一(学生)对多(分数)
class Student(db.Model):
grades = db.relationship("Grade",backref="stu")
class Grade(db.Model):
student = db.Column("student",db.Integer,db.ForeignKey("student.sid"),doc="学生id")
#通过分数查学生
g = Grade.query.first()
s = g.stu
#通过学生查分数
s = Student.query.first()
g = s.grades(列表)
# 多(学生)对多(课程)
class Student(db.Model):
courses = db.relationship("Course",secondary="student_course",backref="stu")
#中间表
class StudentCourse(db.Model):
sid = db.Column(db.Integer,db.ForeignKey("student.sid"),doc="学生id")
cid = db.Column(db.Integer,db.ForeignKey("course.cid"),doc="课程id")
class Course(db.Model):
... ...
s = Student.query.first()
cs = s.courses (列表)
for c in cs:
for stu in c.stu:
print(stu.sname)
t = Teacher.query.first()
c = t.course
# mysql 连表查询
from sqlalchemy.orm import load_only,contains_eager
s = Teacher.query.join(Teacher.course).options(load_only(Teacher.tname),contains_eager(Teacher.course).load_only(Course.cname)).first()
print(s.tname,s.course.cname)
# 第一种
c = Student.query.filter(Student.sname=="张三").update({"sname":"章三"})
db.session.commit()
#第二种
stu = Student.query.filter_by(sname="章三").first()
stu.sname = "张三"
db.session.add(stu)
db.session.commit()
Student.query.filter(Student.sid == 1).delete()
db.session.commit()
try:
teacher = Teacher(tname='老王',gender="男",phone="12345678910")
db.session.add(teacher)
db.session.flush() # 将db.session记录的sql传到数据库中执行
course = Course(cname="数学",teacher=teacher.tid)
db.session.add(course)
db.session.commit()
except:
db.session.rollback()