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获取!
daniel 用户 2022-12-01T16:39:19
good