SQLAlchemy将多列映射到单个属性

时间:2019-01-28 23:32:42

标签: python sqlalchemy

我正在使用Flask&SQLAlchemy(通过Flask-SQLAlchemy;使用MySQL或SQLite)在Python 3中构建Web应用程序,并且遇到了要引用我的单个属性的情况在数据库中封装多个列的模型类。我精通MySQL,但这是我对SQLAlchemy的首次真正尝试,超越了基础知识。阅读文档,搜索SO并搜索Google,使我找到了两种可能的解决方案:混合属性(docs)或Composite列(docs)。

我的问题是使用每一种方法的含义是什么,哪种方法适合我的情况?我在下面提供了示例代码,这是我正在做的代码片段。


背景:我正在开发一个用于跟踪照片并对其进行排序的应用程序,并且具有一个DB表,在其中存储这些照片的元数据,包括拍摄照片的时间。由于照片是在特定的地方拍摄的,因此拍摄的日期和时间具有相关的时区。由于SQL与时区有着众所周知的爱/恨关系,因此我选择在两列中记录照片的拍摄时间:存储日期和时间的datetime和存储时区名称的字符串。 (请避开关于如何在SQL中存储时区感知日期和时间的不可避免的争论。)我想要的是模型类上的单个参数,我可以使用它来获取适当的python datetime对象,而且我还可以像其他任何列一样进行设置。

这是我的桌子:

$ docker run --rm -it ubuntu
root@7f6191875c62:/# whoami
root
root@7f6191875c62:/# id
uid=0(root) gid=0(root) groups=0(root)
root@7f6191875c62:/# id -u -n
root

以下是我使用混合参数的方法(添加到上述类中,datetime / pytz代码为psuedocode):

class Photo(db.Model):

    __tablename__ = 'photos'

    id = db.Column(db.Integer, primary_key=True)
    ...
    taken_dt = db.Column(db.datetime, nullable=False)
    taken_tz = db.Column(db.String(64), nullable=False)
    ...

我不确定从那里 @hybrid_parameter def taken(self): return datetime.datetime(self.taken_dt, self.taken_tz) @taken.setter(self, dt): self.taken_dt = dt self.taken_tz = dt.tzinfo @taken.expression还需要什么,或者为什么我会选择一个。

这是我使用复合列的内容(再次添加到上述类中,datetime / pytz代码为psuedocode):

@taken.comparator

该方法似乎有相当多的额外开销,而且我想不出一种方法来像直接从datetime.datetime对象中设置任何其他列一样进行设置,而无需先使用{实例化value对象{1}}。

任何有关我是否走错路的指导都将受到欢迎。谢谢!

1 个答案:

答案 0 :(得分:1)

TL; DR:研究将AttributeEvent连接到您的列,并检查设置了datetime属性的tz实例,然后返回DateTimeTimeZone宾语。如果查看SQLAlchemy docs for Attribute Events,您会发现可以告诉SQLAlchemy监听属性设置事件并在该事件上调用您的代码。在其中,您可以根据需要对设置的值进行任何修改。但是,您当时无法访问该类的其他属性。我还没有尝试将其与复合材料结合使用,所以我不知道在复合材料的类型转换之前还是之后将其称为。您必须尝试。

edit:它全都与您想要实现的目标有关。 AttributeEvent可以帮助您保持数据的一致性,而hybrid_property和朋友可以使您的查询更加轻松。您应该针对预期的用例使用每个。

关于各种解决方案之间差异的详细讨论:

hybrid_attributecomposite是两种完全不同的野兽。要了解hybrid_attribute,首先必须了解column_property是什么并且可以做什么。

1)column_property

此文件放置在一个映射器上,可以包含任何selectable。因此,如果您将具体的子选择放入column_property中,则可以将其只读作为具体的列访问。计算是即时进行的。您甚至可以使用它来搜索条目。 SQLAlchemy将为您构建包含您的子选择的正确选择。

示例:

class User(Base):
   id = Column(Integer, primary_key=True)
   first_name = Column(Unicode)
   last_name = Column(Unicode)

   name = column_property(first_name + ' ' + last_name)
   category = column_property(select([CategoryName.name])
                              .select_from(Category.__table__
                                           .join(CategoryName.__table__))
                              .where(Category.user_id == id))

db.query(User).filter(User.name == 'John Doe').all()

db.query(User).filter(User.category == 'Paid').all()

如您所见,这可以简化许多代码,但是必须谨慎考虑性能影响。

2)hybrid_method和hybrid_attribute

hybrid_attribute就像column_property一样,但是当您处于实例上下文中时,可以调用其他代码路径。因此,您可以在类级别使用selectable,而在实例级别使用其他实现。使用hybrid_method,您甚至都可以参数化两侧。

3)Composite_attribute

这是使您可以将多个具体列组合为一个逻辑上的单个列的功能。您必须为此逻辑列编写一个类,以便SQLAlchemy可以从那里提取正确的值并在选择中使用它。这可以整齐地集成在查询框架中,不应施加任何其他问题。以我的经验,复合列的用例很少见。您的用例似乎很好。要修改值,您可以始终使用AttributeEvents。如果要使整个实例可用,则必须在刷新前调用MapperEvent。当我用它来实现一个完全透明的 Audit Trail 跟踪系统时,它确实有效,该系统将每个表中更改的每个值存储在单独的表集中。