Можно ли использовать именованный кортеж с SQLalchemy? - PullRequest
6 голосов
/ 03 октября 2011

Я пытался заставить именованный кортеж работать с SQLalchemy, но безрезультатно. Веб-поиск не очень привлекателен, и я новичок в Python и SQLalchemy, поэтому я не совсем уверен, погоня за ветряными мельницами :( Основная идея в том, что у меня есть именованный кортеж, то есть:

Point=namedtuple('Point',['x','y'])

, который в основном создает класс Point (кортеж), если я прав. Сначала это работает нормально, и я могу создавать такие объекты, как:

p=Point(3,4)

Но после того, как я создаю движок и т. Д. И вызываю маппер, я не могу больше создавать объекты без этой ошибки:

Traceback (most recent call last):
  File "<pyshell#62>", line 1, in <module>
    f=Point(3,4)
TypeError: __init__() takes exactly 1 argument (3 given)

Есть идеи, почему это происходит? Кто-нибудь знает, как заставить namedtuple работать с sqlalchemy? Конечно, я могу определить свой собственный класс Point, но сейчас я одержим желанием заставить namedtuple работать ... Я использую Python 2.7, SQLalchemy 0.6.6 (движок sqlite)

Пример:

Я пытаюсь что-то вроде этого:

from sqlalchemy import *
from sqlalchemy.orm import *
from collections import namedtuple

Point=namedtuple('Point',['x','y'],verbose=True)
p=Point(3,4)


db=create_engine('sqlite:///pointtest.db')
metadata=MetaData()
pointxy=Table('pointxy',metadata,
              Column('no',Integer,primary_key=True),
              Column('x',Integer),
              Column('y',Integer),
              sqlite_autoincrement=True)
metadata.create_all(db)
m=mapper(Point, pointxy)
Session=sessionmaker(bind=db)
session=Session()
f=Point(3,4)

Основная идея заключается в том, что я хочу именованную коллекцию вещей, которую можно легко сохранить в базе данных. Итак, это:

class Bunch:
    __init__ = lambda self, **kw: setattr(self, '__dict__', kw)

не будет работать с sqlalchemy (я думаю). Я могу создать класс Bunch, но я не буду заранее знать, сколько целых я хочу сохранить в своей коллекции. Я установлю его перед созданием базы данных. Я надеюсь, что я понимаю ..

Ответы [ 2 ]

3 голосов
/ 11 июля 2012

Именованные кортежи имеют ряд вариантов поведения, которые делают их неподходящими для отображения с помощью sqlalchemy: Самое главное, что именованные кортежи нельзя изменить после их создания.Это означает, что вы не можете использовать именованный кортеж для отслеживания состояния строки в базе данных после, скажем, операции вставки.Как правило, вы хотите сделать что-то вроде этого:

class MyDataHolder(namedtuple('MyDataHolder', ('id', 'my_value')):
    pass

mapper(MyDataHolder, MyDataMeta)

...

newRow = MyDataHolder(None, 'AAA')

...

session.add(newRow)

Когда код сеанса выполняет SQL для добавления новых данных в базу данных, он захочет обновить newRow , чтобы newRow.id соответствует идентификатору, который база данных присвоила вашей строке.Но поскольку newRow является неизменным кортежем, идентификатор нельзя изменить, чтобы он содержал первичный ключ, возвращенный из базы данных.Это делает namedtuples в значительной степени непригодным для картографов.

Проблемы __init__ возникают из-за того, что namedtuple инициализируется в __new__, а затем не ожидается его изменения.__init __ () вызывается после создания объекта и поэтому не имеет никакого эффекта.Таким образом, __init__ для именованного кортежа определяет только один аргумент: self.Я предполагаю, что Mapper предполагает, что __init __ () обрабатывает инициализацию класса и не знает о __new__ и неизменяемых типах.Похоже, они вызывают classname .__ init __ () с аргументами, которые передаются во время создания.Вы можете «исправить» это, указав свой собственный инициализатор: __init __ (self, * args), но тогда вы в конечном итоге столкнетесь с проблемой слабых ссылок.

Ошибка слабых ссылок происходит потому, что namedtuples используют __slots__ для хранения своих значений, а не изменяемых__dict__s.Я знаю, что использование __slots__ - это оптимизация памяти, так что вы можете эффективно хранить множество именованных кортежей.Я предполагаю, что ожидается, что именованный кортеж не изменится после его создания.Это включает в себя добавление атрибутов, поэтому использование __slots__ является полезной оптимизацией памяти.Однако я не претендую на то, что понимаю, почему автор класса namedtuple не поддерживает слабые ссылки.Это не было бы особенно сложно, но, возможно, есть действительно веская причина, по которой я скучаю.

Я столкнулся с этой проблемой сегодня утром и обошел ее, определив свой собственный класс для отображения данных, который был инициализированс атрибутом _fieldset.Это указывает (под) набор полей, которые мне интересно видеть сопоставленными.Затем у меня было некоторое время и я прочитал документацию о том, как именованные кортежи реализованы вместе с кучей других внутренних компонентов Python.Я думаю, я понимаю, почему это не работает, но я уверен, что есть эксперты по Python, которые лучше понимают это, чем я.

- Крис

1 голос
/ 03 октября 2011

Кажется, что маппер добавляет _ init _method. Таким образом, сделав следующее после оператора mapper, он снова заработает:

del Point.__init__

Я не уверен, что использование картографов для такого типа вещей - правильная идея. Для правильной работы преобразователю, скорее всего, понадобится первичный ключ («нет»), для которого в вашем именном корте в настоящее время нет места.

...