sqlalchemy मॉडल के परिभाषित कॉलम पर पुनरावृति की विधि?


95

मैं यह पता लगाने की कोशिश कर रहा हूं कि 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(बड़ी वस्तुओं के लिए) उत्पन्न करता है ।

जवाबों:


84

आप निम्नलिखित फ़ंक्शन का उपयोग कर सकते हैं:

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

यह एसए जादू विशेषताओं को बाहर करेगा , लेकिन संबंधों को बाहर नहीं करेगा। तो मूल रूप से यह निर्भरता, माता-पिता, बच्चों आदि को लोड कर सकता है, जो निश्चित रूप से वांछनीय नहीं है।

लेकिन यह वास्तव में बहुत आसान है क्योंकि अगर आपको विरासत में मिला है 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

21
ध्यान दें कि __table__.columnsआपको SQL फ़ील्ड नाम देगा, न कि विशेषता नाम जो आपने अपने ORM परिभाषाओं में उपयोग किया है (यदि दोनों भिन्न हैं)।
जोश केली

11
मैं बदलने की सलाह देते हो सकता है '_sa_' != k[:4]करने के लिए not k.startswith('_sa_')?
मु माइंड

12
निरीक्षण के साथ लूप की आवश्यकता नहीं है: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
धन्यवाद, इसने मुझे एक आधार पर एक कठिन विधि बनाने की अनुमति दी जिसका उपयोग मैं एक ऐसे उदाहरण के रूप में करता हूं, जिसे मैं एक तानाशाह की प्रतिक्रिया के लिए पारित कर सकता हूं। मैंने अपने मूल प्रश्न में कोड उदाहरण के साथ अधिक विवरण नोट करने की कोशिश की, लेकिन यह स्टैकओवरफ़्लो को प्रस्तुत करने में त्रुटि का कारण बन रहा है।
रिक

4
btw, class_mapperसे आयात करने की जरूरत हैsqlalchemy.orm
priestc

3
हालांकि यह एक वैध जवाब है, संस्करण 0.8 के बाद इसका उपयोग करने का सुझाव दिया गया है inspect(), जो ठीक उसी मैपर ऑब्जेक्ट के रूप में देता है class_mapper()docs.sqlalchemy.org/en/latest/core/inspection.html
kirpit

1
इससे SQLAlchemy मॉडल प्रॉपर्टी नामों को अंतर्निहित कॉलम नामों में मैप करने में मुझे बहुत मदद मिली।
फियरलेस सिवनी

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
_दाता अत्र की कोई आवश्यकता नहीं है, बस .. कोल्युमेसकी () चाल
चलें

1
हां, जहां संभव हो, निजी _data विशेषता का उपयोग करने से बचना चाहिए, @Humoyun अधिक सही है।
एनजी ओन-ई

गुण: __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


यह सरल उत्तर है।
स्ट्रीम्क्स

7

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जैसे कि एचएएल ऑब्जेक्ट का रूप इसके साथ संबंधित वस्तुओं के लिंक के साथ है। इसलिए मैंने इस छोटे से जादू को यहां जोड़ दिया है, जो ऊपर के समान वर्ग के सभी गुणों पर क्रॉल करेगा, इस अंतर के साथ कि मैं 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__

एक प्रमुख रिटर्न देता है जहाँ कुंजियाँ विशेषता के नाम हैं और ऑब्जेक्ट के मूल्यों को महत्व देते हैं।

/ / \ एक पूरक विशेषता है: '_sa_instance_state' लेकिन आप इसे संभाल सकते हैं :)


केवल जब विशेषताएँ सेट की जाती हैं।
स्ट्रीम्क्स

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

आप एक स्ट्रिंग पर आइंस्टीन (कॉल, कॉलम) नहीं कर सकते हैं
Muposat

डाउनवोट किया गया है क्योंकि टेबल। पूर्व छात्र और मैपर .columns आपको पहिया का पुन: आविष्कार किए बिना यह डेटा देते हैं।
डेविड वॉटसन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.