Python – Design code to properly handle errors in Django/Python applications?

Design code to properly handle errors in Django/Python applications?… here is a solution to the problem.

Design code to properly handle errors in Django/Python applications?

I’m building a Django app and I’m hoping to get some advice on how to properly handle bugs and bugs in my code.

This is an example of a common scenario where I have a problem: a user purchases a product. To process the purchase, my View needs to do something:

  1. First, View should create a User object in the database.
  2. If successful, View should create an Order object and assign it to the newly created user.
  3. If successful, my code should create a Product object and add it to the newly created order.

If no errors happen, everything is fine – but I find occasional errors to be inevitable in my code, and I want my application to handle errors gracefully, rather than crashing outright. For example, if an Order object cannot be created for any reason, View should display an error to the user and delete the previously created User object. And, instead of crashing directly and presenting an HTTP 500 error to the user, it should throw an elegant error message.

The only way I can think of is to use an extremely complex series of nested try/except clauses, as shown below. But designing my code this way was very confusing and time-consuming, and didn’t feel like the right way to do things. I know there has to be a better way to design proper error handling in Django and Python, but I’m not quite sure what it is.

I would appreciate any suggestions on how to better structure my code in this case.

Sample code:

try:

# Create a new user
    u = User(email='[email protected]')
    u.save()

try:

# Create a new order
        o = Order(user=u, name='Order name')
        o.save()

try:

# Create a new product
            p = Product(order=o, name='Product name')
            p.save()

# If a product cannot be created, print an error message and try deleting the user and order that were previously created
        except:

messages.add_message(request, messages. ERROR, 'Product could not be created')

# If deleting the order doesn't work for any reason (for example, o.save() didn't properly save the user), 'pass' to ensure my application doesn't crash
            try:
                o.delete()

# I use these 'except: pass' clauses to ensure that if an error occurs, my app doesn't serve a Http 500 error and instead shows the user a graceful error
            except:
                pass

# If deleting the user doesn't work for any reason (for example, u.save() didn't properly save the user), 'pass' to ensure my application doesn't crash
            try:
                u.delete()
            except:
                pass

# If an order cannot be created, print an error message and try deleting the user that was previously created
    except:
        messages.add_message(request, messages. ERROR, 'Order could not be created')

# If deleting the user doesn't work for any reason (for example, u.save() didn't properly save the user), 'pass' to ensure my application doesn't crash
        try:
            u.delete()
        except:
            pass

# If the user cannot be created, throw an error
except:
    messages.add_message(request, messages. ERROR, 'User could not be created')

Solution

I recommend using transaction.atomic block, which should contain your model creation (from the django documentation) like this link):

try:
    with transaction.atomic():
        create_your_objects()
except IntegrityError:
    handle_exception()

This way, any changes made in the context manager are automatically rolled back if any issues occur.

P.S. Actually, this is how django handles every View by default, but for your case you expect it to fail, then you can get rid of the 500 error and still get a clean database in case of a problem without having to drop every created object.

Related Problems and Solutions