Permalink
Browse files

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
  • Loading branch information...
freakboy3742 committed May 2, 2009
1 parent e85bc81 commit 87d3ff731bfbedaefa8cb762c92df18e5d234978
Showing with 55 additions and 46 deletions.
  1. +7 −36 docs/topics/db/models.txt
  2. +18 −6 docs/topics/db/sql.txt
  3. +30 −4 docs/topics/db/transactions.txt
@@ -171,25 +171,25 @@ ones:
(u'SR', u'Senior'),
(u'GR', u'Graduate'),
)
The first element in each tuple is the value that will be stored in the
database, the second element will be displayed by the admin interface,
or in a ModelChoiceField. Given an instance of a model object, the
display value for a choices field can be accessed using the
``get_FOO_display`` method. For example::
from django.db import models
class Person(models.Model):
GENDER_CHOICES = (
(u'M', u'Male'),
(u'F', u'Female'),
)
name = models.CharField(max_length=60)
gender = models.CharField(max_length=2, choices=GENDER_CHOICES)
::
>>> p = Person(name="Fred Flinstone", gender="M")
>>> p.save()
>>> p.gender
@@ -752,37 +752,8 @@ Executing custom SQL
--------------------
Another common pattern is writing custom SQL statements in model methods and
module-level methods. The object :class:`django.db.connection
<django.db.backends.DatabaseWrapper>` represents the current database
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.
module-level methods. For more details on using raw SQL, see the documentation
on :ref:`using raw SQL<topics-db-sql>`.
.. _model-inheritance:
@@ -5,16 +5,28 @@ Performing raw SQL queries
Feel free to write custom SQL statements in custom model methods and
module-level methods. The object ``django.db.connection`` represents the
current database connection. To use it, call ``connection.cursor()`` to get a
cursor object. Then, call ``cursor.execute(sql, [params])`` to execute the SQL
and ``cursor.fetchone()`` or ``cursor.fetchall()`` to return the resulting
rows. Example::
current database connection, and ``django.db.transaction`` represents the
current database transaction. To use the database connection, call
``connection.cursor()`` to get a cursor object. Then, call
``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):
from django.db import connection
from django.db import connection, transaction
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])
row = cursor.fetchone()
return row
``connection`` and ``cursor`` mostly implement the standard `Python DB-API`_
@@ -29,7 +41,7 @@ the sake of consistency and sanity.)
A final note: If all you want to do is a custom ``WHERE`` clause, you can just
use the ``where``, ``tables`` and ``params`` arguments to the standard lookup
API.
API.
.. _Python DB-API: http://www.python.org/peps/pep-0249.html
@@ -10,10 +10,10 @@ if you're using a database that supports transactions.
Django's default transaction behavior
=====================================
Django's default behavior is to commit automatically when any built-in,
data-altering model function is called. For example, if you call
``model.save()`` or ``model.delete()``, the change will be committed
immediately.
Django's default behavior is to run with an open transaction which it
commits automatically when any built-in, data-altering model function is
called. For example, if you call ``model.save()`` or ``model.delete()``, the
change will be committed immediately.
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
@@ -165,3 +165,29 @@ they're called. If your MySQL setup *does* support transactions, Django will
handle transactions as explained in this document.
.. _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.

0 comments on commit 87d3ff7

Please sign in to comment.