Code

Ticket #9206: 9206-r9084-raw-sql-docs.diff

File 9206-r9084-raw-sql-docs.diff, 4.9 KB (added by Richard Davies <richard.davies@…>, 6 years ago)

Improve documentation on raw SQL queries

Line 
1Index: docs/topics/db/models.txt
2===================================================================
3--- docs/topics/db/models.txt   (revision 9084)
4+++ docs/topics/db/models.txt   (working copy)
5@@ -725,38 +725,8 @@
6 --------------------
7 
8 Another common pattern is writing custom SQL statements in model methods and
9-module-level methods. The object :class:`django.db.connection
10-<django.db.backends.DatabaseWrapper>` represents the current database
11-connection. To use it, call :meth:`connection.cursor()
12-<django.db.backends.DatabaseWrapper.cursor>` to get a cursor object. Then, call
13-``cursor.execute(sql, [params])`` to execute the SQL and
14-:meth:`cursor.fetchone() <django.db.backends.CursorWrapper.fetchone>` or
15-:meth:`cursor.fetchall() <django.db.backends.CursorWrapper.fetchall>` to return
16-the resulting rows. For example::
17+module-level methods. See :ref:`raw SQL <topic-db-sql>`.
18 
19-    def my_custom_sql(self):
20-        from django.db import connection
21-        cursor = connection.cursor()
22-        cursor.execute("SELECT foo FROM bar WHERE baz = %s", [self.baz])
23-        row = cursor.fetchone()
24-        return row
25-
26-:class:`connection <django.db.backends.DatabaseWrapper>` and :class:`cursor
27-<django.db.backends.CursorWrapper>` mostly implement the standard Python
28-DB-API -- see :pep:`249` -- with the addition of Django's :ref:`transaction
29-handling <topics-db-transactions>`. If you're not familiar with the Python
30-DB-API, note that the SQL statement in :meth:`cursor.execute()
31-<django.db.backends.CursorWrapper.execute>` uses placeholders, ``"%s"``, rather
32-than adding parameters directly within the SQL. If you use this technique, the
33-underlying database library will automatically add quotes and escaping to your
34-parameter(s) as necessary. (Also note that Django expects the ``"%s"``
35-placeholder, *not* the ``"?"`` placeholder, which is used by the SQLite Python
36-bindings. This is for the sake of consistency and sanity.)
37-
38-A final note: If all you want to do is a custom ``WHERE`` clause, you can use
39-the :meth:`~QuerySet.extra` lookup method, which lets you add custom SQL to a
40-query.
41-
42 .. _model-inheritance:
43 
44 Model inheritance
45Index: docs/topics/db/sql.txt
46===================================================================
47--- docs/topics/db/sql.txt      (revision 9084)
48+++ docs/topics/db/sql.txt      (working copy)
49@@ -5,16 +5,20 @@
50 
51 Feel free to write custom SQL statements in custom model methods and
52 module-level methods. The object ``django.db.connection`` represents the
53-current database connection. To use it, call ``connection.cursor()`` to get a
54+current database connection, and ``django.db.transaction`` represents the
55+current database transaction. To use it, call ``connection.cursor()`` to get a
56 cursor object. Then, call ``cursor.execute(sql, [params])`` to execute the SQL
57 and ``cursor.fetchone()`` or ``cursor.fetchall()`` to return the resulting
58-rows. Example::
59+rows, followed by ``transaction.commit_unless_managed()`` to commit the
60+change. Example::
61 
62     def my_custom_sql(self):
63-        from django.db import connection
64+        from django.db import connection, transaction
65         cursor = connection.cursor()
66         cursor.execute("SELECT foo FROM bar WHERE baz = %s", [self.baz])
67         row = cursor.fetchone()
68+        cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [self.baz])
69+        transaction.commit_unless_managed()
70         return row
71 
72 ``connection`` and ``cursor`` mostly implement the standard `Python DB-API`_
73@@ -31,5 +35,37 @@
74 use the ``where``, ``tables`` and ``params`` arguments to the standard lookup
75 API.
76 
77+Transaction handling
78+--------------------
79+
80+Django :ref:`transaction handling <topic-db-transactions>` offers a choice
81+of different behaviors, if you're using a database that supports
82+transactions.
83+
84+Raw SQL users should note that all their commands are actually always run
85+inside a transaction, even when using Django in auto-commit mode. As per the
86+standard `Python DB-API`, all database connections come wrapped inside a
87+transaction. In the default Django auto-commit mode, it simulates
88+auto-commit by regularly committing this underlying transaction, e.g. in
89+``model.save()``.
90+
91+As a result, raw SQL users should call
92+``transaction.commit_unless_managed()`` after ``cursor.execute()``, just as
93+``model.save()`` does internally.
94+
95+Raw SQL users with PostgreSQL should note that once a PostgreSQL raises an
96+exception, all subsequent SQL in the same transaction fails with the error
97+"current transaction is aborted, queries ignored until end of transaction
98+block". As a result, raw SQL users with PostgreSQL should additionally wrap
99+commands that may throw IntegrityError inside savepoints. Example::
100+
101+    try:
102+        sid = transaction.savepoint()
103+        cursor.execute(XXX)
104+        transaction.savepoint_commit(sid)
105+    except IntegrityError:
106+        transaction.savepoint_rollback(sid)
107+        raise
108+
109 .. _Python DB-API: http://www.python.org/peps/pep-0249.html
110