Actually with web apps we might get into deep trouble if transactions
are _not_ tied to one specific place, but users are allowed to do
transactions themselves. The problem is in the fact that
commit/rollback are not bound to the cursor, but to the connection
(only psycopg works differently there - it allows commit on the cursor,
but that's not DBAPI2).
So we can't just pass cursors around and let users decide what cursor
to commit - the commit would have to be on the connection. And it would
commit all open database changes, even from cursors the user didn't
really know about.
Think about a web project where you use a foreign app that uses
explicit transaction control and your own apps use a commit-on-response
transaction control. As soon as the foreign app would trigger a commit,
the changes you did in your apps are commited, too - but you don't
expect that to happen, as your transaction isn't commited, yet - you
think.
I think the best way to do commits is to tie them to the request by
using a middleware that does the commit-rollback-handling. Only needed
change to django would be a process_exception middleware hook that is
only triggered if the viewfunc throws an exception - to do the
automatic rollback. Additionally the middleware would set a global flag
"don't commit on .save()" that would be adhered to by the model code -
that way the user can either have the current commit-on-.save()
functionality or load the transaction control middleware to get
commit-on-response functionality. Since middleware runs for _all_ apps
in a project, all apps are handled the same way.
For batch scripts, since there is no transaction middleware active,
this would end in using the current .save() transaction system. But it
could be easily changed by giving utility functions that
disable_commit_on_save() in that global flag and give a manual_commit()
and manual_rollback() function to do manual transaction control. Since
commits and rollbacks are defined on the database connection, there is
no need to pass along cursors or such stuff.
Of course this doesn't take advantage of more finegrained transaction
stuff in databases like named cursors, savepoints and stuff like that -
but that could be left for 1.1, I think for 1.0 the above stuff would
be just fine.
bye, Georg
I don't think so - it's just a rather lightweight modification to the
request - it only does the commit/rollback and the global flag setting.
The middleware would only be a rather small thing - some handfull of
lines at max.
And middleware already has side-effects like changing elements of the
request object, for example. Or storing stuff in the cache - the
caching middleware actually would be a much "bigger" middleware than
the transaction middleware :-)
The nice thing about using a middleware for binding transactions to the
request would be that the transaction stuff won't be coupled with the
request machinery at all - it's just a middleware the user can decide
to run or not to run.
bye, Georg
Sure: in those cases you just won't use the "easy-way-out" middleware,
but just use the "disable-commit-on-save" machinery and do explicit
commits and rollbacks yourself.
This discussion is titled "transactions in the admin" - there you
usually don't have those out-of-band activity, you only have the
all-or-nothing-approach. The middleware would make stuff like
transactional admin or simple transactional sites quite easy.
For more complex sites, just use the decorator_from_middleware to
create a decorator for transactionality and use that for those view
functions that should use the all-or-nothing approach and use manual
transactions in all other cases.
My idea is just to provide a really easy way to do the all-or-nothing
approache without taking away the possibility of explicit control for
those who know exactly what to do. And the combined
middleware/decorator thingy is working very nicely with the caching
system, so I just thought it would be the ideal choice for the
transactionality system, too :-)
Maybe it would be easier to discuss this with some code at hand. If
#616 would be applied, django would have the needed "process_exception"
mechanism (that would be usefull regardless wether we use it for
transactions or not, I think). Then I could hack up the middleware and
decorator as I envision it and we could play with it to see wether it's
worth to do more investigation. It's not much work, so I won't mind to
do it even if it's later thrown away for a better approach.
BTW: the "passing around cursors" was a reference to the idea adrian
posted to ticket #9. There he was passing around cursors to signal
explicit transactions.
bye, Georg
from django.utils.decorators import decorator_from_middleware
from django.middleware.middlewaremodule import MiddlewareClass
decoratorfunc = decorator_from_middleware(MiddlewareClass)
:-)
Went into the source with the recent cache reworkings. I didn't like
the code duplication of cache management between the middleware and the
decorator.
bye, Georg
regarding the patch, i'll address the transaction coupling in another
respone in this thread, but the zope usage, i wanted to clarify. first
the dependency is on parts of zope3 not zope2, and as jason alluded to
in the initial thread, zope3 is designed from the ground up to be
reusable as a set of python libraries, and is internally packaged that
way ( using the zpkg tool, and eggs in the future ). as libraries they,
unlike zope2, don't do any magic initialization or alter the runtime,
meaning that they can play well with frameworks like django that do ;-)
and their designed to be pythonic. the portions of zope3 that are
needed for this patch can be trimmed down to 2 small packages.
i'm not clear if the reason that zope3 is 'big reason not to include'
is related to the above or perhaps nih, but imho zope3 has a lot of
quality engineering work done on it, backed up by thousands of unit
tests, and i think its good to benefit from the experience of others
who have been solving complex problems with python for years. the patch
itself is small, in part due to the functionality of the libraries z3
offers, and the nice design of django, but more importantly in addition
to making django transactional it provides a solid foundation as well
for future work with transactions in django, ie integration of multiple
databases (simulatenous), distributed cache transactions, sending
emails out (after transaction commit, or to disk for separate
processing), etc.
-kapil
import transaction
# do some work
# and
transaction.commit()
# or
transaction.abort()
additionally there are more options and apis available to those who
want to use them, like transaction observers, or doing transactional
file system interactions.
integration was done on the web layer there because coming from a zope
world, where thats the norm, i'm loathe to go back to manual
transaction management, or none at all, and imho the cases where you
don't want a web request to map to a transaction are extremly rare,
only one i can think of atm is bulk loading. web requests that mutate
data logically map onto semantic actions, and those actions should
either succeed or not. pushing the burden onto the programmer is the
wrong thing for a framework todo, it gets complex exponentially as the
apps do, and the best thing a framework can do is keep it simple, by
freeing the programmer from having to think about it. unless of course
they need to, in which case the patch still allows for manual
transaction management even in a web request, ie. you can still
directly commit and abort the transaction(s) within a web request.
-kapil
-kapil
cheers,
-kapil
>Maybe it would be easier to discuss this with some code at hand. If
>#616 would be applied, django would have the needed "process_exception"
>mechanism (that would be usefull regardless wether we use it for
>transactions or not, I think). Then I could hack up the middleware and
>decorator as I envision it and we could play with it to see wether it's
>worth to do more investigation. It's not much work, so I won't mind to
>do it even if it's later thrown away for a better approach.
I just did that and added it to ticket #9 - so you could have a look on
how I think transaction management could be done and what ways to
control transactions this would give.
bye, Georg
Me, too. I'd like to use Django for a time and expense app, but it's a
no go without some kind of answer for bootstrapping transactions into
my code.
-jason
>coupling. one question about the the implementation design, i guess i'm
>not clear why a stack is needed at all? unless there is some sort of
>mixing between different modes is expected on the same request, which i
>think would best to avoid due to the subleties and complexities of the
>stack transaction blocks interactions, ie. any automatic blocks in the
>stack prior to a managed will have their lifecycle dictated by it, as
>opposed to the semantic notion in the docs of tying to the
>request/response cycle, basically requiring global knowledge of the
Not everybody will use the django code in webapps - if you write batch
programs, you need a way to do transaction handling, too. And the
decorators already "stack" - you might have the transaction middlware
active, so your transactions are bound to request/response, but you
want to use the commit_manually decorator on some functions where you
want to controll transactions manually.
>regarding the actual implementation, it would be nice to see the core
>transaction manager api pluggable by encompassing the commit/rollback
>function implementations into a private global instance (or at least a
Please provide some sample code how you think this would look like.
It's easier to talk about code than about ideas, as ideas can easily be
misunderstood (or not understood), while code is just that: code. :-)
bye, Georg