Commit 99c87f14 authored by Michael Manfre's avatar Michael Manfre Committed by Anssi Kääriäinen
Browse files

Fixed #17671 - Cursors are now context managers.

parent 04a2a6b0
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
import datetime
import time

from django.db.utils import DatabaseError
from django.db.utils import DatabaseError, ProgrammingError

try:
    from django.utils.six.moves import _thread as thread
@@ -664,6 +664,9 @@ class BaseDatabaseFeatures(object):
    # Does the backend require a connection reset after each material schema change?
    connection_persists_old_columns = False

    # What kind of error does the backend throw when accessing closed cursor?
    closed_cursor_error_class = ProgrammingError

    def __init__(self, connection):
        self.connection = connection

+2 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@ from django.db.backends.postgresql_psycopg2.creation import DatabaseCreation
from django.db.backends.postgresql_psycopg2.version import get_version
from django.db.backends.postgresql_psycopg2.introspection import DatabaseIntrospection
from django.db.backends.postgresql_psycopg2.schema import DatabaseSchemaEditor
from django.db.utils import InterfaceError
from django.utils.encoding import force_str
from django.utils.functional import cached_property
from django.utils.safestring import SafeText, SafeBytes
@@ -60,6 +61,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
    can_rollback_ddl = True
    supports_combined_alters = True
    nulls_order_largest = True
    closed_cursor_error_class = InterfaceError


class DatabaseWrapper(BaseDatabaseWrapper):
+8 −0
Original line number Diff line number Diff line
@@ -36,6 +36,14 @@ class CursorWrapper(object):
    def __iter__(self):
        return iter(self.cursor)

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        # Ticket #17671 - Close instead of passing thru to avoid backend
        # specific behavior.
        self.close()


class CursorDebugWrapper(CursorWrapper):

+19 −0
Original line number Diff line number Diff line
@@ -111,6 +111,25 @@ In addition, the widgets now display a help message when the browser and
server time zone are different, to clarify how the value inserted in the field
will be interpreted.

Using database cursors as context managers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Prior to Python 2.7, database cursors could be used as a context manager. The
specific backend's cursor defined the behavior of the context manager. The
behavior of magic method lookups was changed with Python 2.7 and cursors were
no longer usable as context managers.

Django 1.7 allows a cursor to be used as a context manager that is a shortcut
for the following, instead of backend specific behavior.

.. code-block:: python

    c = connection.cursor()
    try:
        c.execute(...)
    finally:
        c.close()

Minor features
~~~~~~~~~~~~~~

+27 −0
Original line number Diff line number Diff line
@@ -297,3 +297,30 @@ database library will automatically escape your parameters 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.

.. versionchanged:: 1.7

:pep:`249` does not state whether a cursor should be usable as a context
manager. Prior to Python 2.7, a cursor was usable as a context manager due
an unexpected behavior in magic method lookups (`Python ticket #9220`_).
Django 1.7 explicitly added support to allow using a cursor as context
manager.

.. _`Python ticket #9220`: http://bugs.python.org/issue9220

Using a cursor as a context manager:

.. code-block:: python

    with connection.cursor() as c:
        c.execute(...)

is equivalent to:

.. code-block:: python

    c = connection.cursor()
    try:
        c.execute(...)
    finally:
        c.close()
 No newline at end of file
Loading