Django memberikan anda sedikit cara mengendalikan bagaimana transaksi basisdata dikelola.
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.
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.
Peringatan
While the simplicity of this transaction model is appealing, it also makes it inefficient when traffic increases. Opening a transaction for every view has some overhead. The impact on performance depends on the query patterns of your application and on how well your database handles locking.
Per-request transactions and streaming responses
When a view returns a StreamingHttpResponse
, reading
the contents of the response will often execute code to generate the
content. Since the view has already returned, such code runs outside of
the transaction.
Generally speaking, it isn’t advisable to write to the database while generating a streaming response, since there’s no sensible way to handle errors after starting to send the response.
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)[sumber]¶Penghias ini akan meniadakan pengaruh dari ATOMIC_REQUESTS
untuk tampilan yang diberikan:
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.
Django provides a single API to control database transactions.
atomic
(using=None, savepoint=True)[sumber]¶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:
from django.db import transaction
@transaction.atomic
def viewfunc(request):
# This code executes inside a transaction.
do_stuff()
dan sebagai context manager:
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:
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.
Avoid catching exceptions inside atomic
!
When exiting an atomic
block, Django looks at whether it’s exited
normally or with an exception to determine whether to commit or roll
back. If you catch and handle exceptions inside an atomic
block,
you may hide from Django the fact that a problem has happened. This
can result in unexpected behavior.
This is mostly a concern for DatabaseError
and its
subclasses such as IntegrityError
. After such an
error, the transaction is broken and Django will perform a rollback at
the end of the atomic
block. If you attempt to run database
queries before the rollback happens, Django will raise a
TransactionManagementError
. You may
also encounter this behavior when an ORM-related signal handler raises
an exception.
The correct way to catch database errors is around an atomic
block
as shown above. If necessary, add an extra atomic
block for this
purpose. This pattern has another advantage: it delimits explicitly
which operations will be rolled back if an exception occurs.
If you catch exceptions raised by raw SQL queries, Django’s behavior is unspecified and database-dependent.
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.
Pertimbangan penampilan
Open transactions have a performance cost for your database server. To
minimize this overhead, keep your transactions as short as possible. This
is especially important if you’re using atomic()
in long-running
processes, outside of Django’s request / response cycle.
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.
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.
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:
Lewati fungsi apapun (yang tidak mengambil argumen) ke on_commit()
:
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:
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.
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:
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:
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()
On-commit functions for a given transaction are executed in the order they were registered.
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()
.
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.
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.
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!
Peringatan
Selalu memilih atomic()
jika memungkan sama sekali. Itu menghitung untuk keanehan dari setiap basisdata dan mecegah tindakan-tindakan tidak sah.
The low level APIs are only useful if you’re implementing your own transaction management.
Django provides a straightforward API in the django.db.transaction
module to manage the autocommit state of each database connection.
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.
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
.
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.
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)[sumber]¶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)[sumber]¶Releases savepoint sid
. The changes performed since the savepoint was
created become part of the transaction.
These functions do nothing if savepoints aren’t supported or if the database is in autocommit mode.
In addition, there’s a utility function:
The following example demonstrates the use of savepoints:
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.
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.
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:
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.
Catatan
This section is relevant only if you’re implementing your own transaction
management. This problem cannot occur in Django’s default mode and
atomic()
handles it automatically.
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.
The first option is to roll back the entire transaction. For example:
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.
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:
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.
Apr 04, 2017