sqlalchemy modelinin tanımlı sütunları üzerinde yineleme yöntemi?


98

Bir SQLAlchemy modelinde tanımlanan sütunların listesini nasıl yineleyeceğimi anlamaya çalışıyorum. Birkaç modele bazı serileştirme ve kopyalama yöntemleri yazmak için istiyorum. obj.__dict__Çok sayıda SA'ya özgü öğe içerdiği için bunu yineleyemiyorum .

Aşağıdakilerden idve descisimlerini almanın bir yolunu bilen var mı?

class JobStatus(Base):
    __tablename__ = 'jobstatus'

    id = Column(Integer, primary_key=True)
    desc = Column(Unicode(20))

Bu küçük durumda kolaylıkla bir oluşturabilirim:

def logme(self):
    return {'id': self.id, 'desc': self.desc}

ancak dict(daha büyük nesneler için) otomatik olarak üreten bir şey tercih ederim .

Yanıtlar:


85

Aşağıdaki işlevi kullanabilirsiniz:

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 sihirli özniteliklerini dışlayacak , ancak ilişkileri dışlamayacaktır. Yani temelde bağımlılıkları, ebeveynleri, çocukları vs. yükleyebilir ki bu kesinlikle arzu edilmeyen bir durumdur.

Ama aslında çok daha kolay çünkü miras Basealırsanız, bir __table__niteliğiniz vardır, böylece şunları yapabilirsiniz:

for c in JobStatus.__table__.columns:
    print c

for c in JobStatus.__table__.foreign_keys:
    print c

Bkz SQLAlchemy eşlenmiş nesneden tablo özelliklerini keşfetmek için nasıl benzer bir soru -.

Mike tarafından düzenleme: Lütfen Mapper.c ve Mapper.mapped_table gibi işlevlere bakın . 0.8 ve üzeri kullanılıyorsa, Mapper.attrs ve ilgili işlevlere de bakın .

Mapper.attrs için örnek :

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

21
Not __table__.columnssize SQL alan adlarını değil, (iki farklı ise) size ORM tanımlarında kullandım Özellik adlarının verecektir.
Josh Kelley

11
Olarak değiştirmenizi tavsiye '_sa_' != k[:4]edebilir not k.startswith('_sa_')miyim?
Mu Mind

13
inspect(JobStatus).columns.keys()
İncelemeye

model.__dict__[column]sqlalchemy ile değerler ayarlarsanız değişiklikleri algılamaz.
Sebi2020

63

Eşleştiriciden tanımlanan özelliklerin listesini alabilirsiniz. Durumunuz için yalnızca ColumnProperty nesneleriyle ilgileniyorsunuz.

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
Teşekkürler, bu , Base'de, daha sonra pilonun jsonify dekoratör yanıtı için geçebileceğim bir dikteye bir örneği 'dökmek' için kullandığım bir todict yöntemi oluşturmama izin verdi . Orijinal soruma örnek kodla daha fazla ayrıntı notu koymaya çalıştım, ancak bu, gönderim sırasında stackoverflow'un hataya neden olmasına neden oluyor.
Rick

4
btw,class_mappersqlalchemy.orm
priestc

3
Bu geçerli bir cevap olsa da, 0.8 sürümünden sonra, ile inspect()aynı eşleştirici nesnesini döndüren kullanılması önerilir class_mapper(). docs.sqlalchemy.org/en/latest/core/inspection.html
kirpit

1
Bu, SQLAlchemy model özellik adlarını temeldeki sütun adlarıyla eşlememe çok yardımcı oldu.
FearlessFuture

30

Bunun eski bir soru olduğunun farkındayım, ancak aynı gereksinimle karşılaştım ve gelecekteki okuyuculara alternatif bir çözüm sunmak istiyorum.

Josh notları tarafından iade edilecektir tam SQL alan adları olarak JobStatus.__table__.columns, bu yüzden ziyade orijinal alan adı id , alacağınız jobstatus.id . Olabildiğince yararlı değil.

Başlangıçta tanımlandıkları şekliyle alan adlarının bir listesini elde etmenin çözümü _data, tüm verileri içeren sütun nesnesindeki özniteliğe bakmaktır . Bakarsak JobStatus.__table__.columns._data, şöyle görünür:

{'desc': Column('desc', Unicode(length=20), table=<jobstatus>),
 'id': Column('id', Integer(), table=<jobstatus>, primary_key=True, nullable=False)}

Buradan kolayca arayarak JobStatus.__table__.columns._data.keys()size güzel ve temiz bir liste verir:

['id', 'desc']

2
Güzel! Bu yöntemle ilişkilere de girmenin bir yolu var mı?
kefen

3
_data attr'a gerek yoktur, sadece ..columns.keys () hile yapın
Humoyun Ahmad

1
Evet, özel _data özniteliğini kullanmaktan mümkün olduğunca kaçınılmalıdır, @Humoyun daha doğrudur.
Ng Oon-Ee

AttributeError: __data

13

self.__table__.columns"sadece" size o sınıfta tanımlanan sütunları verecektir, yani miras alınmayanlar. hepsine ihtiyacınız varsa kullanın self.__mapper__.columns. Örneğinizde muhtemelen şöyle bir şey kullanırdım:

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)

13

SQLAlchemy'nin bildirim temelli eşlemesini kullandığınızı varsayarsak __mapper__, sınıf eşleyicisine ulaşmak için özniteliği kullanabilirsiniz . Tüm eşlenen öznitelikleri (ilişkiler dahil) almak için:

obj.__mapper__.attrs.keys()

Kesinlikle sütun adları istiyorsanız, kullanın obj.__mapper__.column_attrs.keys(). Diğer görünümler için belgelere bakın.

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


Bu basit cevap.
stgrmks

7

as_dictTüm sınıflarımda bir yöntem elde etmek için Mixin, Ants Aasma tarafından açıklanan teknikleri kullanan bir sınıf kullandım .

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

Ve sonra bunu sınıflarınızda böyle kullanın

class MyClass(BaseMixin, Base):
    pass

Bu şekilde aşağıdakileri bir örneğinde çağırabilirsiniz MyClass.

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

Bu yardımcı olur umarım.


Bununla ilgili biraz daha fazla oynadım, aslında örneklerimi ilgili nesnelere bağlantıları dictolan bir HAL nesnesi biçiminde oluşturmam gerekiyordu. Bu yüzden buraya bu küçük sihri ekledim, sınıfın tüm özellikleri üzerinde yukarıdakilerle aynı şekilde gezinecek, aradaki fark, Relaionshipözelliklerin daha derinlerine inecek ve linksbunlar için otomatik olarak üreteceğim .

Lütfen bunun yalnızca tek bir birincil anahtara sahip ilişkiler için işe yarayacağını unutmayın

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

Lütfen from sqlalchemy.orm import class_mapper, ColumnPropertykod pasajınızın üstüne ekleyin
JVK

Yorumun için teşekkürler! Eksik ithalatı ekledim
flazzarini

Bu, sqlalchemy'nin beyan edici Üssüdür, buradan daha fazlasını okuyun docs.sqlalchemy.org/en/13/orm/extensions/declarative/…
flazzarini

1
self.__dict__

Anahtarların öznitelik adları olduğu ve nesnenin değerlerine değer verdiği bir kural döndürür.

/! \ tamamlayıcı bir özellik var: '_sa_instance_state' ama bununla başa çıkabilirsiniz :)


Yalnızca öznitelikler ayarlandığında.
stgrmks

0

Modelin belirli bir örneğinin verilerini dinamik olarak almak istiyorum. Bu kodu kullandım.

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

-1

Bunun eski bir soru olduğunu biliyorum ama peki ya:

class JobStatus(Base):

    ...

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

Ardından, sütun adlarını almak için: jobStatus.columns()

Geri dönecekti ['id', 'desc']

Ardından, sütunlarla ve değerlerle ilgili işlemler yapabilirsiniz:

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

bir dizede isinstance (sütun, Sütun)
yapamazsınız

Tablo .columns ve mapper .columns , tekerleği yeniden icat etmeden bu verileri size verdiği için olumsuz oy verildi .
David Watson
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.