2010-04-04 9 views
10

Ich habe folgendes Problem:sqlalchemy dynamische Abbildung

Ich habe die Klasse:


class Word(object): 

    def __init__(self): 
     self.id = None 
     self.columns = {} 

    def __str__(self): 
     return "(%s, %s)" % (str(self.id), str(self.columns)) 

self.columns ist ein dict welche halten wird (column: Column) -Werten. Der Name der Spalten werden zur Laufzeit bekannt, und sie sind in einer wordColumns Liste geladen, zum Beispiel


wordColumns = ['english', 'korean', 'romanian'] 

wordTable = Table('word', metadata, 
        Column('id', Integer, primary_key = True) 
       ) 
for columnName in wordColumns: 
    wordTable.append_column(Column(columnName, String(255), nullable = False)) 

Ich habe sogar eine explizite Mapper Eigenschaften zu „zwingen“ die Tabellenspalten auf Wort abgebildet werden. Spalten [columnName], anstelle von word.columnName, bekomme ich keinen Fehler beim Mapping, aber es scheint, dass das nicht funktioniert.


mapperProperties = {} 
for column in wordColumns: 
    mapperProperties['columns[\'%']' % column] = wordTable.columns[column] 

mapper(Word, wordTable, mapperProperties) 

Wenn ich ein Wortobjekt laden, schafft SQLAlchemy ein Objekt, das die word.columns [ ‚Englisch‘], word.columns [Koreanischen '] usw. Eigenschaften, anstatt sie zu laden word.columns hat DIKT . Für jede Spalte wird eine neue Eigenschaft erstellt. Außerdem existiert das word.columns-Wörterbuch nicht einmal.

Auf die gleiche Weise erwartet SQLAlchemy, wenn ich versuche, ein Wort zu persistieren, die Spaltenwerte in Eigenschaften wie word.columns ['english'] (string type) anstelle des Wörterbuchs word.columns zu finden.

Ich muss sagen, dass meine Erfahrung mit Python und SQLAlchemy ziemlich begrenzt ist, vielleicht ist es nicht möglich zu tun, was ich versuche zu tun.

Jede Hilfe geschätzt,

Vielen Dank im Voraus.

Antwort

29

Es scheint, dass Sie einfach die Attribute direkt anstelle der columnsdict verwenden können.

Betrachten Sie die folgende Einstellung:

from sqlalchemy import Table, Column, Integer, Unicode, MetaData, create_engine 
from sqlalchemy.orm import mapper, create_session 

class Word(object): 
    pass 

wordColumns = ['english', 'korean', 'romanian'] 
e = create_engine('sqlite://') 
metadata = MetaData(bind=e) 

t = Table('words', metadata, Column('id', Integer, primary_key=True), 
    *(Column(wordCol, Unicode(255)) for wordCol in wordColumns)) 
metadata.create_all() 
mapper(Word, t) 
session = create_session(bind=e, autocommit=False, autoflush=True) 

Mit diesem leeren Klasse können Sie tun:

w = Word() 
w.english = u'name' 
w.korean = u'이름' 
w.romanian = u'nume' 

session.add(w) 
session.commit() 

Und wenn Sie die Daten zugreifen möchten:

w = session.query(Word).filter_by(english=u'name').one() 
print w.romanian 

Das ist die Ganze sqlalchemy 's ORM Punkt, anstatt zu verwenden aoder dict für den Zugriff auf die Daten verwenden Sie Attribut-ähnliche Zugriff auf Ihre eigene Klasse.

Also habe ich mich gefragt, warum Sie eine dict verwenden möchten. Vielleicht liegt es daran, dass Sie Strings mit den Sprachnamen haben. Nun, für die Sie verwenden Python könnte getattr und setattr stattdessen wie auf jedem Python-Objekt:

language = 'korean' 
print getattr(w, language) 

, dass alle Ihre Probleme lösen sollten.


Das heißt, wenn Sie noch dict -ähnlichen Zugriff auf die Spalten verwenden möchten, ist es auch möglich.Sie müssen nur ein dict ähnliches Objekt implementieren. Ich werde jetzt Code zur Verfügung stellen, um dies zu tun, obwohl ich denke, es ist absolut unnötig Krempel, da der Zugriff auf Attribute so sauber ist. Wenn Ihr Problem mit der oben genannten Methode bereits gelöst wurde, verwenden Sie den Code nicht unter diesem Punkt.

Sie konnte es auf der Word Klasse:

class Word(object): 
    def __getitem__(self, item): 
     return getattr(self, item) 
    def __setitem__(self, item, value): 
     return setattr(self, item, value) 

Der Rest der Einrichtung, wie oben arbeitet. Dann könnten Sie es wie folgt verwendet werden:

w = Word() 
w['english'] = u'name' 

Wenn Sie ein columns Attribut wollen, dann müssen Sie ein dict -ähnlichen

class AttributeDict(DictMixin): 
    def __init__(self, obj): 
     self._obj = obj 
    def __getitem__(self, item): 
     return getattr(self._obj, item) 
    def __setitem__(self, item, value): 
     return setattr(self._obj, item, value) 

class Word(object): 
    def __init__(self): 
     self.columns = AttributeDict(self) 

Dann könnten Sie verwenden, wie Sie gedacht:

w = Word() 
w.columns['english'] = u'name' 

Ich denke, Sie werden mir zustimmen, dass all das unnötig kompliziert ist und keinen zusätzlichen Nutzen bringt.

+2

Vielen Dank, dass Sie sich die Zeit genommen haben, eine solche vollständige Antwort zu schreiben. Ich stimme zu, dass die erste Lösung sauberer und eleganter ist als die zweite. Ich hatte keinen guten Grund, ein Diktat zu benutzen, außer der Tatsache, dass dies das "normale" Ding war, das mir in den Sinn kam. Wie ich schon sagte, ich bin ein wenig neu in Python und ich habe noch nicht gelernt, die Vorteile der dynamischen Sprachen zu nutzen. Ich werde deine erste Lösung verwenden. Nochmals vielen Dank :) –

+3

Ich kann nicht darüber hinwegkommen, wie viele Beiträge wie diese hier existieren, aber ich bin immer noch beeindruckt, wenn ich einen sehe. Danke, dass du so viel darüber nachgedacht hast. Immer noch hilfreich, für neue Leute, über ein Jahr später. Mit freundlichen Grüßen – Profane

+0

Nun, wenn ich Kwarts verwenden wollte, um Word zu initiieren, wie in w = Word (Englisch = 'Name', Koreanisch = 'Phoo', Rumänisch = 'Nume'), wie würde das funktionieren? In meinem Fall wird es weiterhin einen __init__ Fehler geben. Vielen Dank! – Julius

3

Ich habe die Lösung von Nosklo benutzt (Danke!), Aber ich hatte bereits einen Primärschlüssel (als pk_col übergeben) innerhalb der Spaltenzeile (erste Zeile von csv). Also dachte ich, ich würde meine Modifikation teilen. Ich habe ein Ternär verwendet.

table = Table(tablename, metadata, 
    *((Column(pk_col, Integer, primary_key=True)) if rowname == pk_col else (Column(rowname, String())) for rowname in row.keys())) 
table.create()