Skip to content

Commit

Permalink
[1.0.X] Fixed #9206 -- Clarified documentation of transaction handlin…
Browse files Browse the repository at this point in the history
…g in raw SQL, and error recovery for Postgres. Thanks to Richard Davies for the suggestion and draft text.

Merge of r10655 from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.0.X@10657 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
freakboy3742 committed May 2, 2009
1 parent 5f730de commit 6e1869c
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 46 deletions.
43 changes: 7 additions & 36 deletions docs/topics/db/models.txt
Expand Up @@ -171,25 +171,25 @@ ones:
(u'SR', u'Senior'), (u'SR', u'Senior'),
(u'GR', u'Graduate'), (u'GR', u'Graduate'),
) )

The first element in each tuple is the value that will be stored in the 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, database, the second element will be displayed by the admin interface,
or in a ModelChoiceField. Given an instance of a model object, the or in a ModelChoiceField. Given an instance of a model object, the
display value for a choices field can be accessed using the display value for a choices field can be accessed using the
``get_FOO_display`` method. For example:: ``get_FOO_display`` method. For example::

from django.db import models from django.db import models

class Person(models.Model): class Person(models.Model):
GENDER_CHOICES = ( GENDER_CHOICES = (
(u'M', u'Male'), (u'M', u'Male'),
(u'F', u'Female'), (u'F', u'Female'),
) )
name = models.CharField(max_length=60) name = models.CharField(max_length=60)
gender = models.CharField(max_length=2, choices=GENDER_CHOICES) gender = models.CharField(max_length=2, choices=GENDER_CHOICES)

:: ::

>>> p = Person(name="Fred Flinstone", gender="M") >>> p = Person(name="Fred Flinstone", gender="M")
>>> p.save() >>> p.save()
>>> p.gender >>> p.gender
Expand Down Expand Up @@ -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:


Expand Down
24 changes: 18 additions & 6 deletions docs/topics/db/sql.txt
Expand Up @@ -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`_
Expand All @@ -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 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 use the ``where``, ``tables`` and ``params`` arguments to the standard lookup
API. API.


.. _Python DB-API: http://www.python.org/peps/pep-0249.html .. _Python DB-API: http://www.python.org/peps/pep-0249.html


34 changes: 30 additions & 4 deletions docs/topics/db/transactions.txt
Expand Up @@ -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
Expand Down Expand Up @@ -163,3 +163,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.

0 comments on commit 6e1869c

Please sign in to comment.