Подтвердить что ты не робот

Метод итерации по 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 (для более крупных объектов).

4b9b3361

Ответ 1

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

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 - аналогичный вопрос.

Редактировать Майком: Смотрите функции, такие как Mapper.c и Mapper.mapped_table. При использовании 0,8 и выше также см. Mapper.attrs и связанные функции.

Пример для Mapper.attrs:

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

Ответ 2

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

Ответ 3

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

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

Ответ 4

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)

Ответ 5

Чтобы получить метод as_dict во всех моих классах, я использовал класс Mixin, который использует технику, описанную Антсом Аасмой.

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

Ответ 6

Предполагая, что вы используете декларативное отображение SQLAlchemy, вы можете использовать атрибут __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

Ответ 7

self.__dict__

возвращает указание, где ключи являются именами атрибутов и значениями объекта.

/!\есть дополнительный атрибут: '_sa_instance_state' но ты справишься :)

Ответ 8

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

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