programing

sqalchemy 모델의 정의된 열에 대한 반복 방법?

newstyles 2023. 10. 1. 19:19

sqalchemy 모델의 정의된 열에 대한 반복 방법?

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(큰 개체의 경우).

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

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

SQLchemy 매핑된 개체에서 테이블 속성을 검색하는 방법 - 유사한 질문을 참조하십시오.

Mike 편집 : Mapper.c, Mapper.map_table 등의 기능을 참조해 주세요.0.8 이상을 사용하는 경우 Mapper.attrs 및 관련 기능도 참조하십시오.

Mapper.attters의 예:

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

저는 이것이 오래된 질문이라는 것을 깨달았지만, 저는 방금 같은 요구사항을 발견했고 미래의 독자들에게 대안적인 해결책을 제시하고 싶습니다.

처럼, 이름은 Josh 처럼, 됩니다 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.keys()좋은 깨끗한 목록을 얻을 수 있습니다.

['id', 'desc']

하면 SQLLchemy 의를 할 수 __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

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내가 사용한 모든 수업의 방법.MixinAnts 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()

도움이 되길 바랍니다.


이것을 가지고 좀 더 게임을 해봤습니다. 사실 저는 제 사례를 다음과 같이 표현해야 했습니다.dictHAL 객체의 형태와 연관된 객체와의 링크.그래서 저는 여기에 이 작은 마법을 더했습니다. 위와 같은 클래스의 모든 속성을 기어오를 것입니다. 더 깊이 기어 들어가는 차이점이 있습니다.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
self.__dict__

키가 속성 이름이고 개체의 값을 나타내는 dict를 반환합니다.

/!\ 보조 속성 '_sa_instance_state'가 있지만 사용자가 처리할 수 있습니다.

하는 동안에row._asdict()대부분의 경우에 효과가 있었고, 객체 생성 과정 후에도 효과가 있는 접근 방식이 필요했습니다(db.session.add등). 방법을 만들어 보자는 취지입니다.to_dicte 사용getattr.

class Inventory(db.Model):
    __tablename__ = 'inventory'

    id = db.Column('id', db.Integer(), primary_key=True)
    date = db.Column('date', db.DateTime, nullable=False, default=datetime.utcnow)
    item = db.Column('item', db.String(100))

    def to_dict(self):
        return {
            column.name: getattr(self, column.name, None)
            for column in Inventory.__table__.columns
        }



record = Inventory(item="gloves")
db.session.add(record)
db.session.commit()

# print(record._asdict()) # << that doesn't work
print(record.to_dict()) # << that works as intended

이 솔루션은 열만 있는 dict를 생성합니다. 메타 특성이나 다음 주 업데이트 후 수동으로 치료해야 하는 모든 것은 없습니다.

추신. 플라스크-sqalchemy를 사용하지만 아이디어가 바뀌지는 않습니다.

모델의 특정 인스턴스의 데이터를 동적으로 얻고 싶습니다.저는 이 코드를 사용했습니다.

def to_json(instance):
    # get columns data
    data = {}
    columns = list(instance.__table__.columns)
    for column in columns:
        data[column.name] = instance.__dict__[column.name]
    return data

sqalchemy에서 json으로 모델을 매핑하려면 관계를 고려하여 이 코드를 사용합니다.

from sqlalchemy.orm import class_mapper
from sqlalchemy.ext.declarative import DeclarativeMeta
from sqlalchemy.orm import ColumnProperty
from sqlalchemy.orm import RelationshipProperty


class BaseMixin(object):
    """BaseMixin"""

    __repr_hide = ["created_at", "updated_at"]
    __insert_hide = []

    @property
    def _repr_hide(self):
        return self.__repr_hide

    @_repr_hide.setter
    def _repr_hide(self, k):
        self.__repr_hide.append(k)

    @property
    def _insert_hide(self):
        return self.__insert_hide

    @_insert_hide.setter
    def _insert_hide(self, k):
        self.__insert_hide.append(k)

    def serialize(self, obj):
        """serialize from json"""
        for k, v in obj.items():
            if k in self.__repr_hide:
                continue
            if k in self.__insert_hide:
                continue
            if k in self.__table__.c.keys():
                setattr(self, k, v)
        return self

    def deserialize(self, backref=None):
        """deserialize to json"""
        res = dict()

        for prop in class_mapper(self.__class__).iterate_properties:
            if prop.key in self.__repr_hide:
                continue
            if isinstance(prop, ColumnProperty):
                res[prop.key] = getattr(self, prop.key)

        for prop in class_mapper(self.__class__).iterate_properties:
            if prop.key in self.__repr_hide:
                continue
            if isinstance(prop, RelationshipProperty):
                if prop.key == str(backref):
                    continue
                key, value = prop.key, getattr(self, prop.key)
                if value is None:
                    res[key] = None
                elif isinstance(value.__class__, DeclarativeMeta):
                    res[key] = value.deserialize(backref=self.__table__)
                else:
                    res[key] = [i.deserialize(backref=self.__table__) for i in value]
        return res

    def __iter__(self):
        return iter(self.deserialize().items())

    def __repr__(self):
        vals = ", ".join(
            "%s=%r" % (n, getattr(self, n))
            for n in self.__table__.c.keys()
            if n not in self._repr_hide
        )

        return "<%s={%s}>" % (self.__class__.__name__, vals)

이것이 오래된 질문이라는 것을 알지만, 그럼 다음은 어떻습니까?

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

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