【问题标题】:SQLAlchemy: Knowing the field names and values of a model object?SQLAlchemy:知道模型对象的字段名称和值吗?
【发布时间】:2014-01-16 10:30:21
【问题描述】:

我正在努力坚持 SOLID 面向对象的编程原则,保持 DRY 等,但我对 Python/SQLAlchemy/Pyramid 的新认识让这变得非常困难。

我正在尝试将我现在所知道的 SQLAlchemy 模型用于创建一个简单的 Pyramid Framework 对象并使用我所知道的 C# 中的“反射”,它在 Python 中可能被称为不同的东西(内省?不确定,因为这只是我使用 python 的第二周,但我在其他语言(C/C++/C#、Java 等)方面有很多经验,所以问题似乎是将我的知识映射到 python 的词汇,抱歉),在我不知道列名或对象形状的任何东西的情况下,找出数据库表的字段名,最重要的是当前字段值。

没错;我不知道“derp”实例有一个名为 id 或 name 的字段,只是它在每个字段中都有列和一个值。这就是我所关心的。

目标是能够采用任何 SQLAlchemy 定义的数据模型,并将其转换为 column_name 的字典 -> column_value 字段的简单数据类型的 JSON 类型,因为我想最终序列化我在其中创建的任何对象SQLAlchemy 到一个 json 对象,但我会满足于一个字典,因为从那里它是微不足道的,只要字典包含正确的数据类型。手动为每个对象执行此操作违反了太多良好的干净代码规则,并且随着时间的推移会产生过多的工作;我可以再花一个星期的时间来做这件事,并且仍然可以通过正确的方式来节省时间和精力。

所以如果我在 SQLAlchemy 中定义了一个类:

class SimpleFooModel(Base):
    id = Column(Integer, primary_key=True, autoincrement=True, nullable=False)
    name = Column(VARCHAR(length=12), nullable=False, index=True)

.. 我有一个等于(在 python 中)的实例:

derp = SimpleFooModel(id=7, name="Foobar")

我希望能够只拥有上面描述的“derp”实例变量,而不知道模型是如何形成的,并且能够将它展平为那个简单对象的 python 键->值字典,其中该字典中的每个值都可以使用 import json from python syslib 序列化为 JSON。

问题是,我已经看了 2 天了,但我无法找到一个答案,让我在任何地方的单元测试中都能得到我想要的结果;谷歌一直把我带到这里关于 SO 的关于真正旧版本的库的非常老的帖子,这些版本要么使用不再适用的接口,要么接受了根本不起作用的答案;并且由于它们都不是最近的,这确实让我感到惊讶(但是为什么 Stack Overflow 在它们出错时会保留它们并允许谷歌误导人们确实让我感到惊讶)

我知道我可以为每个对象手动将每个对象连接到 json 等,但这不仅不优雅,而且效率低下,因为它只会在我创建更多对象时为我创造更多工作,并且可能会导致未来出现大错误.我想知道如何以正确的方式通过自省/反思来做到这一点,但似乎没有人知道,而且那些声称在这里都给出了堆栈溢出示例的人实际上根本不起作用(至少在当前事物的版本)

这对我来说似乎是一个非常常见的用例;并获取列字段列表,然后使用 getattr 对其进行迭代——就像许多答案所说的那样——也不能按预期工作;它只是创建看起来像命名空间的东西,从不返回列的实际值,并且实际上不存在于任何代码中,因为 sqlalchmy 创建的字段都不是单例/静态的。

所以:

    from sqlalchemy.inspection import inspect

    obj = inspect(derp, raiseerr=True)

    for key in obj.attrs.keys():
        fields[key] = getattr(derp, key)
        print fields[key]

只给我:

    [Class Name].[Column Name]

.. 或者在这种情况下只是给我:

    SimpleFooModel.id
    SimpleFooModel.name

不是 7 和“Foobar”的值分别代表 id 和 name,这是我在测试中实际预期的。

事实上,我什至找不到值存储在对象模型中的位置;或者我可以蛮力解决这个问题,并将它们从那里作为一个丑陋的、邪恶的黑客来看待,我会感到羞耻。我通过“官方公共 api”得到的只是很多对象,它们似乎不知道真实数据存储在哪里,但会很高兴地告诉我列名和类型使用的路径名称、限制等... 只是不是我想要的实际数据。

然而,由于我的要求是我事先不知道字段名称,因此不能使用对 derp.id 或 derp.name 的调用来收集值,因为这会违反 SOLID 并迫使我重复工作每一堂课。所以它不是一个选择。

也许这是我两天没睡觉的事实,但我真的很难不将其视为这些库中的严重设计缺陷;我只想将代表表中单行的 SQLAlchemy 定义的 Model 对象序列化到 python 字典中,而不必事先知道字段的名称,虽然许多其他语言使这变得简单甚至微不足道,但这似乎还很遥远太难了。

有人可以解释一个可行的解决方案,或者为什么我想将 SOLID 应用于我的代码是错误的吗?

编辑:更新拼写。

【问题讨论】:

    标签: python json sqlalchemy pyramid solid-principles


    【解决方案1】:

    使用以下类扩展您的模型:

    class BaseModel(object):
    
        @classmethod
        def _get_keys(cls):
            return sa.orm.class_mapper(cls).c.keys()
    
        def get_dict(self):
            d = {}
            for k in self._get_keys():
                d[k] = getattr(self, k)
            return d
    

    这将完全满足您的要求,以 {'column_name':'value'} 对的形式返回一个字典。

    【讨论】:

    • 即使我没有使用代码,我也会将其设置为答案;这里最大的问题是 'cls' 和 'self' 之间的区别,这两个部分都是 python 的一部分,我显然在过去一周左右的时间里还没有深入研究它。如果我使用实例方法而不是类方法,我 拥有 的代码会起作用,正如您在使用 @classmethod 装饰器用于上面的类公共 get_keys 时指出的那样。在另一个 SO 线程中,对 PEP8 的引用表明我有更多的余地,但显然使用也是确定性的,因此当涉及到装饰器时,情况并非总是如此。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-24
    • 2011-07-18
    相关资源
    最近更新 更多