Я пытаюсь использовать SQLAlchemy версию для хранения моих объектов в базе данных. У меня есть save(...)
функция для этой цели:
#!/usr/bin/env python
# encoding: utf-8
from sqlalchemy import Column, Integer, MetaData, String, Table, create_engine
from sqlalchemy.orm import mapper, sessionmaker
class MyClass(object):
def __init__(self, title):
self.title = title
def __str__(self):
return '%s' % (self.title)
def save(object_list):
metadata = MetaData()
my_class_table = Table('my_class',
metadata,
Column('id', Integer, primary_key=True),
Column('title', String(255), nullable=False))
# everything is OK here, output:
# some title
# another title
# yet another title
for obj in object_list:
print obj
mapper(MyClass, my_class_table)
# on Linux / SQLAlchemy 0.6.8, this fails with
# Traceback (most recent call last):
# File "./test.py", line 64, in <module>
# save(my_objects)
# File "./test.py", line 57, in save
# print obj
# File "./test.py", line 11, in __str__
# return '%s' % (self.title)
# File "/usr/lib/python2.7/dist-packages/sqlalchemy/orm/attributes.py", line 167, in __get__
# return self.impl.get(instance_state(instance),
# AttributeError: 'NoneType' object has no attribute 'get'
# on Mac OSX / SQLAlchemy 0.7.5, this fails with
# Traceback (most recent call last):
# File "./test.py", line 64, in <module>
# save(my_objects)
# File "./test.py", line 57, in save
# print obj
# File "./test.py", line 11, in __str__
# return '%s' % (self.title)
# File "/Library/Python/2.7/site-packages/sqlalchemy/orm/attributes.py", line 165, in __get__
# if self._supports_population and self.key in dict_:
# File "/Library/Python/2.7/site-packages/sqlalchemy/orm/attributes.py", line 139, in __getattr__
# key)
# AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object has an attribute '_supports_population'
for obj in object_list:
print obj
# (more code to set up engine and session...)
if __name__ == '__main__':
my_objects = [MyClass('some title'), MyClass('another title'), MyClass('yet another title')]
save(my_objects)
Мне кажется, что картограф делает что-то с моими объектами неявно, когда я создаю картограф, и я больше не могу их использовать. Из того, что я прочитал в похожих вопросах , это не совсем неизвестно.
Ожидается ли такое поведение?
Я что-то не так понимаю здесь?
Как правильно отображать и хранить мои объекты?
Дополнительная информация: Я использую SQLAlchemy 0.7.5 с системным Python 2.7.1 по умолчанию на Mac OSX 10.7.3 Lion и SQLAlchemy 0.6.8 с системным Python 2.7.2+ по умолчанию на виртуальной машине Kubuntu 11.10.
Обновление:
Похоже, что маппер SQLAlchemy хорошо известен для изменения объектов на «потребности SQLAlchemy». Решение в связанной статье заключается в создании объектов после вызова mapper(...)
.
У меня уже есть допустимые объекты - но я больше не могу их использовать ...
Как мне получить SQLAlchemy для хранения моих объектов?
Обновление 2:
У меня складывается впечатление, что я неправильно понимаю что-то фундаментальное в ОРМ:
Я думал, что концепция сопоставления SQLAlchemy дает мне возможность определять свои объекты и работать с ними в моем приложении, отделенном от любой базы данных - и только когда я хочу сохранить их, я ввожу SQLAlchemy и заставляю его выполнять всю тяжелую работу по отображению Объект класса для таблицы базы данных. Кроме того, я не вижу архитектурной причины, по которой SQLAlchemy следует упоминать где-либо еще в моем коде, кроме функций сохранения save(...)
и load(...)
.
Однако, глядя на первый ответ ниже, я понимаю, что должен сопоставить класс с таблицей базы данных, прежде чем использовать какие-либо объекты; это было бы в самом начале моей программы. Может быть, я что-то здесь не так, но это кажется довольно резким ограничением дизайна со стороны SQLAlchemy - все слабые связи исчезли. Имея это в виду, я также не вижу преимуществ в том, чтобы иметь в первую очередь маппер, поскольку «поздняя связь», которую я хочу, технически невозможна с SQLAlchemy, и я мог бы просто сделать этот «декларативный стиль» и смешать и смешайте бизнес-логику с кодом постоянства: - (
Обновление 3:
Я вернулся к исходной точке и снова перечитал SQLAlchemy. Это написано прямо на главной странице :
SQLAlchemy наиболее известен своим объектно-реляционным отображением (ORM), необязательным компонентом, который предоставляет шаблон отображения данных, где классы могут быть сопоставлены с базой данных открытым способом несколькими способами - что позволяет объектной модели и схеме базы данных с самого начала развиваться чисто отделенным образом.
Однако, исходя из моего опыта, SQLAlchemy, похоже, совсем не выполняет этого обещания, поскольку вынуждает меня соединять объектную модель и схему базы данных с самого начала. После всего, что я слышал о SQLAlchemy, мне действительно трудно поверить, что это действительно так, и я бы предпочел предположить, что я еще не понял чего-то базового.
Что я делаю не так?
Обновление 4:
Для полноты картины я хотел бы добавить рабочий пример кода, который выполняет все сопоставления перед созданием каких-либо бизнес-объектов:
#!/usr/bin/env python
# encoding: utf-8
from sqlalchemy import Column, Integer, MetaData, String, Table, create_engine
from sqlalchemy.orm import mapper, sessionmaker
class MyClass(object):
def __init__(self, title):
self.title = title
def __str__(self):
return '%s' % (self.title)
def save(object_list, metadata):
engine = create_engine('sqlite:///:memory:')
metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
for obj in object_list:
try:
session.add(obj)
session.commit()
except Exception as e:
print e
session.rollback()
# query objects to test they're in there
all_objs = session.query(MyClass).all()
for obj in all_objs:
print 'object id :', obj.id
print 'object title:', obj.title
if __name__ == '__main__':
metadata = MetaData()
my_class_table = Table('my_class',
metadata,
Column('id', Integer, primary_key=True),
Column('title', String(255), nullable=False))
mapper(MyClass, my_class_table)
my_objects = [MyClass('some title'), MyClass('another title'), MyClass('yet another title')]
save(my_objects, metadata)
В своем собственном (не примерном) коде я импортирую MyClass
из его собственного модуля и выполняю сопоставление в отдельном классе «хранилище объектов», который создает MetaData
в своем методе __init__(...)
и сохраняет его в член, поэтому методы постоянства save(...)
и load(...)
могут обращаться к нему по мере необходимости. Все, что требуется от основного приложения, - это создать объект репозитория в самом начале; он содержит влияние SQLAlchemy на проектирование для одного класса, а также распределяет определения бизнес-объектов и сопоставленных таблиц по разным местам в коде. Еще не уверен, пойду ли я с таким долгим сроком, но на данный момент это работает.
Последний быстрый совет для новичков SQLAlchemy, таких как я: вы должны работать с одним и тем же объектом метаданных повсюду, в противном случае вы получите исключения, такие как no such table
или class not mapped
.