高级自定义¶
通过向 SQLAlchemy
构造函数传递参数,可以自定义扩展管理的各种对象。
模型类¶
SQLAlchemy 模型全部继承自声明基类。在 Flask-SQLAlchemy 中,它显示为 db.Model
,所有模型都对其进行扩展。可以通过子类化默认值并向 model_class
传递自定义类来对其进行自定义。
以下示例为每个模型提供一个整数主键,或为联接表继承提供一个外键。
注意
并非所有内容的整数主键都是最好的数据库设计(这取决于项目的具体要求),这只是一个示例。
from sqlalchemy import Integer, String, ForeignKey
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, declared_attr
class Base(DeclarativeBase):
@declared_attr.cascading
@classmethod
def id(cls):
for base in cls.__mro__[1:-1]:
if getattr(base, "__table__", None) is not None:
return mapped_column(ForeignKey(base.id), primary_key=True)
else:
return mapped_column(Integer, primary_key=True)
db = SQLAlchemy(app, model_class=Base)
class User(db.Model):
name: Mapped[str]
class Employee(User):
title: Mapped[str]
抽象模型和 Mixin¶
如果仅在某些模型(而非所有模型)上需要行为,请使用抽象模型基类仅自定义这些模型。例如,如果某些模型应跟踪其创建或更新的时间。
from datetime import datetime, timezone
from sqlalchemy.orm import Mapped, mapped_column
class TimestampModel(db.Model):
__abstract__ = True
created: Mapped[datetime] = mapped_column(default=lambda: datetime.now(timezone.utc))
updated: Mapped[datetime] = mapped_column(default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc))
class Author(db.Model):
id: Mapped[int] = mapped_column(primary_key=True)
username: Mapped[str] = mapped_column(unique=True)
class Post(TimestampModel):
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str]
还可以使用 mixin 类来完成此操作,分别从 db.Model
继承。
class TimestampMixin:
created: Mapped[datetime] = mapped_column(default=lambda: datetime.now(timezone.utc))
updated: Mapped[datetime] = mapped_column(default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc))
class Post(TimestampMixin, db.Model):
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str]
禁用表名生成¶
某些项目更喜欢手动设置每个模型的 __tablename__
,而不是依赖 Flask-SQLAlchemy 的检测和生成。实现此目的的简单方法是设置每个 __tablename__
,而不修改基类。但是,可以通过在 SQLAlchemy 构造函数中设置 disable_autonaming=True 来禁用表名生成。
class Base(sa_orm.DeclarativeBase):
pass
db = SQLAlchemy(app, model_class=Base, disable_autonaming=True)
会话类¶
Flask-SQLAlchemy 的 Session
类根据与模型或表关联的绑定键选择要查询的引擎。但是,还有其他策略(例如水平分片),可以使用不同的会话类来实现。 class_
是扩展的 session_options
参数的键,用于更改会话类。
Flask-SQLAlchemy 总是会将扩展实例作为 db
参数传递给会话,所以它必须接受该参数才能继续工作。这可用于访问 db.engines
。
from sqlalchemy.ext.horizontal_shard import ShardedSession
from flask_sqlalchemy.session import Session
class CustomSession(ShardedSession, Session):
...
db = SQLAlchemy(session_options={"class_": CustomSession})
查询类¶
警告
查询接口在 SQLAlchemy 中被认为是过时的。这包括 session.query
、Model.query
、db.Query
和 lazy="dynamic"
关系。最好使用 session.execute(select(...))
。
可以自定义会话、模型和关系使用的查询接口。这可用于添加额外的查询方法。例如,你可以添加一个 get_or
方法,该方法获取一行或返回一个默认值。
from flask_sqlalchemy.query import Query
class GetOrQuery(Query):
def get_or(self, ident, default=None):
out = self.get(ident)
if out is None:
return default
return out
db = SQLAlchemy(query_class=GetOrQuery)
user = User.query.get_or(user_id, anonymous_user)
传递 query_class
参数将自定义 db.Query
、db.session.query
、Model.query
和 db.relationship(lazy="dynamic")
关系。也可以在每个对象的基础上自定义这些关系。
要自定义特定模型的 query
属性,请在模型类上设置 query_class
属性。
class User(db.Model):
query_class = GetOrQuery
要自定义特定的动态关系,请将 query_class
参数传递给该关系。
db.relationship(User, lazy="dynamic", query_class=GetOrQuery)
要仅自定义 session.query
,请将 query_cls
键传递给构造函数的 session_options
参数。
db = SQLAlchemy(session_options={"query_cls": GetOrQuery})