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(더 큰 개체의 경우) 자동 생성되는 것을 선호합니다 .

답변:


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

11
로 변경 '_sa_' != k[:4]하는 것이 좋습니다 not k.startswith('_sa_').
뮤 마음

11
검사를 반복 할 필요가 없습니다.inspect(JobStatus).columns.keys()
kirpit

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 메소드를 생성 한 다음 pylon의 jsonify 데코레이터 응답을 위해 전달할 수있는 dict에 인스턴스를 '덤프'하는 데 사용합니다. 원래 질문에 코드 예제와 함께 자세한 내용을 메모하려고했지만 제출시 스택 오버플로가 오류를 일으켰습니다.
Rick

4
btw, class_mapper가져와야합니다sqlalchemy.orm
priestc

3
이것은 합법적 인 대답이지만 버전 0.8 이후 inspect()에는 .NET과 똑같은 매퍼 객체를 반환하는를 사용하는 것이 좋습니다 class_mapper(). docs.sqlalchemy.org/en/latest/core/inspection.html
kirpit

1
이것은 SQLAlchemy 모델 속성 이름을 기본 열 이름에 매핑하는 데 많은 도움이되었습니다.
FearlessFuture

29

나는 이것이 오래된 질문이라는 것을 알고 있지만 동일한 요구 사항을 발견했으며 미래의 독자에게 대안 솔루션을 제공하고 싶습니다.

조쉬 노트로, 전체 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']

2
좋은! 이 방법으로 관계를 얻을 수있는 방법이 있습니까?
슈라우드

3
_data ATTR에 대한 필요가 없습니다, 단지 ..columns.keys는 () 트릭을 할
Humoyun 아마드

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)

10

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


이것은 간단한 대답입니다.
stgrmks

7

as_dict모든 클래스에 대한 메소드 를 얻기 위해 Ants Aasma가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

from sqlalchemy.orm import class_mapper, ColumnProperty코드 스 니펫 위에 추가 하십시오
JVK

귀하의 의견에 감사드립니다! 누락 된 수입품을 추가했습니다
flazzarini

sqlalchemy의 선언적 기반입니다. 자세한 내용은 여기 docs.sqlalchemy.org/en/13/orm/extensions/declarative/…
flazzarini

1
self.__dict__

키는 속성 이름이고 값은 객체의 값인 dict를 반환합니다.

/! \ 보조 속성 '_sa_instance_state'가 있지만 처리 할 수 ​​있습니다. :)


속성이 설정된 경우에만.
stgrmks

-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

테이블 .columns 및 매퍼 .columns는 바퀴를 다시 발명하지 않고도이 데이터를 제공 하기 때문에 반대 투표를 받았습니다 .
David Watson
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.