Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Upgrading transaction.commit_manually() to Django > 1.6

I have inherited some code for an app that was written for Django 1.4. We need to update the codebase to work with Django 1.7, and eventually 1.8 as the next Long Term Support release.

In a few places it uses the old style @transaction.commit_manually and with transaction.commit_manually:

I do not know enough about transactions in general but I am trying to understand what they are used for, so I can either remove them (if unnecessary) or upgrade them to the newer set_autocommit(False) or equivalent.

I have understood that transaction management in Django < 1.5 was not ideal, and too complicated for most use cases.

The database connection looks like this, with no special transaction management. (Using Postgres 9.3)

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'dbname',
        'USER': 'user',
        'PASSWORD': '',
        'HOST': 'localhost',
        'PORT': '',
    }
}

There is no special transaction middleware.

I find the following view particularly puzzling. (Edited)

@UserRequiredDecorator
class XMLModelView(View):

    @transaction.commit_manually
    def get(self, request, *args, **kwargs):
        user = request.user

        xml_models = models.XMLModel.objects.filter(user=user).order_by('-created').all()
        if xml_models:
            xml_model = xml_models[0]
            model = xml_model.xml_field
        else:
            obj = initialize_xml_model(user)
            model = obj.xml_field

        transaction.commit()
        if isinstance(model, unicode):
            model = model.encode('utf-8')

        with transaction.commit_manually():
            xml = XMLManipulator(model, remove_blank_text=True)
            xml.insert_user_info(user)
            xml.whitespace_cleanup()
            model = xml.tostring()
            del xml
            transaction.commit()
        return HttpResponse(model, content_type='text/xml')

Where initialize_xml_model is a function that takes a flat xml file (xml model template) and creates a new XMLModel object. And insert_user_info inserts information stored in the user object into the xml model.

The way I read this code is

  1. We turn off autocommit with commit_manually
    • We either get the most recent XMLModel object for the user, or
    • initialize a new XMLModel object
  2. transaction.commit() stores this to the db if there are no errors.
  3. We check if our model is a unicode instance and then encodes it (I'm not sure what this does exactly)
  4. We open a new transaction
  5. Instanciate an XMLManipulator object with model
  6. Insert stuff and clean up xml
  7. Assign xml instance back to model as string (tostring is an XMLManipulator method that retains stylesheet declarations. )
  8. delete the xml object
  9. Commit transaction

After 5. the only thing that deals with the db (in a read) is the insert_user_info method.

I don't really understand why this is happening in a special transaction. There is no writing to the db?

There is no other methods on this view, only get.

I might have missed out something important here, feel free to ask any questions or for more information.

Is the transaction really necessary here, and if so, how could this be rewritten to suit the new transaction.set_autocommit?

Any help or pointers would be greatly appreciated.

like image 307
seb Avatar asked Mar 19 '15 16:03

seb


1 Answers

A good way to do this now is using transaction.atomic. In your example I'd do:

from django.db import transaction

@UserRequiredDecorator
class XMLModelView(View):

    def get(self, request, *args, **kwargs):

        with transaction.atomic():
            user = request.user

            xml_models = models.XMLModel.objects.filter(user=user).order_by('-created').all()
            if xml_models:
                xml_model = xml_models[0]
                model = xml_model.xml_field
            else:
                obj = initialize_xml_model(user)
                model = obj.xml_field

        if isinstance(model, unicode):
            model = model.encode('utf-8')

        with transaction.atomic():
            xml = XMLManipulator(model, remove_blank_text=True)
            xml.insert_user_info(user)
            xml.whitespace_cleanup()
            model = xml.tostring()
            del xml

        return HttpResponse(model, content_type='text/xml')
like image 81
hugotacito Avatar answered Oct 12 '22 03:10

hugotacito