динамическое отображение sqlalchemy - PullRequest
12 голосов
/ 04 апреля 2010

У меня следующая проблема:

У меня есть класс:


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 - это диктовка, которая будет содержать значения (columnName: columnValue). Имя столбцов известно во время выполнения и загружается в список wordColumns, например


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

Я даже создал явные свойства сопоставления, чтобы «принудительно» отображать столбцы таблицы на word.columns [columnName], вместо word.columnName я не получаю никакой ошибки при сопоставлении, но кажется, что т работа.


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

mapper(Word, wordTable, mapperProperties)

Когда я загружаю объект word, SQLAlchemy создает объект, обладающий свойствами word.columns ['english'], word.columns ['korean'] и т. Д., Вместо загрузки их в dict word.columns. Таким образом, для каждого столбца создается новое свойство. Более того, словарь word.columns даже не существует.

Точно так же, когда я пытаюсь сохранить слово, SQLAlchemy ожидает найти значения столбца в свойствах, названных как word.columns ['english'] (строковый тип) вместо словаря word.columns.

Я должен сказать, что мой опыт работы с Python и SQLAlchemy довольно ограничен, возможно, я не могу сделать то, что я пытаюсь сделать.

Любая помощь приветствуется,

Спасибо заранее.

Ответы [ 2 ]

32 голосов
/ 04 апреля 2010

Кажется, что вы можете просто использовать атрибуты напрямую вместо использования columns dict.

Рассмотрим следующую настройку:

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)

С этим пустым классом вы можете сделать:

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

session.add(w)
session.commit()

А когда вы хотите получить доступ к данным:

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

Это точка целого sqlalchemy ORM , вместо использования tuple или dict для доступа к данным, вы используете атрибутный доступ к вашему собственный класс.

Так что мне было интересно, по каким причинам вы хотели бы использовать dict. Возможно, это потому, что у вас есть строки с названиями языков. Ну, для этого вы можете использовать вместо getattr и setattr в Python, как и для любого объекта Python:

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

Это должно решить все ваши проблемы.


Тем не менее, если вы все еще хотите использовать dict -подобный доступ к столбцам, это также возможно. Вам просто нужно реализовать dict -подобный объект. Теперь я предоставлю код для этого, хотя я думаю, что это абсолютно ненужный беспорядок, поскольку доступ к атрибутам настолько чистый. Если ваша проблема уже решена с помощью метода, описанного выше, не используйте код ниже этой точки.

Вы можете сделать это в Word классе:

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

Остальная часть настройки работает, как указано выше. Тогда вы можете использовать это так:

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

Если вам нужен атрибут columns, тогда вам нужен dict -подобный

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)

Тогда вы можете использовать, как и предполагали:

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

Думаю, вы согласитесь с тем, что все это излишне сложно без каких-либо дополнительных преимуществ.

3 голосов
/ 06 февраля 2012

Я использовал решение nosklo (спасибо!), Но у меня уже был первичный ключ (переданный как pk_col) в строке столбца (первая строка csv).Поэтому я решил поделиться своей модификацией.Я использовал троичный.

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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...