Commit 75303b01 authored by Tim Graham's avatar Tim Graham
Browse files

Fixed #24245 -- Added introspection for database defaults.

Needed for tests for migrations handling of database defaults.
parent 64a899dc
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -125,6 +125,9 @@ class BaseDatabaseFeatures(object):
    # This is True for all core backends.
    can_introspect_null = True

    # Can the backend introspect the default value of a column?
    can_introspect_default = True

    # Confirm support for introspected foreign keys
    # Every database can do this reliably, except MySQL,
    # which can't do it for MyISAM tables
+6 −5
Original line number Diff line number Diff line
@@ -9,8 +9,8 @@ from django.utils.encoding import force_text

from MySQLdb.constants import FIELD_TYPE

FieldInfo = namedtuple('FieldInfo', FieldInfo._fields + ('extra',))

FieldInfo = namedtuple('FieldInfo', FieldInfo._fields + ('extra', 'default'))
InfoLine = namedtuple('InfoLine', 'col_name data_type max_len num_prec num_scale extra column_default')
foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)")


@@ -61,9 +61,9 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
        #   not visible length (#5725)
        # - precision and scale (for decimal fields) (#5014)
        # - auto_increment is not available in cursor.description
        InfoLine = namedtuple('InfoLine', 'col_name data_type max_len num_prec num_scale extra')
        cursor.execute("""
            SELECT column_name, data_type, character_maximum_length, numeric_precision, numeric_scale, extra
            SELECT column_name, data_type, character_maximum_length, numeric_precision,
                   numeric_scale, extra, column_default
            FROM information_schema.columns
            WHERE table_name = %s AND table_schema = DATABASE()""", [table_name])
        field_info = {line[0]: InfoLine(*line) for line in cursor.fetchall()}
@@ -80,7 +80,8 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
                               to_int(field_info[col_name].num_prec) or line[4],
                               to_int(field_info[col_name].num_scale) or line[5])
                            + (line[6],)
                            + (field_info[col_name].extra,)))
                            + (field_info[col_name].extra,)
                            + (field_info[col_name].column_default,)))
            )
        return fields

+1 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
    has_bulk_insert = True
    supports_tablespaces = True
    supports_sequence_reset = False
    can_introspect_default = False  # Pending implementation by an interested person.
    can_introspect_max_length = False
    can_introspect_time_field = False
    atomic_transactions = False
+23 −9
Original line number Diff line number Diff line
import re
from collections import namedtuple

from django.db.backends.base.introspection import (
    BaseDatabaseIntrospection, FieldInfo, TableInfo,
@@ -6,6 +7,7 @@ from django.db.backends.base.introspection import (


field_size_re = re.compile(r'^\s*(?:var)?char\s*\(\s*(\d+)\s*\)\s*$')
FieldInfo = namedtuple('FieldInfo', FieldInfo._fields + ('default',))


def get_field_size(name):
@@ -69,8 +71,18 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):

    def get_table_description(self, cursor, table_name):
        "Returns a description of the table, with the DB-API cursor.description interface."
        return [FieldInfo(info['name'], info['type'], None, info['size'], None, None,
                 info['null_ok']) for info in self._table_info(cursor, table_name)]
        return [
            FieldInfo(
                info['name'],
                info['type'],
                None,
                info['size'],
                None,
                None,
                info['null_ok'],
                info['default'],
            ) for info in self._table_info(cursor, table_name)
        ]

    def column_name_converter(self, name):
        """
@@ -211,12 +223,14 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):

    def _table_info(self, cursor, name):
        cursor.execute('PRAGMA table_info(%s)' % self.connection.ops.quote_name(name))
        # cid, name, type, notnull, dflt_value, pk
        return [{'name': field[1],
        # cid, name, type, notnull, default_value, pk
        return [{
            'name': field[1],
            'type': field[2],
            'size': get_field_size(field[2]),
            'null_ok': not field[3],
                 'pk': field[5]     # undocumented
            'default': field[4],
            'pk': field[5],  # undocumented
        } for field in cursor.fetchall()]

    def get_constraints(self, cursor, table_name):
+9 −0
Original line number Diff line number Diff line
@@ -165,6 +165,15 @@ Backwards incompatible changes in 1.9
    deprecation timeline for a given feature, its removal may appear as a
    backwards incompatible change.

Database backend API
~~~~~~~~~~~~~~~~~~~~

* A couple of new tests rely on the ability of the backend to introspect column
  defaults (returning the result as ``Field.default``). You can set the
  ``can_introspect_default`` database feature to ``False`` if your backend
  doesn't implement this. You may want to review the implementation on the
  backends that Django includes for reference (:ticket:`24245`).

Miscellaneous
~~~~~~~~~~~~~

Loading