views.py
def add_post(topic, request):
post_form = PostForm(request.POST)
if 'submit' in request.POST and post_form.validate():
post = Post(body=post_form.body.data)
post.user = request.user
post.topic = topic
DBSession.add(post)
request.session.flash(_('Post was added'))
transaction.commit()
raise HTTPFound(location=request.route_url('topic',id=topic.id))
return {'post_form':post_form}
models.py
class Topic(Base):
__tablename__ = 'topics'
id = Column(Integer, primary_key=True)
...
post_count = Column(Integer, default=0)
posts = relationship('Post', primaryjoin="Post.topic_id==Topic.id", backref='topic', lazy='dynamic')
class Post(Base):
__tablename__ = 'posts'
id = Column(Integer, primary_key=True)
...
topic_id = Column(Integer, ForeignKey('topics.id'))
def post_inserted(mapper, conn, post):
topic = post.topic
topic.post_count = topic.posts.count()
event.listen(Post, "after_insert", post_inserted)
我想在我的Pyramid应用程序中使用SQLAchemy事件'after_insert'来更新主题模型,其中包含属于它的帖子数量。但我得到了解释:
Traceback (most recent call last):
File "/home/user/workspace/myforum/env/lib/python2.6/site-packages/waitress/channel.py", line 329, in service
task.service()
File "/home/user/workspace/myforum/env/lib/python2.6/site-packages/waitress/task.py", line 173, in service
self.execute()
File "/home/user/workspace/myforum/env/lib/python2.6/site-packages/waitress/task.py", line 380, in execute
app_iter = self.channel.server.application(env, start_response)
File "/home/user/workspace/myforum/env/lib/python2.6/site-packages/pyramid/router.py", line 251, in __call__
response = self.invoke_subrequest(request, use_tweens=True)
File "/home/user/workspace/myforum/env/lib/python2.6/site-packages/pyramid/router.py", line 227, in invoke_subrequest
response = handle_request(request)
File "/home/user/workspace/myforum/env/lib/python2.6/site-packages/pyramid_tm/__init__.py", line 107, in tm_tween
return response
File "/home/user/workspace/myforum/env/lib/python2.6/site-packages/transaction/_manager.py", line 116, in __exit__
self.commit()
File "/home/user/workspace/myforum/env/lib/python2.6/site-packages/transaction/_manager.py", line 107, in commit
return self.get().commit()
File "/home/user/workspace/myforum/env/lib/python2.6/site-packages/transaction/_transaction.py", line 354, in commit
reraise(t, v, tb)
File "/home/user/workspace/myforum/env/lib/python2.6/site-packages/transaction/_transaction.py", line 345, in commit
self._commitResources()
File "/home/user/workspace/myforum/env/lib/python2.6/site-packages/transaction/_transaction.py", line 493, in _commitResources
reraise(t, v, tb)
File "/home/user/workspace/myforum/env/lib/python2.6/site-packages/transaction/_transaction.py", line 465, in _commitResources
rm.tpc_begin(self)
File "/home/user/workspace/myforum/env/lib/python2.6/site-packages/zope/sqlalchemy/datamanager.py", line 86, in tpc_begin
self.session.flush()
File "/home/user/workspace/myforum/env/lib/python2.6/site-packages/sqlalchemy/orm/session.py", line 1583, in flush
self._flush(objects)
File "/home/user/workspace/myforum/env/lib/python2.6/site-packages/sqlalchemy/orm/session.py", line 1654, in _flush
flush_context.execute()
File "/home/user/workspace/myforum/env/lib/python2.6/site-packages/sqlalchemy/orm/unitofwork.py", line 331, in execute
rec.execute(self)
File "/home/user/workspace/myforum/env/lib/python2.6/site-packages/sqlalchemy/orm/unitofwork.py", line 475, in execute
uow
File "/home/user/workspace/myforum/env/lib/python2.6/site-packages/sqlalchemy/orm/persistence.py", line 67, in save_obj
states_to_insert, states_to_update)
File "/home/user/workspace/myforum/env/lib/python2.6/site-packages/sqlalchemy/orm/persistence.py", line 702, in _finalize_insert_update_commands
mapper.dispatch.after_insert(mapper, connection, state)
File "/home/user/workspace/myforum/env/lib/python2.6/site-packages/sqlalchemy/event.py", line 291, in __call__
fn(*args, **kw)
File "/home/user/workspace/myforum/env/lib/python2.6/site-packages/sqlalchemy/orm/events.py", line 360, in wrap
wrapped_fn(*arg, **kw)
File "/home/user/workspace/myforum/cube_forum/models.py", line 165, in post_saved
topic.post_count = topic.posts.count()
File "/home/user/workspace/myforum/env/lib/python2.6/site-packages/sqlalchemy/orm/dynamic.py", line 249, in count
sess = self.__session()
File "/home/user/workspace/myforum/env/lib/python2.6/site-packages/sqlalchemy/orm/dynamic.py", line 219, in __session
sess.flush()
File "/home/user/workspace/myforum/env/lib/python2.6/site-packages/sqlalchemy/orm/session.py", line 1577, in flush
raise sa_exc.InvalidRequestError("Session is already flushing")
InvalidRequestError: Session is already flushing
如何在Pyramid / SQLalchemy中正确执行?
编辑:问题实际上是如何在金字塔中使用SQLAlchemy的事件。
答案 0 :(得分:0)
我发现数据库级别存在问题 - 无法保证获得有效的帖子计数值。想象一下,如果在多个交易中同时向主题添加了两个或更多帖子 - 除非您每次添加新帖子时都锁定主题表或记录,否则无法确定相关帖子的有效计数。
可能的解决方案:
1)将方法添加到Topic类。始终是有效数字,但每次都会对db执行查询。
class Topic(Base):
...
def posts_count(self):
return self.posts.count()
2)在不使用after_insert事件的情况下,在帖子中添加帖子并更新post_count。每次更改帖子主题时,您都需要更新topic.post_count。此方法也不会解决我在同时向主题添加多个帖子时所描述的问题。
def add_post(topic, request):
post_form = PostForm(request.POST)
if 'submit' in request.POST and post_form.validate():
post = Post(body=post_form.body.data)
post.user = request.user
topic.posts.append(post)
topic.post_count = topic.posts.count()
DBSession.add(post)
DBSession.add(topic)
request.session.flash(_('Post was added'))
transaction.commit()
raise HTTPFound(location=request.route_url('topic',id=topic.id))
return {'post_form':post_form}
答案 1 :(得分:0)
SQLAlchemy事件“after_insert”不适合此任务。
答案是使用Pyramid的自定义事件,如http://dannynavarro.net/2011/06/12/using-custom-events-in-pyramid/
所述