метод итерации по определенным столбцам модели sqlalchemy?


Я пытался выяснить, как перебирать список столбцов, определенных в модели SQLAlchemy. Я хочу его для написания некоторых методов сериализации и копирования в пару моделей. Я не могу просто перебирать obj.__dict__ так как он содержит много SA конкретных элементов.

кто-нибудь знает способ, чтобы просто получить id и desc имена из следующих?

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 (для больших объектов).

Спасибо за помощь.

7 73

7 ответов:

вы можете использовать следующие функции:

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, у вас есть , так что вы можете сделать:

for c in JobStatus.__table__.columns:
    print c

for c in JobStatus.__table__.foreign_keys:
    print c

посмотреть как открыть свойства таблицы из SQLAlchemy сопоставленный объект - аналогичный вопрос.

Edit by Mike: пожалуйста, смотрите такие функции, как маппер.c и маппер.mapped_table. Если через 0,8 и выше, также посмотреть маппер.attrs и связанные с этим функции.

пример маппер.attrs:

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

вы можете получить список определенных свойств от маппера. Для вашего случая вас интересуют только объекты 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)]

Я понимаю, что это старый вопрос, но я только что столкнулся с тем же требованием и хотел бы предложить альтернативное решение для будущих читателей.

как отмечает Джош, полные имена полей SQL будут возвращены JobStatus.__table__.columns, Так что вместо исходного имени Поля id, вы получите jobstatus.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']

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)

для получения as_dict метод на всех моих классах я использовал Mixin класс, который использует технику, описанную Муравьи 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()

надеюсь, что это помогает.


я играл с этим немного дальше, мне действительно нужно было сделать мои экземпляры как dict как форма а HAL object С его ссылками на связанные объекты. Поэтому я добавил сюда эту маленькую магию, которая будет ползать по всем свойствам класса так же, как и выше, с той разницей, что я буду ползти глубже в Relaionship свойства и генерировать links для них автоматически.

обратите внимание, что это будет работать только для отношения имеют один первичный ключ

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

предполагая, что вы используете декларативное отображение SQLAlchemy, вы можете использовать __mapper__ атрибут, чтобы получить на класс mapper. Чтобы получить все сопоставленные атрибуты (включая отношения):

obj.__mapper__.attrs.keys()

Если вы хотите строго имена столбцов, используйте obj.__mapper__.column_attrs.keys(). Другие представления см. В документации.

https://docs.sqlalchemy.org/en/latest/orm/mapping_api.html#sqlalchemy.orm.mapper.Mapper.attrs

Я знаю, что это старый вопрос, но как насчет:

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))