> On Mar 4, 2017, at 20:19, Mike Orr <[email protected]> wrote:
> 
> On Sat, Mar 4, 2017 at 12:16 PM, Bert JW Regeer <[email protected]> wrote:
>> 
>> On Mar 3, 2017, at 12:52, Mike Orr <[email protected]> wrote:
>> 
>> On Fri, Mar 3, 2017 at 8:34 AM, Michael Merickel <[email protected]> wrote:
>> 
>> On Fri, Mar 3, 2017 at 10:10 AM, Mike Orr <[email protected]> wrote:
>> 
>> 
>> The biggest problem with pyramid_tm I've run into is wanting to
>> rollback or commit the accumulated changes within the view without
>> messing up the global transaction outside the view. Normally when you
>> commit SQLAlchemy starts a new transaction afterward, but with
>> pyramid_tm you have to use 'transaction.commit' and that messes up its
>> transaction for the rest of request processing.
>> 
>> 
>> 
>> Isn't this what savepoints are for?
>> 
>> def view(request):
>>   sp = request.tm.savepoint()
>>   try:
>>       # view things ...
>> 
>>       # finally flush to push changes to the db without committing... this
>> will
>>       # raise any IntegrityError, etc
>>       request.dbsession.flush()
>>   except Exception:
>>       # go back to where we were before the view executed
>>       sp.rollback()
>> 
>> 
>> Ah, I didn't realize you could do that.
>> 
>> Obviously a lot of people are confused on this point, and that's an issue.
>> The most common cases are usually solved by some combination of savepoints
>> and flushing the underlying database session to catch errors earlier. For
>> example, SQLAlchemy buffers all of your edits until a flush, and if you
>> don't do it manually then it waits until commit. You could catch them
>> earlier if you did a request.dbsession.flush() in a try/except (probably
>> around a savepoint).
>> 
>> 
>> The issue is that you may set up 'pyramid_tm' because the normal case
>> applies 99% of the time and seemingly always, and then you already
>> have an application built and discover that one view needs something
>> more complex.
>> 
>> I'll try savepoints and see if that solves it. As for flush, there's a
>> problem in the opposite direction. If you don't flush then you can
>> expunge pending adds/deletes from the session and it's like they never
>> were there. But if you do flush (e.g., to get an autoincrement ID or
>> fill in server-side defaults), then you can't simply expunge them, you
>> have to roll back. And if you've written a library function that
>> flushes, you can't get around the flush without bypassing or rewriting
>> the function.
>> 
>> 
>> I don’t follow this at all… either way you don’t delete because the
>> transaction is rolled back.
> 
> I don't want the global transaction to be rolled back because that
> would cause a 500 error for the user. I want the request to succeed; I
> just want to roll back my little work and give the user some kind of
> status message on the page.
> 

The transaction is rolled back when you raise from your standard view code, 
that exception that is raised will then be handled by the exception view tween, 
which will attempt to render the exception much like a normal view.

For example, given this view:

@view_config(renderer='../templates/index.mako',
             accept='text/html')
def create_new_user(request):
    user = create_user(request.POST[‘username’])
    return {‘msg’: ’Success, user created'}

and this as an exception view:

@view_config(
    context=sqlalchemy.exc,
    renderer=‘../templates/error.mako’,
    exception_only=True,
)
def reterror_sql(context, request):
    return {‘error’: ‘Temporary issue with our backend, please try again.’}
    


If the create_user call fails with a sqlalchemy error (which will cause the 
transaction to be aborted), then the exception view can still render the error 
page and the message as appropriate. However, an exception view is not allowed 
to raise yet another exception, that will cause that exception to be uncaught 
and it will be returned to the WSGI server, which will turn the response into a 
500 error.

This allows you to then deal with the exception raised from sqlalchemy in a 
sane manner. If you are doing things within sqlalchemy outside of the view, 
those would also be rolled back, but that is the whole point of a transaction, 
it makes sure that when the request comes in, it doesn’t leave the state 
hanging in an unknown state.

If you absolutely have something that should always be committed after a 
request (success or not), then you should probably not be using the transaction 
manager.

> -- 
> You received this message because you are subscribed to the Google Groups 
> "pylons-discuss" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to [email protected].
> To post to this group, send email to [email protected].
> To view this discussion on the web visit 
> https://groups.google.com/d/msgid/pylons-discuss/CAH9f%3Duqv8dkUc-QpW0MkDSf5xAVjTrzUgPKQKFtMQS3H90FFdw%40mail.gmail.com.
> For more options, visit https://groups.google.com/d/optout.

-- 
You received this message because you are subscribed to the Google Groups 
"pylons-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/pylons-discuss/8CE7F8EC-81CA-47EC-9522-57E7C6EEC9D3%400x58.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to