Skip to content

数据库管理

ORM基类

以下是数据库ORM基类的定义

# Create a Base class using the custom MetaData
# Base = declarative_base(metadata=meta)  # SQLAlchemy V2 版本推荐使用基于类DeclarativeBase的扩展来取代原来的declarative_base()方法


class DBBase(DeclarativeBase):
    """
    Attributes:
        metadata: The metadata object for the database. 等价于 declarative_base(metadata=meta)的方法
        __table_args__: The table arguments for the database. Specify the schema for the database here.
    """

    metadata = meta
    __table_args__ = {"schema": "tfrserver"}

    create_timestamp: Mapped[int] = Column(BigInteger, nullable=False, default=now_timestamp)
    update_timestamp: Mapped[int] = Column(BigInteger, nullable=False, default=now_timestamp, onupdate=now_timestamp)

需要注意:

  1. 随着SQLAlchemy V2版本的全面升级,推荐使用基于类DeclarativeBase的扩展来取代原来的declarative_base()方法
  2. 通过__table_args__属性可以指定数据库的schema。但需要注意,在子类中存在定义索引的需求,按SQLAlchemy的实现,常见地有两种方式来实现索引定义,分别如下:

方式一:在数据类中使用__table_args__属性定义索引

class DocMetadata(DBBase):
    __tablename__ = "doc_metadata"

    doc_id: Mapped[int] = Column(
        Integer,
        primary_key=True,
        autoincrement=False,
        nullable=False,
        comment="指向TFRobot框架中的Document的ID。完整路径为`doc.documents.doc_id`,`doc`是Schema,`documents`是表名",
    )
    meta: Mapped[dict] = Column("meta", JSONB)

    __table_args__ = (
        Index(None, "meta", postgresql_using='gin'), {'schema': 'tfrserver'}
    )

方式二:使用Index类定义索引

class DocMetadata(DBBase):
    __tablename__ = "doc_metadata"

    doc_id: Mapped[int] = Column(
        Integer,
        primary_key=True,
        autoincrement=False,
        nullable=False,
        comment="指向TFRobot框架中的Document的ID。完整路径为`doc.documents.doc_id`,`doc`是Schema,`documents`是表名",
    )
    meta: Mapped[dict] = Column("meta", JSONB)

Index(None, DocMetadata.meta, postgresql_using='gin')

因为在基类中已经使用了__table_args__属性,所以推荐使用方式二进行索引的定义。如果一定要使用方式一,需要在子类中使用__table_args__属性时,额外添加{"schema": "tfrserver"}schema字段。否则会导致子类的表没有schema属性,从而建表时错误地在default schema下创建表。

视图类

参考 Sqlalchemy官方创建View的文档 可以进行视图的定义。但需要注意,在通过这种方式定义完视图类后,select语句中查询参数的指定需要有所调整:

对于表结构,查询时可以直接使用类名配合.查询属性。举例如下:

select(User).where(User.name == 'ed')

但对于视图类结构,需要在视图类后添加一个.c来先获取Columns,举例如下:

select(UserView.c).where(UserView.c.name == 'ed')

数据库迁移

目前主要是通过ORM类的映射,配合Alembic进行迁移。但需要注意,如果使用数据库视图功能,Alembic不会自动迁移,需要手动维护视图的定义与结构。