sqlalchemy模型的已定义列上迭代的方法?


95

我一直在尝试找出如何遍历SQLAlchemy模型中定义的列列表。我希望它为一些模型编写一些序列化和复制方法。我不能仅对其进行迭代,obj.__dict__因为它包含许多SA特定项。

有人知道一种从以下项中获取iddesc名称的方法吗?

class JobStatus(Base):
    __tablename__ = 'jobstatus'

    id = Column(Integer, primary_key=True)
    desc = Column(Unicode(20))

在这种情况下,我可以轻松创建一个:

def logme(self):
    return {'id': self.id, 'desc': self.desc}

但我更喜欢自动生成dict(对于较大的对象)的东西。

Answers:


83

您可以使用以下功能:

def __unicode__(self):
    return "[%s(%s)]" % (self.__class__.__name__, ', '.join('%s=%s' % (k, self.__dict__[k]) for k in sorted(self.__dict__) if '_sa_' != k[:4]))

它将排除SA 魔术属性,但不会排除关系。因此,基本上它可能会加载依赖项,父项,子项等,这绝对是不可取的。

但这实际上要容易得多,因为如果继承自Base,则具有__table__属性,因此您可以执行以下操作:

for c in JobStatus.__table__.columns:
    print c

for c in JobStatus.__table__.foreign_keys:
    print c

请参阅如何从SQLAlchemy映射的对象中发现表属性 -类似的问题。

迈克(Mike)编辑:请参见Mapper.cMapper.mapped_table之类的函数。如果使用0.8或更高版本,还请参见Mapper.attrs和相关函数。

Mapper.attrs的示例:

from sqlalchemy import inspect
mapper = inspect(JobStatus)
for column in mapper.attrs:
    print column.key

21
请注意,这__table__.columns将为您提供SQL字段名称,而不是您在ORM定义中使用的属性名称(如果两者不同)。
Josh Kelley 2010年

11
我可能建议更改'_sa_' != k[:4]not k.startswith('_sa_')
Mu Mind

11
无需与检查循环:inspect(JobStatus).columns.keys()
kirpit 2015年

63

您可以从映射器获取已定义属性的列表。对于您的情况,您仅对ColumnProperty对象感兴趣。

from sqlalchemy.orm import class_mapper
import sqlalchemy

def attribute_names(cls):
    return [prop.key for prop in class_mapper(cls).iterate_properties
        if isinstance(prop, sqlalchemy.orm.ColumnProperty)]

4
谢谢,这让我在Base上创建了一个todict方法,然后我将其用于将实例“转储”到dict中,然后我可以通过该方法获得pylon的jsonify装饰器响应。我试图在原始问题中用代码示例添加更多详细信息,但这导致stackoverflow在提交时出错。
里克(Rick)2010年

4
顺便说一句,class_mapper需要从sqlalchemy.orm
priestc

3
尽管这是一个合理的答案,但建议在0.8版之后使用inspect(),它返回与完全相同的mapper对象class_mapper()docs.sqlalchemy.org/en/latest/core/inspection.html
kirpit 2015年

1
这对我将SQLAlchemy模型属性名称映射到基础列名称很有帮助。
FearlessFuture

29

我意识到这是一个古老的问题,但是我遇到了相同的要求,并希望为未来的读者提供替代解决方案。

如Josh所述,完整的SQL字段名称将由返回JobStatus.__table__.columns,因此您将获得jobstatus.id而不是原始的字段名称 id。没有那么有用。

获取最初定义的字段名称列表的解决方案是_data在包含完整数据的列对象上查找属性。如果我们看一下JobStatus.__table__.columns._data,它看起来像这样:

{'desc': Column('desc', Unicode(length=20), table=<jobstatus>),
 'id': Column('id', Integer(), table=<jobstatus>, primary_key=True, nullable=False)}

从这里您可以简单地致电JobStatus.__table__.columns._data.keys()给您一个干净的清单:

['id', 'desc']

2
真好!这种方法是否也有解决关系的方法?
裹尸布

3
不需要_data attr,只需..columns.keys()就能解决问题
Humoyun Ahmad

1
是的,应该避免使用private _data属性,@ Humoyun更正确。
Ng Oon-Ee

AttributeError:__data

13

self.__table__.columns将“仅”为您提供在该特定类中定义的列,即没有继承的列。如果需要全部,请使用self.__mapper__.columns。在您的示例中,我可能会使用以下内容:

class JobStatus(Base):

    ...

    def __iter__(self):
        values = vars(self)
        for attr in self.__mapper__.columns.keys():
            if attr in values:
                yield attr, values[attr]

    def logme(self):
        return dict(self)


7

为了获得as_dict我所有班级的方法,我使用了一个Mixin使用Ants Aasma描述的技术的班级

class BaseMixin(object):                                                                                                                                                                             
    def as_dict(self):                                                                                                                                                                               
        result = {}                                                                                                                                                                                  
        for prop in class_mapper(self.__class__).iterate_properties:                                                                                                                                 
            if isinstance(prop, ColumnProperty):                                                                                                                                                     
                result[prop.key] = getattr(self, prop.key)                                                                                                                                           
        return result

然后在课堂上像这样使用它

class MyClass(BaseMixin, Base):
    pass

这样,您可以在的实例上调用以下内容MyClass

> myclass = MyClass()
> myclass.as_dict()

希望这可以帮助。


我对此进行了进一步的研究,实际上我需要将实例渲染为HAL对象dict的形式,并带有指向相关对象的链接。因此,我在这里添加了这个小技巧,它将覆盖与上述相同的类的所有属性,不同之处在于,我将更深入地搜索属性并自动生成这些属性。Relaionshiplinks

请注意,这仅适用于具有单个主键的关系

from sqlalchemy.orm import class_mapper, ColumnProperty
from functools import reduce


def deepgetattr(obj, attr):
    """Recurses through an attribute chain to get the ultimate value."""
    return reduce(getattr, attr.split('.'), obj)


class BaseMixin(object):
    def as_dict(self):
        IgnoreInstrumented = (
            InstrumentedList, InstrumentedDict, InstrumentedSet
        )
        result = {}
        for prop in class_mapper(self.__class__).iterate_properties:
            if isinstance(getattr(self, prop.key), IgnoreInstrumented):
                # All reverse relations are assigned to each related instances
                # we don't need to link these, so we skip
                continue
            if isinstance(prop, ColumnProperty):
                # Add simple property to the dictionary with its value
                result[prop.key] = getattr(self, prop.key)
            if isinstance(prop, RelationshipProperty):
                # Construct links relaions
                if 'links' not in result:
                    result['links'] = {}

                # Get value using nested class keys
                value = (
                    deepgetattr(
                        self, prop.key + "." + prop.mapper.primary_key[0].key
                    )
                )
                result['links'][prop.key] = {}
                result['links'][prop.key]['href'] = (
                    "/{}/{}".format(prop.key, value)
                )
        return result

请添加from sqlalchemy.orm import class_mapper, ColumnProperty您的代码段
JVK

谢谢你的评论!我添加了缺失的进口商品
flazzarini

这是SQLAlchemy的的声明中了解到更多关于这里docs.sqlalchemy.org/en/13/orm/extensions/declarative/...
flazzarini


-1

我知道这是一个古老的问题,但是:

class JobStatus(Base):

    ...

    def columns(self):
        return [col for col in dir(self) if isinstance(col, db.Column)]

然后,获取列名: jobStatus.columns()

那会回来 ['id', 'desc']

然后,您可以遍历,并对列和值进行处理:

for col in jobStatus.colums():
    doStuff(getattr(jobStatus, col))

您不能在字符串上执行isinstance(col,Column)
Muposat 16-10-13

拒绝投票,是因为table .columns和mapper .columns无需重新发明轮子即可为您提供此数据。
大卫·沃森
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.