我有一个相当复杂的类,具有从提供的 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