Flask 数据库orm操作Flask-SQLAlchemy

段干德泽
2023-12-01

在这里插入代码片## ORM
ORM 全拼Object-Relation Mapping,中文意为 对象-关系映射。主要实现模型对象到关系数据库数据的映射

优点 :

  • 只需要面向对象编程, 不需要面向数据库编写代码.
    • 对数据库的操作都转化成对类属性和方法的操作.
    • 不用编写各种数据库的sql语句.
  • 实现了数据模型与数据库的解耦, 屏蔽了不同数据库操作上的差异.
    • 不再需要关注当前项目使用的是哪种数据库。
    • 通过简单的配置就可以轻松更换数据库, 而不需要修改代码.

缺点 :

  • 相比较直接使用SQL语句操作数据库,有性能损失.
  • 根据对象的操作转换成SQL语句,根据查询的结果转化成对象, 在映射过程中有性能损失.

Flask-SQLAlchemy

flask默认提供模型操作,但是并没有提供ORM,所以一般开发的时候我们会采用flask-SQLAlchemy模块来实现ORM操作。

SQLAlchemy是一个关系型数据库框架,它提供了高层的 ORM 和底层的原生数据库的操作。flask-sqlalchemy 是一个简化了 SQLAlchemy 操作的flask扩展。

SQLAlchemy: https://www.sqlalchemy.org/

中文文档: https://www.osgeo.cn/sqlalchemy/index.html

安装 flask-sqlalchemy【清华源】

pip install flask-sqlalchemy -i https://pypi.tuna.tsinghua.edu.cn/simple

如果连接的是 mysql 数据库,需要安装 mysqldb 驱动

pip install flask-mysqldb -i https://pypi.tuna.tsinghua.edu.cn/simple

安装flask-mysqldb时,注意

安装 flask-mysqldb的时候,python底层依赖于一个底层的模块 mysql-client模块
如果没有这个模块,则会报错如下:

Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-install-21hysnd4/mysqlclient/

解决方案:

sudo apt-get install libmysqlclient-dev python3-dev

运行上面的安装命令如果再次报错如下:
   dpkg 被中断,您必须手工运行 ‘sudo dpkg --configure -a’ 解决此问题。

则根据提示执行命令以下命令,再次安装mysqlclient
	sudo dpkg --configure -a
	apt-get install libmysqlclient-dev python3-dev

解决了mysqlclient问题以后,重新安装 flask-mysqldb即可。
pip install flask-mysqldb -i https://pypi.tuna.tsinghua.edu.cn/simple

数据库连接设置

在 Flask-SQLAlchemy 中,数据库使用URL指定,而且程序使用的数据库必须保存到Flask配置对象的 SQLALCHEMY_DATABASE_URI 键中

config.py,配置文件代码:

class Config(object):
    DEBUG = True
    SECRET_KEY = "*(%#4sxcz(^(#$#8423"
    # 数据库链接配置 = 数据库名称://登录账号:登录密码@数据库主机IP:数据库访问端口/数据库名称?charset=编码类型
    SQLALCHEMY_DATABASE_URI = "mysql://root:123@127.0.0.1:3306/students?charset=utf8mb4"

其他设置:

# 动态追踪修改设置,如未设置只会提示警告
SQLALCHEMY_TRACK_MODIFICATIONS = True
#查询时会显示原始SQL语句
SQLALCHEMY_ECHO = True

配置完成需要去 MySQL 中创建项目所使用的数据库

mysql -uroot -p123
mysql > create database students charset=utf8mb4;

常用的SQLAlchemy字段类型

模型字段类型名python中数据类型说明
Integerint普通整数,一般是32位
SmallIntegerint取值范围小的整数,一般是16位
BigIntegerint或long不限制精度的整数
Floatfloat浮点数
Numericdecimal.Decimal普通数值,一般是32位
Stringstr变长字符串
Textstr变长字符串,对较长或不限长度的字符串做了优化
LongTextstr长文本类型
Unicodeunicode变长Unicode字符串
UnicodeTextunicode变长Unicode字符串,对较长或不限长度的字符串做了优化
Booleanbool布尔值
Datedatetime.date日期
Timedatetime.time时间
DateTimedatetime.datetime日期和时间
DECIMALdecimal.Decimal定点类型
Enumstr枚举类型

常用的SQLAlchemy列约束选项

选项名说明
primary_key如果为True,代表表的主键
unique如果为True,代表这列不允许出现重复的值
index如果为True,为这列创建索引,提高查询效率
nullable如果为True,允许有空值,如果为False,不允许有空值
default为这列定义默认值
autoincrement是否自动增长
onupdate更新的时候执行的函数
name该属性在数据库中的字段映射

query可以参数:

  1. 模型对象。指定查找这个模型中所有的对象。
  2. 模型中的属性。可以指定只查找某个模型的其中几个属性。
  3. 聚合函数:
选项名说明
func.count统计行的数量
func.avg求平均值
func.max求最大值
func.min求最小值
func.sum求和

过滤方法:

过滤是数据 提取的一个很重要的功能,以下对一些常用的过滤条件进行详解,并且这些过滤条件都是只能通过filter方法实现的:

  1. equals:
query.filter(User.name == 'ed')
  1. not equals:
query.filter(User.name != 'ed')
  1. like:
query.filter(User.name.like('%ed%'))
  1. in:
query.filter(User.name.in_(['ed','wendy','jack']))
#同时
query.filter(User.name.in_(session.query(User.name).filter(User.name.like('%ed%'))))
  1. not in:
query.filter(~User.name.in_('ed','wendy','jack'))
  1. is null:
query.filter(User.name==None)

query.filter(User.name.is_(None))
  1. is not null:
query.filter(User.name != None)

query.filter(User.name.isnot(None)
  1. and:
from sqlalchemy import and_

query.filter(and_(User.name=='ed', User.fullname=='Ed Jones'))
# 或者
query.filter(User.name=='ed', User.fullname=='Ed Jones')
# 或者
query.filter(User.name=='ed',).filter(User.fullname=='Ed Jones')
  1. or:
from sqlalchemy import or_ 
query.filter(or_(User.name='ed', User.name='wendy'))

数据库基本操作

  • 在Flask-SQLAlchemy中,添加、修改、删除操作,均由数据库会话管理。
    • 会话用 db.session 表示。在准备把数据写入数据库前,要先将数据添加到会话中然后调用 db.commit() 方法提交会话。
  • 在 Flask-SQLAlchemy 中,查询操作是通过 query 对象操作数据。
    • 最基本的查询是返回表中所有数据,可以通过过滤器进行更精确的数据库查询。

定义模型类

from flask import Flask, render_template, request
from flask_sqlalchemy import SQLAlchemy
import os

app = Flask(__name__, template_folder='templates')


class Config(object):
    DEBUG = True
    SECRET_KEY = os.urandom(24)  # 随机生成一个密钥
    # 数据库链接配置 = 数据库名称://登录账号:登录密码@数据库主机IP:数据库访问端口/数据库名称?charset=编码类型
    SQLALCHEMY_DATABASE_URI = "mysql://root:123456@127.0.0.1:3306/students?charset=utf8mb4"
    # 动态追踪修改设置,如未设置只会提示警告
    SQLALCHEMY_TRACK_MODIFICATIONS = True
    # 查询时会显示原始SQL语句
    SQLALCHEMY_ECHO = True


app.config.from_object(Config)
# 初始化SQLAlchemy
db = SQLAlchemy()  # 初始化数据库操作对象
db.init_app(app)  # 初始化数据库链接


# 创建模型类
class Student(db.Model):
    # 表结构声明
    __tablename__ = "tb_student"

    # 字段声明
    id = db.Column(db.Integer, primary_key=True, comment="主键ID") # 字段类型
    name = db.Column(db.String(250), comment="姓名")
    age = db.Column(db.Integer, comment="年龄")
    sex = db.Column(db.Boolean, default=False, comment="性别")
    # DECIMAL设置小数精度,最多八位小数点后面两位 ,允许有空值,comment为列注释
    money = db.Column(db.DECIMAL(8,2), nullable=True, comment="钱包")     

    def __repr__(self):          # 定义 __repr__ 是为了方便调试,你可以不定义,也可以定义的更详细一些。

        return self.name
    
    
class Teacher(db.Model):
    __tablename__ = "tb_teacher"

    id = db.Column(db.Integer, primary_key=True, comment="主键ID")
    name = db.Column(db.String(250), comment="姓名")
    sex = db.Column(db.Boolean, default=False, comment="性别")
    option = db.Column(db.Enum("老师", "助教", "班主任"), default="老师", comment="职位")

    def __repr__(self):    # 定义 __repr__ 是为了方便调试,你可以不定义,也可以定义的更详细一些。
        return self.name


class Course(db.Model):
    __tablen__ = "tb_course"
    id = db.Column(db.Integer, primary_key=True, comment="主键ID")
    name = db.Column(db.String(250),comment="课程名称")
    price = db.Column(db.Numeric(6.2))  # 设置小数精度,一共6位小数点后面2位

    def __repr__(self):
        return self.name


@app.route('/')
def index():
    return 'ok'


if __name__ == '__main__':
    # with app.app_context():
    #     db.create_all() # 根据模型创建所有的数据表
    #     # db.drop_all()   # 删除模型对应的所有数据表
    app.run()

数据表操作

创建表

db.create_all()

注意,create_all()方法执行的时候,需要放在模型的后面
上面这段语句,后面我们需要转移代码到flask-script的自定义命令中。
执行了一次以后,需要注释掉。

删除表

db.drop_all()

代码:

from flask import Flask, render_template, request
from flask_sqlalchemy import SQLAlchemy
import os

app = Flask(__name__, template_folder='templates')


class Config(object):
    DEBUG = True
    SECRET_KEY = os.urandom(24)  # 随机生成一个密钥
    # 数据库链接配置 = 数据库名称://登录账号:登录密码@数据库主机IP:数据库访问端口/数据库名称?charset=编码类型
    SQLALCHEMY_DATABASE_URI = "mysql://root:123456@127.0.0.1:3306/students?charset=utf8mb4"
    # 动态追踪修改设置,如未设置只会提示警告
    SQLALCHEMY_TRACK_MODIFICATIONS = True
    # 查询时会显示原始SQL语句
    SQLALCHEMY_ECHO = True


app.config.from_object(Config)
# 初始化SQLAlchemy
db = SQLAlchemy()  # 初始化数据库操作对象
db.init_app(app)  # 初始化数据库链接


# 创建模型类
class Student(db.Model):
    # 表结构声明


    __tablename__ = "tb_student"

    # 字段声明
    id = db.Column(db.Integer, primary_key=True, comment="主键ID") # 字段类型
    name = db.Column(db.String(250), comment="姓名")
    age = db.Column(db.Integer, comment="年龄")
    sex = db.Column(db.Boolean, default=False, comment="性别")
    # DECIMAL设置小数精度,一共八位小数点后面两位 ,允许有空值,comment为列注释
    money = db.Column(db.DECIMAL(8,2), nullable=True, comment="钱包")

    def __repr__(self):          # 定义 __repr__ 是为了方便调试,你可以不定义,也可以定义的更详细一些。

        return self.name


class Teacher(db.Model):
    __tablename__ = "tb_teacher"

    id = db.Column(db.Integer, primary_key=True, comment="主键ID")
    name = db.Column(db.String(250), comment="姓名")
    sex = db.Column(db.Boolean, default=False, comment="性别")
    option = db.Column(db.Enum("老师", "助教", "班主任"), default="老师", comment="职位")

    def __repr__(self):    # 定义 __repr__ 是为了方便调试,你可以不定义,也可以定义的更详细一些。
        return self.name


class Course(db.Model):
    __tablen__ = "tb_course"
    id = db.Column(db.Integer, primary_key=True, comment="主键ID")
    name = db.Column(db.String(250),comment="课程名称")
    price = db.Column(db.Numeric(6.2))  # 设置小数精度,一共6位小数点后面2位

    def __repr__(self):
        return self.name


@app.route('/')
def index():
    """数据库基本操作"""

    """添加数据"""
    # 添加一条数据
    # student1 = Student(name='小灰',age=16,money=100,sex=True)
    # db.session.add(student1)
    # db.session.commit()

    # 批量添加多条数据
    # data_list = [
    #     Student(name="xiaohui1号",age=16,money=1000, sex=True),
    #     Student(name="xiaohui2号",age=16,money=1000, sex=True),
    #     Student(name="xiaohui3号",age=16,money=1000, sex=True),
    #     Student(name="xiaohui4号",age=16,money=1000, sex=True),
    #     Student(name="xiaohui5号",age=16,money=1000, sex=True),
    #     Student(name="xiaohui6号",age=16,money=1000, sex=True),
    # ]
    # db.session.add_all(data_list)
    # db.session.commit()
    """查询数据"""
    # 根据主键ID查询一条数据,如果ID不存在,则返回None不会报错!
    # student = Student.query.get(100)
    # if student is None:
    #     print("当前学生不存在!")
    # else:
    #     print(student)
    #     print(student.name,student.age)

    # 根据查询条件获取一条数据
    # 模型.query.filter(模型.字段==条件值).first()
    # student = Student.query.filter(Student.id==1).one()  # 返回最多一个结果,而且以单项形式,而不是只有一个元素的tuple形式返回这个结果
    # print(student)
    # print(student.name, student.age)

    # 根据查询条件获取多条数据
    # 模型.query.filter(模型.字段==条件值).all()
    # student = Student.query.filter(Student.id<5).first()
    # print(student)

    """更新数据"""
    # 先查询后修改
    # student = Student.query.filter(Student.name=='小灰').first()
    # student.money += 1000
    # db.session.commit()

    # 直接根据条件修改
    # Student.query.filter(Student.name == "小灰", Student.money == 1100)\
    #     .update({Student.money: 2000})  # 乐观锁
    # 实现类似django的F函数效果,字段值累加
    # Student.query.filter(Student.name == '小灰').update({Student.money: Student.money+50})
    # db.session.commit()

    """删除数据"""
    # 先查询后删除
    # student = Student.query.filter(Student.name == 'xiaohui6号').first()
    # db.session.delete(student)
    # db.session.commit()

    # 直接根据条件进行删除操作
    student = Student.query.filter(Student.name == "xiaohui5号").delete()
    db.session.commit()

    return 'ok'


if __name__ == '__main__':
    # with app.app_context():
    #     db.create_all() # 根据模型创建所有的数据表
    #     # db.drop_all()   # 删除模型对应的所有数据表
    app.run()

常用的SQLAlchemy查询过滤器

过滤器说明
filter()把过滤器添加到原查询上,返回一个新查询
filter_by()把等值过滤器添加到原查询上,返回一个新查询
limit()使用指定的值限定原查询返回的结果
offset()偏移原查询返回的结果,返回一个新查询
order_by()根据指定条件对原查询结果进行排序,返回一个新查询
group_by()根据指定条件对原查询结果进行分组,返回一个新查询

常用的SQLAlchemy查询结果的方法

方法说明
all()以列表形式返回查询的所有结果
first()返回查询的第一个结果,如果未查到,返回None
first_or_404()返回查询的第一个结果,如果未查到,返回404
get()返回指定主键对应的行,如不存在,返回None
get_or_404()返回指定主键对应的行,如不存在,返回404
count()返回查询结果的数量
paginate()返回一个Paginate分页器对象,它包含指定范围内的结果
having返回结果中符合条件的数据,必须跟在group by后面,其他地方无法使用。

get():参数为数字,表示根据主键查询数据,如果主键不存在返回None

Student.query.get()

all()返回查询到的所有对象

Student.query.all()

first()返回查询到的第一个对象【first获取一条数据,all获取多条数据】

Student.query.first()

filter模糊查询,支持各种运算符和查询方法

from flask import Flask, render_template, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import or_, and_, not_
import os

app = Flask(__name__, template_folder='templates')


class Config(object):
    DEBUG = True
    SECRET_KEY = os.urandom(24)  # 随机生成一个密钥
    # 数据库链接配置 = 数据库名称://登录账号:登录密码@数据库主机IP:数据库访问端口/数据库名称?charset=编码类型
    SQLALCHEMY_DATABASE_URI = "mysql://root:123456@127.0.0.1:3306/students?charset=utf8mb4"
    # 动态追踪修改设置,如未设置只会提示警告
    SQLALCHEMY_TRACK_MODIFICATIONS = True
    # 查询时会显示原始SQL语句
    SQLALCHEMY_ECHO = True


app.config.from_object(Config)
# 初始化SQLAlchemy
db = SQLAlchemy()  # 初始化数据库操作对象
db.init_app(app)  # 初始化数据库链接


# 创建模型类
class Student(db.Model):
    # 表结构声明


    __tablename__ = "tb_student"

    # 字段声明
    id = db.Column(db.Integer, primary_key=True, comment="主键ID") # 字段类型
    name = db.Column(db.String(250), comment="姓名")
    age = db.Column(db.Integer, comment="年龄")
    sex = db.Column(db.Boolean, default=False, comment="性别")
    # DECIMAL设置小数精度,一共八位小数点后面两位 ,允许有空值,comment为列注释
    money = db.Column(db.DECIMAL(8,2), nullable=True, comment="钱包")

    def __repr__(self):          # 定义 __repr__ 是为了方便调试,你可以不定义,也可以定义的更详细一些。

        return self.name


class Teacher(db.Model):
    __tablename__ = "tb_teacher"

    id = db.Column(db.Integer, primary_key=True, comment="主键ID")
    name = db.Column(db.String(250), comment="姓名")
    sex = db.Column(db.Boolean, default=False, comment="性别")
    option = db.Column(db.Enum("老师", "助教", "班主任"), default="老师", comment="职位")

    def __repr__(self):    # 定义 __repr__ 是为了方便调试,你可以不定义,也可以定义的更详细一些。
        return self.name


class Course(db.Model):
    __tablen__ = "tb_course"
    id = db.Column(db.Integer, primary_key=True, comment="主键ID")
    name = db.Column(db.String(250),comment="课程名称")
    price = db.Column(db.Numeric(6.2))  # 设置小数精度,一共6位小数点后面2位

    def __repr__(self):
        return self.name


@app.route('/0')
def index0():
    """数据库基本操作"""

    """添加数据"""
    # 添加一条数据
    # student1 = Student(name='小灰',age=16,money=100,sex=True)
    # db.session.add(student1)
    # db.session.commit()

    # 批量添加多条数据
    # data_list = [
    #     Student(name="xiaohui1号",age=16,money=1000, sex=True),
    #     Student(name="xiaohui2号",age=16,money=1000, sex=True),
    #     Student(name="xiaohui3号",age=16,money=1000, sex=True),
    #     Student(name="xiaohui4号",age=16,money=1000, sex=True),
    #     Student(name="xiaohui5号",age=16,money=1000, sex=True),
    #     Student(name="xiaohui6号",age=16,money=1000, sex=True),
    # ]
    # db.session.add_all(data_list)
    # db.session.commit()
    """查询数据"""
    # 根据主键ID查询一条数据,如果ID不存在,则返回None不会报错!
    # student = Student.query.get(100)
    # if student is None:
    #     print("当前学生不存在!")
    # else:
    #     print(student)
    #     print(student.name,student.age)

    # 根据查询条件获取一条数据
    # 模型.query.filter(模型.字段==条件值).first()
    # student = Student.query.filter(Student.id==1).one()  # 返回最多一个结果,而且以单项形式,而不是只有一个元素的tuple形式返回这个结果
    # print(student)
    # print(student.name, student.age)

    # 根据查询条件获取多条数据
    # 模型.query.filter(模型.字段==条件值).all()
    # student = Student.query.filter(Student.id<5).first()
    # print(student)

    """更新数据"""
    # 先查询后修改
    # student = Student.query.filter(Student.name=='小灰').first()
    # student.money += 1000
    # db.session.commit()

    # 直接根据条件修改
    # Student.query.filter(Student.name == "小灰", Student.money == 1100)\
    #     .update({Student.money: 2000})  # 乐观锁
    # 实现类似django的F函数效果,字段值累加
    # Student.query.filter(Student.name == '小灰').update({Student.money: Student.money+50})
    # db.session.commit()

    """删除数据"""
    # 先查询后删除
    # student = Student.query.filter(Student.name == 'xiaohui6号').first()
    # db.session.delete(student)
    # db.session.commit()

    # 直接根据条件进行删除操作
    student = Student.query.filter(Student.name == "xiaohui5号").delete()
    db.session.commit()

    return 'ok'
@app.route('/')
def index():
    """数据库进阶操作"""
    """filter设置判断条件
    ==  判断相等
    >=
    <
    >
    <=
    !=
    """
    # studnet = Student.query.filter(Student.age<=15).all()
    # if not studnet:
    #     return jsonify({'error': '404', 'errmsg': '没有如何条件的年龄!'})
    # print(studnet,type(studnet))


    """filter设置模糊查询"""
    # like模糊条件
    # 模型.字段.like("%值%")  等价于  模型.字段.contains("值")    包含xxx
    # 模型.字段.like("值%")   等价于  模型.字段.startswith("值")  以xxx开头
    # 模型.字段.like("%值")   等价于  模型.字段.endswith("值")    以xxx结尾
    # 模型.字段.like("__")    值长度为2个字符的.几个下划线代表几个字符

    # student_list = Student.query.filter(Student.name.like('%xiao%')).all()
    # student_list = Student.query.filter(Student.name.like('%3号')).all()
    # student_list = Student.query.filter(Student.name.like('__')).all()
    # print(student_list)

    """filter_by设置精确条件查找数据"""
    # filter_by 只支持一个等号作为判断条件,而且字段左边不需要声明模型类名
    # 可以用于获取一条数据,也可以获取多条数据
    # student = Student.query.filter_by(age=16).first()

    """filter多条件查询"""
    # 多条件需要基于逻辑运算来编写,当然,可以其他的声明方式
    """and_ 并且, 与"""
    # student = Student.query.filter(and_(Student.age<20,Student.name.like('%xiao%'))).all()

    """or_ 或者,或"""
    # student = Student.query.filter(or_(Student.age==16,Student.age>20)).all()

    """not_ 排除,非"""
    # student = Student.query.filter(not_(Student.age==16)).all()

    """filter值范围查询"""
    # 查询年龄=15或者17或者19的
    # student = Student.query.filter(Student.age.in_([15,17,19])).all()

    """order_by结果排序"""
    # order_by(模型.字段.desc())   db.desc(模型.字段)    倒序
    # order_by(模型.字段.asc())    db.asc(模型.字段)     升序
    # student = Student.query.order_by(db.desc(Student.age)).all()
    # student = Student.query.order_by(db.asc(Student.age)).all()

    """count 统计结果数量"""
    # student = Student.query.filter(Student.sex==1).count()

    """limit 结果数量进行限制"""
    """offset 对查询开始位置进行设置"""
    student = Student.query.order_by(Student.money.desc()).offset(2).limit(3).all()

    # print(student)

    """paginate分页器"""
    # paginate(page=当前页码, per_page=每一页数据量, max_per_page=每一页最大数据量)
    # 当前页码,默认是从request.args["page"],如果当前参数没有值,则默认为1
    # 每一页数据量,默认是100条
    # 因为分页器有提供了一个  request.args.["per_page"]给客户端设置每一页数据量,所以再次限定客户端最多能设置的每一页数据量

    # pagination = Student.query.filter(Student.sex==True).paginate(per_page=1)
    # print(pagination)
    # print( pagination.items ) # 获取当前页数据量
    # print( pagination.has_next ) # 如果还有下一页数据,则结果为True
    # print( pagination.has_prev ) # 如果有上一页数据,则结果为True
    # print( pagination.page ) # 当前页页码 request.args.get("page",1)
    # print( pagination.total ) # 本次查询结果的数据总量[被分页的数据量总数]
    # print( pagination.pages )   # 总页码
    # print( pagination.prev() ) # 上一页的分页器对象,如果没有上一页,则默认为None
    # print( pagination.next() ) # 下一页的分页器对象,如果没有下一页,则默认为None
    # if pagination.has_next:
    #     print( pagination.next().items ) # 下一页的数据列表
    # return  render_template('list.html',pagination=pagination)

    return jsonify({'ok': '202'})


if __name__ == '__main__':
    # with app.app_context():
    #     db.create_all() # 根据模型创建所有的数据表
    #     # db.drop_all()   # 删除模型对应的所有数据表
    app.run()

list.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
    .page a,.page span{
        padding: 2px 6px;
        color: #fff;
        background: #6666ff;
        text-decoration: none;
    }
    .page span{
        color: #fff;
        background: orange;
    }

    </style>
</head>
<body>
    <table border="1" align="center" width="600">
        <tr>
           <th>ID</th>
           <th>age</th>
           <th>name</th>
           <th>sex</th>
           <th>money</th>
        </tr>
        {% for student in pagination.items %}
        <tr>
           <td>{{ student.id }}</td>
           <td>{{ student.age }}</td>
           <td>{{ student.name }}</td>
           <td>{{ "男" if student.sex else "女" }}</td>
           <td>{{ student.money }}</td>
        </tr>
        {% endfor %}
        <tr align="center">
            <td colspan="5" class="page">
                {% if pagination.has_prev %}
                <a href=http://127.0.0.1:5000/?page=2"?page=1">首  页</a>
                <a href="?page={{ pagination.prev_num }}">上一页</a>
                <a href="?page={{ pagination.prev_num }}">{{ pagination.prev_num }}</a>
                {% endif %}
                <span>{{ pagination.page }}</span>
                {% if pagination.has_next %}
                <a href="?page={{ pagination.next_num }}">{{ pagination.next_num }}</a>
                <a href="?page={{ pagination.next_num }}">下一页</a>
                <a href="?page={{ pagination.pages }}">尾  页</a>
                {% endif %}
            </td>
        </tr>
    </table>
</body>
</html>

分组查询和分组查询结果过滤

一般分组都会结合聚合函数来一起使用。SQLAlchemy中所有的聚合函数都在func模块中声明的。

from sqlalchemy import func

函数名说明
func.count统计总数
func.avg平均值
func.min最小值
func.max最大值
func.sum

代码视图:


@app.route("/")
def index():
    from sqlalchemy import func
    """ group_by 分组查询"""
    # 查询男生和女生的最大年龄
    # ret = db.session.query(Student.sex,func.max(Student.age)).group_by(Student.sex).all()
    # print(ret)

    # 查询出男生和女生年龄大于18的人数
    # having是针对分组的结果进行过滤处理,所以having能调用的字段,必须是分组查询结果中的字段,否则报错!!
    # ret = db.session.query(Student.sex,Student.age, func.count(Student.age)).group_by(Student.sex,Student.age).having(Student.age>18).all()
    # print(ret)

    """执行原生SQL语句,返回结果不是模型对象, 是列表和元祖"""
    # 查询多条
    # ret = db.session.execute("select id,name,age,IF(sex,'男','女') from tb_student").fetchall()
    # print(ret)
    # # 查询单条
    # ret = db.session.execute("select * from tb_student where id = 3").fetchone()
    # print(ret)

    # 添加/修改/删除
    # db.session.execute("UPDATE tb_student SET money=(money + %s) WHERE age = %s" % (200, 22))
    # db.session.commit()

    # 查询出女生和男生中大于18岁的人数
    ret = db.session.execute("SELECT IF(sex,'男','女'), count(id) from (SELECT id,name,age,sex FROM `tb_student` WHERE age>18) as stu group by sex").fetchall()
    print(ret)
    return "Ok"

执行原生SQL语句:

    """执行原生SQL语句,返回结果不是模型对象, 是列表和元祖"""
    # 查询多条
    # ret = db.session.execute("select id,name,age,IF(sex,'男','女') from tb_student").fetchall()
    # print(ret)
    # # 查询单条
    # ret = db.session.execute("select * from tb_student where id = 3").fetchone()
    # print(ret)

    # 添加/修改/删除
    # db.session.execute("UPDATE tb_student SET money=(money + %s) WHERE age = %s" % (200, 22))
    # db.session.commit()

    # 查询出女生和男生中大于18岁的人数
    ret = db.session.execute("SELECT IF(sex,'男','女'), count(id) from (SELECT id,name,age,sex FROM `tb_student` WHERE age>18) as stu group by sex").fetchall()
    print(ret)
 类似资料: