Fixed #9206 -- Clarified documentation of transaction handling in raw SQL, and error recovery for Postgres. Thanks to Richard Davies for the suggestion and draft text.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@10655 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Russell Keith-Magee 2009-05-02 07:40:25 +00:00
parent e85bc81651
commit 87d3ff731b
3 changed files with 55 additions and 46 deletions

View File

@ -752,37 +752,8 @@ Executing custom SQL
-------------------- --------------------
Another common pattern is writing custom SQL statements in model methods and Another common pattern is writing custom SQL statements in model methods and
module-level methods. The object :class:`django.db.connection module-level methods. For more details on using raw SQL, see the documentation
<django.db.backends.DatabaseWrapper>` represents the current database on :ref:`using raw SQL<topics-db-sql>`.
connection. To use it, call :meth:`connection.cursor()
<django.db.backends.DatabaseWrapper.cursor>` to get a cursor object. Then, call
``cursor.execute(sql, [params])`` to execute the SQL and
:meth:`cursor.fetchone() <django.db.backends.CursorWrapper.fetchone>` or
:meth:`cursor.fetchall() <django.db.backends.CursorWrapper.fetchall>` to return
the resulting rows. For example::
def my_custom_sql(self):
from django.db import connection
cursor = connection.cursor()
cursor.execute("SELECT foo FROM bar WHERE baz = %s", [self.baz])
row = cursor.fetchone()
return row
:class:`connection <django.db.backends.DatabaseWrapper>` and :class:`cursor
<django.db.backends.CursorWrapper>` mostly implement the standard Python
DB-API -- see :pep:`249` -- with the addition of Django's :ref:`transaction
handling <topics-db-transactions>`. If you're not familiar with the Python
DB-API, note that the SQL statement in :meth:`cursor.execute()
<django.db.backends.CursorWrapper.execute>` uses placeholders, ``"%s"``, rather
than adding parameters directly within the SQL. If you use this technique, the
underlying database library will automatically add quotes and escaping to your
parameter(s) as necessary. (Also note that Django expects the ``"%s"``
placeholder, *not* the ``"?"`` placeholder, which is used by the SQLite Python
bindings. This is for the sake of consistency and sanity.)
A final note: If all you want to do is a custom ``WHERE`` clause, you can use
the :meth:`~QuerySet.extra` lookup method, which lets you add custom SQL to a
query.
.. _model-inheritance: .. _model-inheritance:

View File

@ -5,16 +5,28 @@ Performing raw SQL queries
Feel free to write custom SQL statements in custom model methods and Feel free to write custom SQL statements in custom model methods and
module-level methods. The object ``django.db.connection`` represents the module-level methods. The object ``django.db.connection`` represents the
current database connection. To use it, call ``connection.cursor()`` to get a current database connection, and ``django.db.transaction`` represents the
cursor object. Then, call ``cursor.execute(sql, [params])`` to execute the SQL current database transaction. To use the database connection, call
and ``cursor.fetchone()`` or ``cursor.fetchall()`` to return the resulting ``connection.cursor()`` to get a cursor object. Then, call
rows. Example:: ``cursor.execute(sql, [params])`` to execute the SQL and ``cursor.fetchone()``
or ``cursor.fetchall()`` to return the resulting rows. After performing a data
changing operation, you should then call
``transaction.commit_unless_managed()`` to ensure your changes are committed
to the database. If your query is purely a data retrieval operation, no commit
is required. For example::
def my_custom_sql(self): def my_custom_sql(self):
from django.db import connection from django.db import connection, transaction
cursor = connection.cursor() cursor = connection.cursor()
# Data modifying operation - commit required
cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [self.baz])
transaction.commit_unless_managed()
# Data retrieval operation - no commit required
cursor.execute("SELECT foo FROM bar WHERE baz = %s", [self.baz]) cursor.execute("SELECT foo FROM bar WHERE baz = %s", [self.baz])
row = cursor.fetchone() row = cursor.fetchone()
return row return row
``connection`` and ``cursor`` mostly implement the standard `Python DB-API`_ ``connection`` and ``cursor`` mostly implement the standard `Python DB-API`_

View File

@ -10,10 +10,10 @@ if you're using a database that supports transactions.
Django's default transaction behavior Django's default transaction behavior
===================================== =====================================
Django's default behavior is to commit automatically when any built-in, Django's default behavior is to run with an open transaction which it
data-altering model function is called. For example, if you call commits automatically when any built-in, data-altering model function is
``model.save()`` or ``model.delete()``, the change will be committed called. For example, if you call ``model.save()`` or ``model.delete()``, the
immediately. change will be committed immediately.
This is much like the auto-commit setting for most databases. As soon as you This is much like the auto-commit setting for most databases. As soon as you
perform an action that needs to write to the database, Django produces the perform an action that needs to write to the database, Django produces the
@ -165,3 +165,29 @@ they're called. If your MySQL setup *does* support transactions, Django will
handle transactions as explained in this document. handle transactions as explained in this document.
.. _information on MySQL transactions: http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html .. _information on MySQL transactions: http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html
Transactions and savepoints in PostgreSQL 8
===========================================
When a call to a PostgreSQL 8 cursor raises an exception, all subsequent SQL
in the same transaction fails with the error "current transaction is aborted,
queries ignored until end of transaction block". Whilst simple use of save()
is unlikely to raise an exception in PostgreSQL, there are many more advanced
usage patterns which might: for example, saving objects with unique fields,
saving using the force_insert/force_update flag, or invoking custom SQL.
In any of these cases, you can wrap the command which may throw
IntegrityError inside savepoints, which will then allow subsequent commands
to proceed. Example::
try:
sid = transaction.savepoint()
x.save()
transaction.savepoint_commit(sid)
except IntegrityError:
transaction.savepoint_rollback(sid)
raise
Savepoints are not implemented in PostgreSQL 7. If you experience an
IntegrityError when using PostgreSQL 7, you will need to rollback to the
start of the transaction.