首页 > 编程问答 >如何优雅地将复杂的Python对象和SQLAlchemy对象模型类结合起来?

如何优雅地将复杂的Python对象和SQLAlchemy对象模型类结合起来?

时间:2024-07-24 11:46:55浏览次数:14  
标签:python sqlalchemy flask-sqlalchemy

我有一个相当复杂的类,具有从提供的 df 到 init 计算的复杂属性,这些属性可能是最终可以序列化为字符串的其他类类型。在 Python 中,我想处理对象而不是原始类型,但也想使用 SQLAlchemy 与数据库交互。表中的列与许多类属性相同,如何优雅地组合这两个类?我可以使用组合并将数据库模型作为类中的对象,但这感觉不像真正的组合?我必须有两个单独的类来调用彼此的 API。

子类化不起作用,我不认为这是因为列属性不能与列具有相同的名称。当我使用 ORM 并从数据库中提取数据时,我希望该对象已经初始化,以便我可以修改 df 并更改属性,但不会使用 ORM 调用 init

from enum import Enum

import pandas as pd
from sqlalchemy import Base, Float, Text
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column


class Thing(Enum):
    A = "a"
    B = "b"


def thing_factory(df: pd.DataFrame) -> Thing:
    tmp = df["thing"].max()
    return Thing.A if tmp > 4 else Thing.B


class ComplexObject:
    def __init__(self, df: pd.DataFrame):
        self.df = df
        self.thing: Thing = thing_factory(self.df)

    @property
    def complex_property(self) -> float:
        # complex logic here
        return 0


class Base(DeclarativeBase):
    pass


class ComplexObjectModel(Base):
    __tablename__ = "complex_object"

    id: Mapped[int] = mapped_column(primary_key=True)
    thing: Mapped[str] = mapped_column(Text)
    complex_property: Mapped[float] = mapped_column(Float)

是对的,子类化和组合都不是将复杂 Python 对象与 SQLAlchemy 模型类优雅结合的理想解决方案。以下是一种更优雅的方法:

1. 使用混合方法:

  • 创建一个单独的类来处理复杂的逻辑和属性。 此类不应从 Base 继承,并且应仅包含与数据库表示形式无关的逻辑。
  • 创建一个 SQLAlchemy 模型类来表示数据库表。 此类将包含映射到数据库列的属性。
  • 在 SQLAlchemy 模型类中使用描述符来桥接复杂对象和模型属性之间的差距。

代码示例:

from enum import Enum

import pandas as pd
from sqlalchemy import Base, Float, Text, Column
from sqlalchemy.orm import declarative_base, Mapped, mapped_column

class Thing(Enum):
    A = "a"
    B = "b"


def thing_factory(df: pd.DataFrame) -> Thing:
    tmp = df["thing"].max()
    return Thing.A if tmp > 4 else Thing.B


class ComplexObject:
    def __init__(self, df: pd.DataFrame):
        self.df = df
        self.thing: Thing = thing_factory(self.df)

    @property
    def complex_property(self) -> float:
        # complex logic here
        return 0


class _ComplexObjectDescriptor:
    def __get__(self, instance, owner):
        if instance is None:
            return self
        if not hasattr(instance, '_complex_object'):
            instance._complex_object = ComplexObject(...) # 加载逻辑
        return instance._complex_object

    def __set__(self, instance, value):
        if not isinstance(value, ComplexObject):
            raise TypeError("Expected a ComplexObject instance.")
        instance._complex_object = value


class ComplexObjectModel(declarative_base()):
    __tablename__ = "complex_object"

    id: Mapped[int] = mapped_column(primary_key=True)
    _thing: Mapped[str] = mapped_column('thing', Text)
    _complex_property: Mapped[float] = mapped_column('complex_property', Float)

    complex_object = _ComplexObjectDescriptor()

    @property
    def thing(self):
        return Thing(self._thing)

    @thing.setter
    def thing(self, value):
        self._thing = value.value

    @property
    def complex_property(self):
        return self.complex_object.complex_property

    @complex_property.setter
    def complex_property(self, value):
        self.complex_object.complex_property = value

解释:

  • ComplexObject 类保持不变,包含所有复杂的逻辑和属性。
  • ComplexObjectModel 类现在有一个 complex_object 描述符。此描述符负责:
    • 惰性加载: 当第一次访问 complex_object 属性时,它会从数据库中加载数据并创建 ComplexObject 实例。
    • 数据同步: ComplexObjectModel 实例上的 thing complex_property 属性的访问将被路由到 complex_object 实例的相应属性。

优点:

  • 关注点分离: 业务逻辑和数据库表示形式是分开的。
  • 代码重用: 可以在需要时轻松地在其他地方重用 ComplexObject 类。
  • 透明度: ComplexObjectModel 属性的访问感觉像是直接访问 ComplexObject 实例,而无需关心数据库交互。

这种方法允许优雅地将复杂的 Python 对象与 SQLAlchemy 模型类结合起来,同时保持关注点分离并提供代码重用和透明度。

标签:python,sqlalchemy,flask-sqlalchemy
From: 78786311

相关文章

  • Python Match Case:检查未知长度的可迭代内部的类型
    我想使用匹配大小写检查一个未知长度的迭代(假设为list)仅包含给定类型(假设为float)(还有其他情况,只有这个给我带来了问题)。case[*elems]ifall([isinstance(elem,float)foreleminelems]):returnnum这个似乎可行,但确实很不Pythony。看来应该有更简单的方法。......
  • 在 Flask-SQLAlchemy 模型类中使用数据类装饰器?
    我编写了一个Flask-SQLAlchemy模型类,如下所示(来自此参考):fromflaskimportFlaskfromflask_sqlalchemyimportSQLAlchemyapp=Flask(__name__)db=SQLAlchemy(app)classUser(db.Model):__tablename__='user'user_id=db.Column(d......
  • Python实现excel数据的读取和写入
    1.安装说到前面的话,实现excel文件数据的读取和写入,在python中还有其它方法,比如说pandas。鉴于最近粉丝朋友问到上面的“xlrd”和“xlwt”,那么笔者下面将通过这两个方法,来实现excel文件数据的读取和写入。首先,我们先需要提前安装好对应的库。需要注意的是,xlrd从2.0版本开始,只......
  • python_进程与线程_多线程
    一、程序与进程的概念1、进程:指启动后的程序,系统会为进程分配内存空间二、创建进程的方式1、第一种创建进程的方式process(group=None,target,name,args,kwargs)group:表示分组,实际上不使用,默认为None即可target:表示子进程要执行的任务,支持函数名name:表示子进程的......
  • python_网络编程_socket
    一、网络编程的基本概念通信协议:internet协议,任何私有网络支持此协议,就可以接入互联网二、七层协议与四层协议从下到上分别是:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层三、掌握TCP、IP协议ip协议是整个TCP、IP协议族的核心IP地址就是会联网上计算......
  • 你能对 Python 类型注释进行模式匹配吗?
    你能在Python类型上进行模式匹配吗?我见过简单的例子:importbuiltinsmatchx:casebuiltins.str:print("matchedstr")casebuildins.int:print("matchedint")但我想在嵌套类型上进行模式匹配,比如Annotated[Optional[Literal["a",......
  • python Polars:替换嵌套列表的元素
    有谁知道是否可以有效地替换极坐标中嵌套列表的元素。例如:s=pl.Series('s',[[1,2,3],[3,4,5]])#replace3with10toget[[1,2,10],[10,4,5]]我已经尝试过s.to_frame().with_columns(pl.when(pl.col('s')==3)...)但是pl.when不喜欢List[bo......
  • Python 中的常量应该大写吗?
    在PEP8中,一般规则是在UPPER_CASE字符中声明常量。在现实生活中,可能有多种情况:#!envpythonDATABASE_HOST='localhost'app=Flask('myapp')base_two=partial(int,base=2)通常我们将字符串类型或数字类型变量视为不可变的,因此是常量,而不是对象或函数。问题是......
  • 多重处理会导致 Python 崩溃,并给出一个错误:调用 fork() 时可能已在另一个线程中进行
    我对Python比较陌生,并试图为我的for循环实现一个多处理模块。我在img_urls中存储了一个图像url数组,我需要下载并应用一些Google视觉。if__name__=='__main__':img_urls=[ALL_MY_Image_URLS]runAll(img_urls)print("---%sseconds---"%(......
  • Python编程时输入操作数错误
    我正在用Python编写下面的代码来模拟控制系统。但是,当我调试代码时,我面临以下问题:matmul:输入操作数1没有足够的维度(有0,gufunc核心,签名为(n?,k),(k,m?)->(n?,m?)需要1)文件“D:\ÁreadeTrabalho\GitHub\TCC\CódigosMarcela\SistemaSISO_tres_estados_new.py”,......