programing

sqlalchemy 모델의 정의 된 열을 반복하는 방법?

nasanasas 2020. 9. 5. 10:01
반응형

sqlalchemy 모델의 정의 된 열을 반복하는 방법?


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


다음 기능을 사용할 수 있습니다.

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

매퍼에서 정의 된 속성 목록을 가져올 수 있습니다. 귀하의 경우에는 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, 그래서보다는 원래 필드 이름 아이디 , 당신은 얻을 것이다 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)

To get an as_dict method on all of my classes I used a Mixin class which uses the technics described by 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

And then use it like this in your classes

class MyClass(BaseMixin, Base):
    pass

That way you can invoke the following on an instance of MyClass.

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

Hope this helps.


I've played arround with this a bit further, I actually needed to render my instances as dict as the form of a HAL object with it's links to related objects. So I've added this little magic down here, which will crawl over all properties of the class same as the above, with the difference that I will crawl deeper into Relaionship properties and generate links for these automatically.

Please note that this will only work for relationships have a single primary key

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

Assuming you're using SQLAlchemy's declarative mapping, you can use __mapper__ attribute to get at the class mapper. To get all mapped attributes (including relationships):

obj.__mapper__.attrs.keys()

If you want strictly column names, use obj.__mapper__.column_attrs.keys(). See the documentation for other views.

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


self.__dict__

returns a dict where keys are attribute names and values the values of the object.

/!\ there is a supplementary attribute: '_sa_instance_state' but you can handle it :)


I know this is an old question, but what about:

class JobStatus(Base):

    ...

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

Then, to get column names: jobStatus.columns()

That would return ['id', 'desc']

Then you can loop through, and do stuff with the columns and values:

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

참고URL : https://stackoverflow.com/questions/2537471/method-of-iterating-over-sqlalchemy-models-defined-columns

반응형