Transaksi basisdataLink to this heading

Django memberikan anda sedikit cara mengendalikan bagaimana transaksi basisdata dikelola.

Mengelola transaksi basisdataLink to this heading

Kebiasaan transaksi awal DjangoLink to this heading

Kebiasaan awal Django adalah menjalankan suasana penyeraahn otomatis. Setiap query segera diserahkan ke basisdata, meskipun transaksi adalah aktif. Lihat dibawah ini untuk rincian.

Django menggunakan transaksi atau titiksimpan otomatis untuk menjamin kesatuan dari operasi ORM yang membutuhkan banyak query, khususnya query delete() dan update().

Django's TestCase class also wraps each test in a transaction for performance reasons.

Tying transactions to HTTP requestsLink to this heading

Cara umum untuk menangani transaksi di jaringan adalah membungkus setiap permintaan di transaksi. Setel ATOMIC_REQUESTS ke True di konfigurasi dari setiap basisdata untuk anda ingin adakan kebiasaan ini.

It works like this. Before calling a view function, Django starts a transaction. If the response is produced without problems, Django commits the transaction. If the view produces an exception, Django rolls back the transaction.

You may perform subtransactions using savepoints in your view code, typically with the atomic() context manager. However, at the end of the view, either all or none of the changes will be committed.

In practice, this feature simply wraps every view function in the atomic() decorator described below.

Note that only the execution of your view is enclosed in the transactions. Middleware runs outside of the transaction, and so does the rendering of template responses.

Ketika ATOMIC_REQUESTS diadakan, itu masih memungkinkan mencegah tampilan dari berjalan dalam sebuah transaksi.

non_atomic_requests(using=None)Link to this definition

Penghias ini akan meniadakan pengaruh dari ATOMIC_REQUESTS untuk tampilan yang diberikan:

Code
from django.db import transaction

@transaction.non_atomic_requests
def my_view(request):
    do_stuff()

@transaction.non_atomic_requests(using='other')
def my_other_view(request):
    do_stuff_on_the_other_database()

It only works if it's applied to the view itself.

Mengendalikan transaksi secara eksplisitLink to this heading

Django provides a single API to control database transactions.

atomic(using=None, savepoint=True)Link to this definition

Atomicity is the defining property of database transactions. atomic allows us to create a block of code within which the atomicity on the database is guaranteed. If the block of code is successfully completed, the changes are committed to the database. If there is an exception, the changes are rolled back.

Blok atomic dapat bersarang. Dalam kasus ini, ketika sebuah blok sebelah dalam berhasil terpenuhi, pengaruhnya dapat masih di rollback jika sebuah pengecualian dimunculkan di blok sebelah luar pada titik kemudian.

atomic is usable both as a decorator:

Code
from django.db import transaction

@transaction.atomic
def viewfunc(request):
    # This code executes inside a transaction.
    do_stuff()

dan sebagai context manager:

Code
from django.db import transaction

def viewfunc(request):
    # This code executes in autocommit mode (Django's default).
    do_stuff()

    with transaction.atomic():
        # This code executes inside a transaction.
        do_more_stuff()

Wrapping atomic in a try/except block allows for natural handling of integrity errors:

Code
from django.db import IntegrityError, transaction

@transaction.atomic
def viewfunc(request):
    create_parent()

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

    add_children()

In this example, even if generate_relationships() causes a database error by breaking an integrity constraint, you can execute queries in add_children(), and the changes from create_parent() are still there. Note that any operations attempted in generate_relationships() will already have been rolled back safely when handle_exception() is called, so the exception handler can also operate on the database if necessary.

In order to guarantee atomicity, atomic disables some APIs. Attempting to commit, roll back, or change the autocommit state of the database connection within an atomic block will raise an exception.

atomic takes a using argument which should be the name of a database. If this argument isn't provided, Django uses the "default" database.

Dibawah tenda, kode pengelolaan transaksi Django:

  • membuka sebuah transaksi ketika memasukkan blok atomic paling luar;

  • membuat titik simpan ketika memasukkan blok atomic sebelah dalam;

  • melepaskan atau digulung kembali ke titik simpan ketika keluar blok paling dalam;

  • menyerahkan atau gulung kembali transaksi ketika keluar blok paling luar

Anda dapat meniadakan pembuatan dari titik simpan untuk blok sebelah dalam dengan mengatur argumen savepoint ke False. Jika sebuah pengecualian muncul, Django akan melakukan gulunh kembali ketika keluar blok induk pertama dengan sebuah titik simpan jika ada, dan blok paling luar jika tidak. Atomicity masih dijamin oleh transaksi paling luar. Pilihan ini harus hanya digunakan jika kelebihan dari titik simpan dapat dilihat. Itu mempunyai kekurangan dari memecahkan penanganan kesalahan yang digambarkan diatas.

Anda dapat menggunakan atomic ketika penyerahan otomatis dimatikan. Itu akan hanya menggunakan titik simpan, bahkan untuk blok paling luar.

Penyerahan otomatisLink to this heading

Kenapa Django menggunakan penyerahan otomatisLink to this heading

Dalam standar SQL, setiap permintaan SQL memulai sebuah transaksi, meskipun satu sudah aktif. Transaksi tersebut harus secara eksplisit diserahkan dan digulung kembali.

Ini tidak selalu mudah untuk pengembang aplikasi. Untuk meredakan masalah ini, kebanyakan basisdata menyediakan sebuah suasana penyerahan otomatis. Ketika penyerahan otomatis dinayalan dan tidak ada transaksi aktif, setiap permintaan SQL dibungkus dalam transaksinya sendiri. Dengan kata lain, bukan hanya melakukan setiap permintaan tersebut mulai sebuah transaksi, tetapi transaksi juga mendapatkan otomatis diserahkan atau digulung kembali, tergandunt pada apakah permintaan berhasil.

PEP 249, the Python Database API Specification v2.0, requires autocommit to be initially turned off. Django overrides this default and turns autocommit on.

Untuk menghindari ini, anda dapat menonaktifkan pengelolaan transaksi, tetapi itu sangat tidak dianjurkan.

Menonaktifkan pengelolaan transaksiLink to this heading

You can totally disable Django's transaction management for a given database by setting AUTOCOMMIT to False in its configuration. If you do this, Django won't enable autocommit, and won't perform any commits. You'll get the regular behavior of the underlying database library.

Ini membutuhkan anda untuk menyerahkan secara eksplisit setiap transaksi, bahkan yang dimulai oleh Django atau oleh pustaka pihak ketiga. Jadi, ini adalah penggunaan terbaik dalam keadaan dimana anda ingin menjalankan middleware pengendalian transaksi milik anda atau melakukan sesuatu yang sangat aneh.

Melakukan tindakan setelah penyerahanLink to this heading

Terkadang anda butuh melakukan sebuah tindakan terkait pada transaksi basisdata saat ini, tetapi hanya jika transaksi berhasil diserahkan. Contoh mungkin termasuk tugas Celery, sebuah pemberitahuan surel, atau penghapusan tembolok

Django menyediakan fungsi on_commit() untuk mendaftarkan fungsi panggil kembali yang harusnya dijalankan setelah transaksi berhasil diserahkan:

on_commit(func, using=None)Link to this definition

Lewati fungsi apapun (yang tidak mengambil argumen) ke on_commit():

Code
from django.db import transaction

def do_something():
    pass  # send a mail, invalidate a cache, fire off a Celery task, etc.

transaction.on_commit(do_something)

Anda dapat juga membungkus fungsi anda dalam lambda:

Code
transaction.on_commit(lambda: some_celery_task.delay('arg1'))

Fungsi anda lewati akan dipanggil segera setelah hipotetis penulisan basisdata dibuat dimana on_commit() dipanggil akan berhasil diserahkan.

Jika anda memanggil on_commit() selagi tidak ada sebiah transaksi aktif, panggil kembali akan dijalankan segera.

Jika hipotetis penulisan basisdata itu bukannya digulung kembali (khususnya ketika sebuah pengecualian tidak tertangani dimunculkan dalam sebuah blok atomic()), fungsi anda akan disingkirkan dan tidak pernah dipanggil.

SavepointLink to this heading

Titik simpan (yaitu blok atomic() bersarang) ditangani dengan benar. Yaitu, sebuah on_commit() callable terdaftar setelah sebuah titik simpan (dalam blok atomic() bersarang) akan dipanggil setelah transaksi paling luar diserahkan, tetapi tidak jika disimpan kembali ke titik simpan tersebut atau titik simpan sebelumnya yang muncul selama transaksi:

Code
with transaction.atomic():  # Outer atomic, start a new transaction
    transaction.on_commit(foo)

    with transaction.atomic():  # Inner atomic block, create a savepoint
        transaction.on_commit(bar)

# foo() and then bar() will be called when leaving the outermost block

Pada sisi lain, ketika sebuah titik simpan disimpan kembali (karena sebuah pengecualian telah dimunculkan), sebe;ah dalam dapat dipanggil tidak akan dipanggil:

Code
with transaction.atomic():  # Outer atomic, start a new transaction
    transaction.on_commit(foo)

    try:
        with transaction.atomic():  # Inner atomic block, create a savepoint
            transaction.on_commit(bar)
            raise SomeError()  # Raising an exception - abort the savepoint
    except SomeError:
        pass

# foo() will be called, but not bar()

Urutan pelaksanaanLink to this heading

On-commit functions for a given transaction are executed in the order they were registered.

Penanganan pengecualianLink to this heading

If one on-commit function within a given transaction raises an uncaught exception, no later registered functions in that same transaction will run. This is, of course, the same behavior as if you'd executed the functions sequentially yourself without on_commit().

Waktu pelaksanaanLink to this heading

Gulungan kembali anda dijalankan setelah penyerahan berhasil, jadi sebuah kegagalan dalam gulung kembali tidak akan menyebabkan transaksi di gulung kembali. Mereka dijalankan kondisional atas transaksi berhasil, tetapi mereka bukan bagian dari transaksi. Untuk penggunaan kasus dimaksud (pemberitahuan surat, tugas Celery, dll.), ini harus baik-baik saja. Jika tidak (jika tindakan mengikuti anda juga gawat dimana kegagalan itu harus berarti kegagalan dari transaksi itu sendiri), lalu anda tidak ingin menggunakan hubungan on_commit(). Sebagai gantinya, anda ingin two-phase commit seperti psycopg Two-Phase Commit protocol support dan optional Two-Phase Commit Extensions in the Python DB-API specification.

Callback tidak berjalan sampai perbaikan otomatis dipulihkan pada hubungan perbaikan berikut (karena sebaliknya apapun permintaan selesai dalam sebuah callback akan membuka sebuah transaksi tersirat, mencegah hubungan dari kembali kedalam suasana perbaikan otomatis).

When in autocommit mode and outside of an atomic() block, the function will run immediately, not on commit.

On-commit functions only work with autocommit mode and the atomic() (or ATOMIC_REQUESTS) transaction API. Calling on_commit() when autocommit is disabled and you are not within an atomic block will result in an error.

Use in testsLink to this heading

Django's TestCase class wraps each test in a transaction and rolls back that transaction after each test, in order to provide test isolation. This means that no transaction is ever actually committed, thus your on_commit() callbacks will never be run. If you need to test the results of an on_commit() callback, use a TransactionTestCase instead.

Why no rollback hook?Link to this heading

A rollback hook is harder to implement robustly than a commit hook, since a variety of things can cause an implicit rollback.

For instance, if your database connection is dropped because your process was killed without a chance to shut down gracefully, your rollback hook will never run.

The solution is simple: instead of doing something during the atomic block (transaction) and then undoing it if the transaction fails, use on_commit() to delay doing it in the first place until after the transaction succeeds. It’s a lot easier to undo something you never did in the first place!

Low-level APIsLink to this heading

Penyerahan otomatisLink to this heading

Django provides a straightforward API in the django.db.transaction module to manage the autocommit state of each database connection.

get_autocommit(using=None)Link to this definition
set_autocommit(autocommit, using=None)Link to this definition

These functions take a using argument which should be the name of a database. If it isn't provided, Django uses the "default" database.

Autocommit is initially turned on. If you turn it off, it's your responsibility to restore it.

Once you turn autocommit off, you get the default behavior of your database adapter, and Django won't help you. Although that behavior is specified in PEP 249, implementations of adapters aren't always consistent with one another. Review the documentation of the adapter you're using carefully.

You must ensure that no transaction is active, usually by issuing a commit() or a rollback(), before turning autocommit back on.

Django will refuse to turn autocommit off when an atomic() block is active, because that would break atomicity.

TransaksiLink to this heading

A transaction is an atomic set of database queries. Even if your program crashes, the database guarantees that either all the changes will be applied, or none of them.

Django doesn't provide an API to start a transaction. The expected way to start a transaction is to disable autocommit with set_autocommit().

Once you're in a transaction, you can choose either to apply the changes you've performed until this point with commit(), or to cancel them with rollback(). These functions are defined in django.db.transaction.

commit(using=None)Link to this definition
rollback(using=None)Link to this definition

These functions take a using argument which should be the name of a database. If it isn't provided, Django uses the "default" database.

Django will refuse to commit or to rollback when an atomic() block is active, because that would break atomicity.

SavepointLink to this heading

A savepoint is a marker within a transaction that enables you to roll back part of a transaction, rather than the full transaction. Savepoints are available with the SQLite (≥ 3.6.8), PostgreSQL, Oracle and MySQL (when using the InnoDB storage engine) backends. Other backends provide the savepoint functions, but they're empty operations -- they don't actually do anything.

Savepoints aren't especially useful if you are using autocommit, the default behavior of Django. However, once you open a transaction with atomic(), you build up a series of database operations awaiting a commit or rollback. If you issue a rollback, the entire transaction is rolled back. Savepoints provide the ability to perform a fine-grained rollback, rather than the full rollback that would be performed by transaction.rollback().

When the atomic() decorator is nested, it creates a savepoint to allow partial commit or rollback. You're strongly encouraged to use atomic() rather than the functions described below, but they're still part of the public API, and there's no plan to deprecate them.

Each of these functions takes a using argument which should be the name of a database for which the behavior applies. If no using argument is provided then the "default" database is used.

Savepoints are controlled by three functions in django.db.transaction:

savepoint(using=None)Link to this definition

Creates a new savepoint. This marks a point in the transaction that is known to be in a "good" state. Returns the savepoint ID (sid).

savepoint_commit(sid, using=None)Link to this definition

Releases savepoint sid. The changes performed since the savepoint was created become part of the transaction.

savepoint_rollback(sid, using=None)Link to this definition

Rolls back the transaction to savepoint sid.

These functions do nothing if savepoints aren't supported or if the database is in autocommit mode.

In addition, there's a utility function:

clean_savepoints(using=None)Link to this definition

Resets the counter used to generate unique savepoint IDs.

The following example demonstrates the use of savepoints:

Code
from django.db import transaction

# open a transaction
@transaction.atomic
def viewfunc(request):

    a.save()
    # transaction now contains a.save()

    sid = transaction.savepoint()

    b.save()
    # transaction now contains a.save() and b.save()

    if want_to_keep_b:
        transaction.savepoint_commit(sid)
        # open transaction still contains a.save() and b.save()
    else:
        transaction.savepoint_rollback(sid)
        # open transaction now contains only a.save()

Savepoint dapat digunakan untuk memulihkan dari kesalahan basisdata dengan melakukan rollback sebagian. Jika anda sedang melakukan ini didalam sebuah blok atomic(), keseluruhan blok akan masih dapat di rollback, karena itu tidak mengetahui anda telah menangani keadaan pada tingkatan terendah! Untuk mencegah ini, anda dapat mengendalikan perilaku rollback dengan fungsi-fungsi berikut.

get_rollback(using=None)Link to this definition
set_rollback(rollback, using=None)Link to this definition

Setel bendera rollback menjadi True memaksa sebuah rollback ketika keluar blok atomik paling sebelah dalam. Ini mungkin berguna untuk memicu sebuah rollback tanpa memunculkan sebuah pengecualian.

Setel itu menjadi False mencegah rollback seperti itu. Sebelum melakukan itu, pastikan anda telah rollback transaksi pada savepoint dikenal-baik dalam blok atomic saat ini! Sebaliknya anda sedang merusak atomic dan kerusakan data mungkin muncul.

Database-specific notesLink to this heading

Savepoint di SQLiteLink to this heading

While SQLite ≥ 3.6.8 supports savepoints, a flaw in the design of the sqlite3 module makes them hardly usable.

When autocommit is enabled, savepoints don't make sense. When it's disabled, sqlite3 commits implicitly before savepoint statements. (In fact, it commits before any statement other than SELECT, INSERT, UPDATE, DELETE and REPLACE.) This bug has two consequences:

  • The low level APIs for savepoints are only usable inside a transaction ie. inside an atomic() block.

  • It's impossible to use atomic() when autocommit is turned off.

Transaksi di MySQLLink to this heading

If you're using MySQL, your tables may or may not support transactions; it depends on your MySQL version and the table types you're using. (By "table types," we mean something like "InnoDB" or "MyISAM".) MySQL transaction peculiarities are outside the scope of this article, but the MySQL site has information on MySQL transactions.

If your MySQL setup does not support transactions, then Django will always function in autocommit mode: statements will be executed and committed as soon as they're called. If your MySQL setup does support transactions, Django will handle transactions as explained in this document.

Penanganan pengecualian dalam transaksi PostgreSQLLink to this heading

Inside a transaction, when a call to a PostgreSQL cursor raises an exception (typically IntegrityError), all subsequent SQL in the same transaction will fail with the error "current transaction is aborted, queries ignored until end of transaction block". While simple use of save() is unlikely to raise an exception in PostgreSQL, there are more advanced usage patterns which might, such as saving objects with unique fields, saving using the force_insert/force_update flag, or invoking custom SQL.

There are several ways to recover from this sort of error.

Gulung kembali transaksiLink to this heading

The first option is to roll back the entire transaction. For example:

Code
a.save() # Succeeds, but may be undone by transaction rollback
try:
    b.save() # Could throw exception
except IntegrityError:
    transaction.rollback()
c.save() # Succeeds, but a.save() may have been undone

Calling transaction.rollback() rolls back the entire transaction. Any uncommitted database operations will be lost. In this example, the changes made by a.save() would be lost, even though that operation raised no error itself.

Savepoint rollbackLink to this heading

You can use savepoints to control the extent of a rollback. Before performing a database operation that could fail, you can set or update the savepoint; that way, if the operation fails, you can roll back the single offending operation, rather than the entire transaction. For example:

Code
a.save() # Succeeds, and never undone by savepoint rollback
sid = transaction.savepoint()
try:
    b.save() # Could throw exception
    transaction.savepoint_commit(sid)
except IntegrityError:
    transaction.savepoint_rollback(sid)
c.save() # Succeeds, and a.save() is never undone

In this example, a.save() will not be undone in the case where b.save() raises an exception.