Django Transactions

Learn via video courses
Topics Covered

Overview

We have a few options to manage transactions thanks to Django. Django supports ORM which is relatively based on relational data. So to work with the databases, we have to learn about transactions. Django transaction consists of a discrete group of database queries which help us to handle with database with the use of Django queries(as ORM works). To manage database transactions, Django offers a solitary API. The defining characteristic of database transactions is atomicity. When performing multiple-query operations, such as delete() and update(), Django ORM automatically uses transactions or savepoints to ensure the integrity of the data. So in this article, we'll learn deeply about Django transactions.

Introduction

An autonomous, coherent, and dependable unit of work that is conducted against a database using a database management system (or a system comparable to it) is referred to as a database transaction. Any update to a database is often represented as a transaction. In a database environment, transactions serve two fundamental functions:

  • To offer dependable work units that enable accurate failure recovery and maintain database consistency even in the event of a system loss. For instance, when an application's execution abruptly and prematurely terminates, many operations on a database are left unfinished and uncertain of their status.
  • To offer separation between applications that are concurrently accessing a database. If this isolation is not offered, the results of the programs may be flawed.

Transactions

A transaction is a single logic or work unit that can occasionally consist of multiple operations in a database management system. A transaction is any logical operation carried out in a consistent mode within a database.

A transfer between bank accounts is one instance where this would occur; to complete the transaction, the amount to be transferred would need to be subtracted from one account and added to the other. A database transaction must be atomic (complete in its entirety or have no effect at all), consistent (conform to the constraints of the database), isolated (not affect other transactions), and durable by definition.

Django Transactions

In Django, a transaction is a sequence of database operations that are treated as a single unit of work. Transactions are used to ensure the consistency and integrity of data in a database, by making sure that either all of the operations in a transaction are completed successfully, or none of them are applied at all. Django provides built-in support for transactions, which allows developers to easily wrap blocks of code in transactions and control when they are committed. This allows for the implementation of complex transaction logic and the handling of errors and rollbacks.

In Django 1.6 and later, transactions are managed using the atomic decorator or context manager, which allows developers to wrap blocks of code in transactions and control when they are committed. This provides more flexibility and granular control over transactions and allows for the implementation of nested transactions and savepoints. Overall, transactions are an important part of working with databases in Django and are used to ensure the consistency and integrity of data. The improvements to transaction handling in Django 1.6 and later have made them more powerful and easier to work with.

In the majority of our transaction-related queries, we used transaction property. The name of a database should be provided as the user parameter for these functions. In the absence of one, Django employs the "default" database. Because doing so would violate atomicity, Django will not allow a commit or rollback while an atomic() block is in use. A single API is made available by Django to manage database transactions. The characteristic that distinguishes database transactions is atomicity. By using atomic, we may write a block of code that ensures atomicity on the database.

Each SQL query is wrapped in its transaction when auto-commit (which we'll learn further) is enabled and there isn't any other transaction going on. In other words, depending on whether the query was successful, each of these queries not only initiates a transaction but also automatically commits or rolls back the transaction. Django ORM automatically ensures the integrity of ORM operations.

Changes and Differences from Django 1.6 and Earlier

In Django 1.6 and earlier, the application essentially operated with an open transaction that was automatically committed whenever you wrote data to the database. Django created the necessary SQL queries and committed the transaction every time you called a method like model.save() or model.update(). Additionally, for binding transactions to HTTP requests in Django 1.6 and prior, it was advised that you utilize the TransactionMiddleware. Each request received its transaction. Django would commit the transaction if the response was returned without any exceptions, but ROLLBACK would be used if your view method provided an error. In essence, this disabled AUTOCOMMIT.

In Django 1.6, the features listed below were declared deprecated, and they will be eliminated in Django 1.8. They are documented to make the switch to the new transaction management APIs easier.

  • The following functions provided a mechanism to manage transactions on a per-function or per-code-block basis and were specified in django.db.transaction. They accepted a using parameter, just like atomic, and may be used as decorators or context_managers().

    • autocommit(): Activate Django's default autocommit functionality. Any time you use a function that writes to the database, such as model.save() or model.delete(), a transaction will be committed.
    • commit_on_success(): For all work completed in a function, use a single transaction. Django will immediately commit all work done within the function if it succeeds in returning. But Django will reverse the transaction if the function throws an exception.
    • commit_manually(): Django informs you that you will be handling the transaction by yourself. You must explicitly commit() or rollback() when working with the database, whether you are writing or just reading data, or Django will throw a TransactionManagementError exception. Because SELECT statements may call functions that modify tables, it is impossible to know if any data has been modified, so this is necessary when reading from the database.
  • The three aforementioned functions all made use of a concept known as "transaction states." Although it was discontinued in Django 1.6, this technique is still usable up until Django 1.8. Django initiates and starts in auto mode. Autocommit() enables auto mode, while TransactionMiddleware, commit_on_success(), and commit_manually() activate the managed mode. Inside, Django maintains a stack of states so that there must be a balance between activations and deactivations.

  • TransactionMiddleware is no longer supported in Django 1.6 and has been replaced with ATOMIC_REQUESTS.

    • There are two variances despite the general behavior remaining the same. The former API allowed users to change between autocommit and explicit committing at any point inside a view. This is not permitted any longer since ATOMIC_REQUESTS depends on atomic(), which ensures atomicity. It is still possible to avoid encapsulating a complete view in a transaction at the top level, though. Instead of using autocommit(), adorn the view with non atomic_requests() to achieve this.
    • In addition to view functions, the transaction middleware also applied to middleware modules that came after it. For instance, session creation was a part of the transaction if the session middleware was invoked after the transaction middleware. Only the view itself is covered by ATOMIC_REQUESTS.

In Django 1.6 and later versions, the way transactions are handled has been improved and changed in several ways. Some of the key changes and differences from earlier versions include:

  • Support for nested transactions and savepoints, which allows for more flexible and granular control over transaction logic.
  • Improved performance, thanks to the ability to break up transactions into smaller units and control when they are committed.
  • Better support for different databases, with more consistent behavior across different database engines.
  • The ability to manually control transactions using the atomic decorator or context manager, allowing developers to easily wrap blocks of code in transactions.
  • The introduction of the on_commit hook, which allows for the execution of code after a transaction has been committed.

Overall, these changes have made transactions in Django more powerful, flexible, and easier to work with.

Disadvantages with Transactions Before Django 1.6

  • All database operations were wrapped in a single, global transaction, which could lead to performance issues and make it difficult to manage complex transaction logic.
  • Lack of support for nested transactions and savepoints made it difficult to implement certain types of functionality, such as rollbacks or partial saves.
  • Overall, the transaction handling in earlier versions of Django was limited and inflexible.

Autocommit

Each SQL query in the SQL standards initiates a transaction unless one is already underway. Then, these transactions need to be expressly committed or undone. It's not always practical for application developers to do this. The majority of databases have an autocommit mode to help with this issue. Each SQL query is wrapped in its transaction when auto-commit is enabled and there isn't any other transaction in progress. In other words, depending on whether the query was successful, each of these queries not only initiates a transaction but also automatically commits or rolls back the transaction. In Django, the autocommit option determines whether or not database operations are automatically committed. By default, autocommit is set to True, which means that any database operations that are performed outside of a transaction will be automatically committed to the database.

However, in some cases, it may be necessary to disable autocommit and explicitly manage transactions using the atomic decorator or context manager. This allows for more granular control over when database changes are committed and can be useful for implementing complex transaction logic or handling rollbacks.

To disable autocommit in Django, you can set the AUTOCOMMIT option in your database settings to False. This will prevent any database operations from being automatically committed and will require you to use the atomic decorator or context manager to explicitly manage transactions.

If you set the AUTOCOMMIT option to True, all data-altering operations (such as Create, Update, and Delete) will run in their transaction and, depending on the result, will either be automatically committed to the database if successful or rolled back if unsuccessful. Since it eliminates the need to explicitly mark actions as final and provides sensible behavior (i.e., if the data operation is successful, it is made final, if not, it is reverted], the AUTOCOMMIT=True setting meets the expectations of most applications.

For example, you might use the following code to disable autocommit and wrap a block of code in a transaction:

Overall, the autocommit option in Django allows you to control whether or not database operations are automatically committed, and can be useful for implementing complex transaction logic. Disabling autocommit and explicitly managing transactions using the atomic decorator or context manager can provide greater control and flexibility.

Savepoints

In Django, a savepoint is a point within a transaction where you can "roll back" in the event of an error. This allows you to undo part of a transaction without having to undo the entire transaction. The SQLite ( 3.6.8), PostgreSQL, Oracle, and MySQL backends all support savepoints. The savepoint functions are offered by other backends, but they are merely empty operations that do nothing. For example, if you are performing multiple database operations and one of them fails, you can roll back to a savepoint and then continue executing the rest of the operations without having to start the entire transaction over again.

Savepoints aren't particularly helpful if you use autocommit, which is Django's default setting. However, as soon as you use atomic() to open a transaction, you start to accumulate a list of database actions that are waiting for a commit or rollback. The entire transaction is rolled back when you issue a rollback. Instead of the complete rollback that would be accomplished by a transaction, savepoints offer the opportunity to do a fine-grained rollback.

Despite being atomic, transactions can still be divided into savepoints. Consider savepoints to be incomplete transactions. Therefore, you may set up a savepoint after the second statement in a transaction that requires four SQL statements/Django queries to finish. Once that savepoint has been established, you can perform a partial rollback by deleting the third and fourth statements while maintaining the first two, even if the third or fourth statement fails. Therefore, you can perform partial rollbacks or commits by dividing a transaction into smaller, lighter transactions.

In this case, a transaction contains the full function. We generate a savepoint after adding a new user and obtain a reference to the savepoint. The following three assertions:

They are not included in the current savepoint, thus they may be included in the upcoming savepoint_rollback or savepoint_commit. The line user = User.create('jj','inception','jj','1234') will still be committed to the database in the event of a savepoint_rollback, even though the other updates won't.

For example, if we updated the save points function as follows:

When a database issue occurs, savepoints can be utilized to partially rewind the database. Because it is unaware that you resolved the situation at a lower level, if you perform this inside an atomic() block, the entire block will still be rolled back. The following functions let you manage the rollback behavior to stop this from happening.

Conclusion

This tutorial shows

  • Django transaction is a discrete group of database queries that help us to handle with database with the use of Django queries(as ORM works).
  • In Django 1.6 and earlier, the application essentially operated with an open transaction that was automatically committed whenever you wrote data to the database.
  • Autocommits i.e. depending on whether the query was successful, each of these queries not only initiates a transaction but also automatically commits or rolls back the transaction.
  • Even though transactions are atomic they can be further broken down into savepoints.