Latest posts on [sqlalchemy] - удаление объектов topichttps://python.su/forum/topic/2840/2008-10-17T08:38:18+03:00Общий :: Базы данных :: [sqlalchemy] - удаление объектов
2008-10-17T08:38:18+03:00demas18287<blockquote><em>bw</em><br/>Сложно сказать почему так получается, может ты не весь код показал? Если не добавляешь объект к сессии, то он не должен insert'иться.</blockquote>Да, действительно - в чем причина пока не нашел, в выходные посмотрю. Но попробовал подготовить тестовый пример и проблема не повторилась:<br/><br/><div class="code"><pre>#! /usr/bin/python<br/># -*- coding: utf-8 -*-<br/><br/>import sqlalchemy<br/>from sqlalchemy import create_engine<br/>from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey<br/>from sqlalchemy.orm import mapper<br/>from sqlalchemy.orm import sessionmaker<br/>from sqlalchemy import ForeignKey<br/>from sqlalchemy.orm import relation<br/><br/>class User(object):<br/> def __init__(self, name, fullname):<br/> self.name = name<br/> self.fullname = fullname<br/><br/>engine = create_engine('sqlite:///tutorial.db', echo = True)<br/>Session = sessionmaker(bind = engine, autoflush=True, transactional = True)<br/>metadata = MetaData()<br/><br/>users_table = Table('users', metadata,<br/> Column('name', String(40), primary_key = True),<br/> Column('fullname', String(100)))<br/><br/>metadata.create_all(engine)<br/>mapper(User, users_table)<br/>session = Session()<br/><br/>print "=========== Insert =============="<br/><br/>ed_user = User('ed', 'Ed Jones')<br/>session.save(ed_user)<br/>session.commit()<br/><br/>print "=========== Select =============="<br/>new_user = session.query(User).filter_by(name='ed').first()<br/>print new_user.name<br/><br/>print "=========== Strange Delete ============="<br/>session = Session()<br/>newUser = User('ed', 'Ed Jones')<br/>session.query(User).filter_by(name = newUser.name).delete()<br/>session.flush()<br/>session.commit()</pre></div>
Общий :: Базы данных :: [sqlalchemy] - удаление объектов
2008-10-17T05:39:18+03:00PooH18283<blockquote><em>demas</em><br/>Да потому, что код написан так, что к моменту удаления объекта у меня его просто нет. Есть элемент управления заполненный данными и все. Код конечно можно переписать, просто хочу сначала с этим моментом разобраться.</blockquote>Если я правильно разобрался в ситуации вам достаточно хранить ID объекта в своем дереве, этого будет достаточно для удаления из базы без всяких выкрутасов.
Общий :: Базы данных :: [sqlalchemy] - удаление объектов
2008-10-16T17:37:07+03:00bw18267Ты можешь не делать flush перед commit, он выполнится автоматически.<br/>Сложно сказать почему так получается, может ты не весь код показал? Если не добавляешь объект к сессии, то он не должен insert'иться.<br/><br/>..bw
Общий :: Базы данных :: [sqlalchemy] - удаление объектов
2008-10-16T15:36:37+03:00demas18260Кстати, еще маленький вопрос по теме - если кто-то объяснит - буду благодарен:<br/><br/><div class="code"><pre> (model, iter) = self.treeview.get_selection().get_selected()<br/> session = Session()<br/> name = self.treestore.get_value(iter, 0)<br/> session.query(Product).filter_by(name = name).delete()<br/> session.flush()<br/> session.commit()</pre></div><blockquote>2008-10-16 16:27:10,558 INFO sqlalchemy.engine.base.Engine.0x…6d2c <br/>2008-10-16 16:27:10,559 INFO sqlalchemy.engine.base.Engine.0x…6d2c DELETE FROM product WHERE product.name = ?<br/>2008-10-16 16:27:10,562 INFO sqlalchemy.engine.base.Engine.0x…6d2c <br/>2008-10-16 16:27:10,563 INFO sqlalchemy.engine.base.Engine.0x…6d2c COMMIT</blockquote>Все удалилось нормально.<br/><br/>А теперь чуть-чуть правим код:<br/><br/><div class="code"><pre> (model, iter) = self.treeview.get_selection().get_selected()<br/> session = Session()<br/><br/> #name = self.treestore.get_value(iter, 0)<br/> product= Product(self.treestore.get_value(iter, 0), <br/> self.treestore.get_value(iter, 1), <br/> self.treestore.get_value(iter, 2), <br/> self.treestore.get_value(iter, 3), <br/> self.treestore.get_value(iter, 4))<br/> name = product.name <br/> session.query(Product).filter_by(name = name).delete()<br/> session.flush()<br/> session.commit()</pre></div><blockquote>2008-10-16 16:30:49,795 INFO sqlalchemy.engine.base.Engine.0x…3d2c <br/>2008-10-16 16:30:49,796 INFO sqlalchemy.engine.base.Engine.0x…3d2c DELETE FROM product WHERE product.name = ?<br/>2008-10-16 16:30:49,796 INFO sqlalchemy.engine.base.Engine.0x…3d2c <br/>2008-10-16 16:30:49,799 INFO sqlalchemy.engine.base.Engine.0x…3d2c INSERT INTO product (name, category, fat, protein, carbo) VALUES (?, ?, ?, ?, ?)<br/>2008-10-16 16:30:49,800 INFO sqlalchemy.engine.base.Engine.0x…3d2c <br/>2008-10-16 16:30:49,801 INFO sqlalchemy.engine.base.Engine.0x…3d2c COMMIT</blockquote>Откуда то взялся INSERT. Откуда? <br/>Я всего лишь создал объект. Я не добавлял его в session.
Общий :: Базы данных :: [sqlalchemy] - удаление объектов
2008-10-16T15:17:10+03:00demas18258Поставил 1.5 - последний способ заработал.<br/>Спасибо за терпение :)
Общий :: Базы данных :: [sqlalchemy] - удаление объектов
2008-10-16T14:37:36+03:00bw18256> AttributeError: ‘Query’ object has no attribute ‘delete’<br/>Я гворил что он появлился недавно, проверь версию SQLAlchemy.<br/>Думаю что именно этот метод, последний, и будет оптимальным в твоем случае. Такой delete не будет загружать объект (опять, насколько мне известно, если использовать расширения в мапере, возможно, он все же будет загружаться).<br/><br/>..bw
Общий :: Базы данных :: [sqlalchemy] - удаление объектов
2008-10-16T13:06:14+03:00demas18252<div class="code"><pre> def delete_product(self, widget):<br/> print '=== delete ==='<br/> session = Session()<br/> session.query(Product).filter_by(name = 'two').delete()<br/> session.flush()</pre></div><blockquote>AttributeError: ‘Query’ object has no attribute ‘delete’</blockquote>
Общий :: Базы данных :: [sqlalchemy] - удаление объектов
2008-10-16T13:02:35+03:00demas18250<div class="code"><pre> def get_selected_product(self):<br/> (model, iter) = self.treeview.get_selection().get_selected()<br/> return Product(self.treestore.get_value(iter, 0), <br/> self.treestore.get_value(iter, 1), <br/> self.treestore.get_value(iter, 2), <br/> self.treestore.get_value(iter, 3), <br/> self.treestore.get_value(iter, 4))<br/><br/> def delete_product(self, widget):<br/> print '=== delete ==='<br/> session = Session()<br/> del_obj = self.get_selected_product()<br/> print del_obj.name<br/> session.save(del_obj)<br/> print 'a'<br/> session.flush()<br/> session.delete(del_obj)<br/> print 'b'<br/> session.commit()<br/> session.flush()</pre></div><blockquote>=== delete ===<br/>two<br/>a<br/>2008-10-16 14:00:12,832 INFO sqlalchemy.engine.base.Engine.0x..cc INSERT INTO product (name, category, fat, protein, carbo) VALUES (?, ?, ?, ?, ?)<br/>2008-10-16 14:00:12,833 INFO sqlalchemy.engine.base.Engine.0x..cc <br/>2008-10-16 14:00:12,834 INFO sqlalchemy.engine.base.Engine.0x..cc ROLLBACK<br/><br/>….<br/><br/>File “/var/lib/python-support/python2.5/sqlalchemy/engine/base.py”, line 942, in _handle_dbapi_exception<br/> raise exceptions.DBAPIError.instance(statement, parameters, e, connection_invalidated=is_disconnect)<br/>sqlalchemy.exceptions.IntegrityError: (IntegrityError) column name is not unique u'INSERT INTO product (name, category, fat, protein, carbo) VALUES (?, ?, ?, ?, ?)' </blockquote>Я не могу сделать save() так как у меня уже есть такая запись в БД.
Общий :: Базы данных :: [sqlalchemy] - удаление объектов
2008-10-16T12:17:45+03:00bw18246<blockquote><em>demas</em><br/>Почему 2? Поле где “two” это вообще первичный ключ.</blockquote><div class="code"><pre>(*1*) print id<br/> orig = session.query(Product).filter(Product.name==id).first()<br/>(*2*) print orig.name</pre></div><blockquote><em>demas</em><br/>Здесь просто считываются поля из интерфейса и порождается объект.</blockquote>Но он не является у тебя (в таком состоянии, содя по коду) элементом базы и, как минимум, не подвязан к сесси. Вот после такого кода, он будет находится в базе и сесси и ты сможешь его удалить (код относится к SQLAlchemy 0.5, в ранних версиях небыло метода <strong>add</strong>, он заменил собой <strong>save</strong>):<br/><div class="code"><pre>s = Session()<br/>s.add(product)<br/>s.flush() # и/или s.commit(), если используешь транзакции</pre></div>Всё, без создания новой сессии и без запроса product в базе, ты можешь обращаться к product:<br/><div class="code"><pre>s.delete(product)<br/>s.flush() # и/или s.commit(), если используешь транзакции<br/>s.close()</pre></div>p.s. Опять же, точно не скажу, но кажется нет необходимости делать первый flush, что бы удалить объект. Если, конечно ты не хочешь что бы объект всё же отразился в БД.<br/><br/>..bw
Общий :: Базы данных :: [sqlalchemy] - удаление объектов
2008-10-16T12:13:00+03:00j2a18245<blockquote><em>demas</em><br/><blockquote><em>j2a</em><br/>Но ты можешь сделать что хочешь <a href="http://www.sqlalchemy.org/docs/05/sqlexpression.html#sql_delete">средствами SQL expression language</a>.</blockquote>А на сколько это корректно смешивать эти два подхода? Просто мне кажется, что в крупных проектах это не должно поощряться.<br/><br/><blockquote><em>j2a</em><br/>P.S. А в чем сермяжная правда? Какой смысл делать “копию” объекта для удаления из БД? Почему не воспользоваться самим объектом?</blockquote>Да потому, что код написан так, что к моменту удаления объекта у меня его просто нет. Есть элемент управления заполненный данными и все. Код конечно можно переписать, просто хочу сначала с этим моментом разобраться.</blockquote>А ты не смешивай. Допиши метод к маппер-классу, который бы делал нужное.
Общий :: Базы данных :: [sqlalchemy] - удаление объектов
2008-10-16T10:49:33+03:00demas18242<blockquote><em>j2a</em><br/>Но ты можешь сделать что хочешь <a href="http://www.sqlalchemy.org/docs/05/sqlexpression.html#sql_delete">средствами SQL expression language</a>.</blockquote>А на сколько это корректно смешивать эти два подхода? Просто мне кажется, что в крупных проектах это не должно поощряться.<br/><br/><blockquote><em>j2a</em><br/>P.S. А в чем сермяжная правда? Какой смысл делать “копию” объекта для удаления из БД? Почему не воспользоваться самим объектом?</blockquote>Да потому, что код написан так, что к моменту удаления объекта у меня его просто нет. Есть элемент управления заполненный данными и все. Код конечно можно переписать, просто хочу сначала с этим моментом разобраться.
Общий :: Базы данных :: [sqlalchemy] - удаление объектов
2008-10-16T10:46:25+03:00demas18241<blockquote><em>bw</em><br/>Не знаю почему.</blockquote>Ок, буду разбираться дальше.<br/><br/><blockquote><em>bw</em><br/>У тебя как минимум должно быть две строки “two”, если судить поприведенному коду.</blockquote>Почему 2? Поле где “two” это вообще первичный ключ.<br/><br/><blockquote><em>bw</em><br/>Ты уверен, что после создания объекта ты делаешь flush/commit (flush, кстати, идет перед commit, после commit он теряет свой смысл, а commit используется, только если у тебя транзакция, похоже так оно и есть, иначе СУБД выдают ошибку, что транзакция не была начата)? <br/>Я вот про это “и порождаю новый экземпляр объекта Product (просто у меня уже был такой метод)”, в приведенном коде ты ничего не порождаешь.</blockquote><div class="code"><pre> def get_selected_product(self):<br/> (model, iter) = self.treeview.get_selection().get_selected()<br/> return Product(self.treestore.get_value(iter, 0), <br/> self.treestore.get_value(iter, 1), <br/> self.treestore.get_value(iter, 2), <br/> self.treestore.get_value(iter, 3), <br/> self.treestore.get_value(iter, 4))</pre></div>Здесь просто считываются поля из интерфейса и порождается объект.<br/><br/><blockquote><em>bw</em><br/>p.s. И если ты при создании указываешь те же атрибуты атрибуты, что и у сушествующего, ты таким образом не ссылаешься на существующий, ты просто создаешь идентичный (при попытке сохранения такого объекта у тебя возникнут ожиданные проблемы с уникальными ключами).</blockquote>Угу, понял. Спасибо.
Общий :: Базы данных :: [sqlalchemy] - удаление объектов
2008-10-15T17:09:36+03:00j2a18219<blockquote><em>demas</em><br/><blockquote><em>j2a</em><br/>Человек, ты во втором примере создаешь новый объект (new_product = bla-bla) с такими же данными, как у старого. И новый несохраненный объект пытаешься удалить.</blockquote>Ага. Именно это я и пытаюсь сделать :) Я просто удивлен, что здесь возникла проблема.<br/><br/>Ведь в моем новом объекте есть вся необходимая информация, чтобы удалить запись из базы. Теоретически sqlAlchemy может по маппингу определить какие поля в таблице ключевые и сгенерить delete from table where field = object.property. Но она так и не делает.<br/><br/>Отсюда и вопрос - если у меня нет объекта считанного из базы, но есть вся информация, необходимая для того чтобы создать его точную копию (все поля объектов совадают) я все равно должен вычитывать объект из базы перед его удалением. Так?</blockquote>Если в рамках ORM, то нужно чтобы связь объект<->запись БД была установлена. Если объект в состоянии pending, он не связан ни с какой записью в БД. Но ты можешь сделать что хочешь <a href="http://www.sqlalchemy.org/docs/05/sqlexpression.html#sql_delete">средствами SQL expression language</a>.<br/><br/>P.S. А в чем сермяжная правда? Какой смысл делать “копию” объекта для удаления из БД? Почему не воспользоваться самим объектом?
Общий :: Базы данных :: [sqlalchemy] - удаление объектов
2008-10-15T17:07:32+03:00bw18218Не знаю почему.<br/>У тебя как минимум должно быть две строки “two”, если судить поприведенному коду. Вывод - это выхлоп другого кода.<br/>Ты уверен, что после создания объекта ты делаешь flush/commit (flush, кстати, идет перед commit, после commit он теряет свой смысл, а commit используется, только если у тебя транзакция, похоже так оно и есть, иначе СУБД выдают ошибку, что транзакция не была начата)? Я вот про это “и порождаю новый экземпляр объекта Product (просто у меня уже был такой метод)”, в приведенном коде ты ничего не порождаешь.<br/><br/>> Ага. Именно это я и пытаюсь сделать :) Я просто удивлен, что здесь возникла проблема.<br/>А я удивлен, что ты удивлен. Раньше, при использовании ORM, объект обязательно нужно было грузить, сейчас можно сделать:<br/><div class="code"><pre>session = Session()<br/>session.query(Product).filter_by(id = 1).delete()<br/>session.flush() # session.commit()</pre></div>(Кажется в таком случае объект не будет грузиться. Лень в документацию лезть, точно не скажу.)<br/><br/>Еще раз по первому посту: Ты не можешь сделать session.delete() для объекта, если объект не “подключен” к сессии. Он должен быть загружен из базы в этой сессии, либо появиться в ней каким-то другим способом (их несколько).<br/><br/>p.s. И если ты при создании указываешь те же атрибуты атрибуты, что и у сушествующего, ты таким образом не ссылаешься на существующий, ты просто создаешь идентичный (при попытке сохранения такого объекта у тебя возникнут ожиданные проблемы с уникальными ключами).<br/><br/>..bw
Общий :: Базы данных :: [sqlalchemy] - удаление объектов
2008-10-15T16:53:41+03:00demas18217Все равно у меня не выходит…<br/><br/><div class="code"><pre> session = Session()<br/> id = self.get_selected_product().name<br/> print id<br/> orig = session.query(Product).filter(Product.name==id).first()<br/> print orig.name<br/> session.delete(orig)<br/> session.commit()<br/> session.flush()</pre></div>Здесь:<br/><br/>* в self.get_selected_product() я считываю с интерфейса поля и порождаю новый экземпляр объекта Product (просто у меня уже был такой метод).<br/>* print id я убеждаюсь, что у меня есть правильный id<br/>* orig = session.query(Product).filter(Product.name==id).first() - вычитываю объект из базы<br/>* delete() пытаюсь его удалить<br/><br/>Не удаляется !!! При этом в лог пишет:<br/><br/>two<br/>2008-10-15 17:51:07,927 INFO sqlalchemy.engine.base.Engine.0x..0c UPDATE product SET name=?, category=?, fat=?, protein=?, carbo=? WHERE product.name = ?<br/>2008-10-15 17:51:07,928 INFO sqlalchemy.engine.base.Engine.0x..0c <br/>2008-10-15 17:51:07,930 INFO sqlalchemy.engine.base.Engine.0x..0c COMMIT<br/><br/>Почему update ???