展开

文章目录

修改历史

修改历史记录

  1. 2022-09-04 22:52:44
  2. 2022-09-04 18:55:36
  3. 2022-09-04 18:55:16
  4. 2022-09-04 18:54:45
  5. 2022-09-04 18:32:29

Flask-SQLAlchemy一些高级用法记录

2022-09-04 18:26:23 Python 715

简介

相信使用过Python的同学都知道SQLAlchemy这个数据库ORM库,通过这个第三方库,我们可以告别原生的SQL语句,通过ORM去对数据库进行CURD,本篇博客就对一些不常见的使用方法进行一个记录汇总,方便后续在开发中使用时进行查询。

1. 使用Mixin定义共有字段

使用ORM时,我们首先必须得先通过Class定义一个数据库表的模型对象,例如我有一张emp表,如下所示

如果我们想通过ORM去操作这张表的话,首先需要通过定义Class来映射emp表的结构,如下面的代码所示,这样我们需要操作emp表的话,就可以直接通过Employee这个类来进行操作了。

class Employee(db.Model):
    __tablename__ = 'emp'

    id = db.Column(db.INTEGER, primary_key=True, autoincrement=True)
    username = db.Column(db.String(128), default='')
    password = db.Column(db.String(128), default='')
    department = db.Column(db.INTEGER, default=0, comment='')

在很多场景,都需要将表中记录的创建时间以及修改时间记录下来,比如上述的emp表,如果需要记录,则需要在Employee 的定义中新增两个字段,如下所示

class Employee(db.Model):
    ...
    create_time = db.Column(db.DATETIME, default=datetime.now)
    update_time = db.Column(db.DATETIME, default=datetime.now, onupdate=datetime.now)

如果在其他表中也需要记录这两个字段呢,这时候我们就可以通过Mixin来将每个都有的公共字段定义在一个Class里面,然后通过继承该Mixin使得每个表结构中都包含该公有字段

class TimestampMixin(object):
    create_time = db.Column(db.DateTime, default=db.func.now())
    update_time = db.Column(db.DateTime, default=db.func.now(), onupdate=db.func.now())

然后将Employee类继承自TimestampMixin类既可

class Employee(TimestampMixin, db.Model):
    __tablename__ = 'emp'

    id = db.Column(db.INTEGER, primary_key=True, autoincrement=True)
    username = db.Column(db.String(128), default='')
    password = db.Column(db.String(128), default='')
    department = db.Column(db.INTEGER, default=0, comment='')

这是查看emp的表结构,如下图所示

可以发现,继承的两个字段被自动定义在了最前面了,这与平时的习惯不一致,一般都是ID为第一个字段,这种时间戳的字段一般都是放在表的末尾,那么要怎么做,才能将公共字段自动定义到表的末尾呢?可以通过SQLAlchemy里面的declared_attr装饰器去实现,如下代码

class TimestampMixin(object):

    @declared_attr
    def created_time(cls):
        return db.Column(db.DateTime, default=datetime.now)

    @declared_attr
    def updated_time(cls):
        return db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now)

此时再去查看emp表的结构,就会发现公共字段被自动的定义到了末尾了,如下图

2. 使用JOIN

JOIN在MySQL语法中使用的非常之多,常见的有JOIN、RIGHT JOIN、LEFT JOIN三种形式,通过SQLAlchemy也可以实现这三种形式,跟随这第一节的例子,继续创建一张department表,用来存储部门相关的信息,如下代码

class Department(TimestampMixin, db.Model):
    __tablename__ = 'dept'

    id = db.Column(db.INTEGER, primary_key=True, autoincrement=True)
    name = db.Column(db.String(128), default='')

    def __init__(self, name):
        self.name = name

emp表通过字段department与dept表进行关联,这里是通过逻辑外键进行关联的,而不是通过物理外键进行关联的,因此如果需要进行链表查询的话,就需要用JOIN的方式,比如查询部门为研发部的员工,可以通过下面的代码进行查询

emp = Employee.query.join(Department, Employee.department == Department.id).filter(Department.name == '研发部').all()

默认的JOIN为内连接,如果需要使用左连接,可以是有outerjoin或者设置is_outer=True

emp = Employee.query.outerjoin(Department, Employee.department == Department.id).filter(Department.name == '研发部').all()
emp = Employee.query.join(Department, Employee.department == Department.id, is_outer=True).filter(Department.name == '研发部').all()

右连接跟左连接一样,想使用右连接直接调换一下表的顺序即可!

3. 选择字段

在MySQL中SELECT * 表示选择所有字段,与此同时,也可以通过指定字段来使得查询结果中只包含有指定指端的值,在SQLAlchemy中可以通过with_entities来指定查询结果中返回哪些字段,在第二节中,使用了JOIN但是没有指定字段,因此还是无法获取到员工所在的部门信息,通过with_entities就可以获取员工所在部门的信息了,代码如下

emp = Employee.query.join(Department, Employee.department == Department.id).with_entities(Employee.username, Department.name).filter(
        Department.name == '研发部').all()
print(str(emp))

查询结果

with_entities函数除了可以指定模型类的字段外,还可以在其中使用函数,比如我想查询各个部门的人数,代码如下

emp = Employee.query.join(Department, Employee.department == Department.id).with_entities(Department.name,db.func.count( Employee.id)).group_by(Department.name).all()

查询结果

完整的是示例代码请访问https://github.com/weijiang1994/blog-demo获取!

1条评论


daniel 用户

good

Macv 博主 回复:daniel

very good!