Ticket #14204: 14204-sqlite-fk-constraints.diff

File 14204-sqlite-fk-constraints.diff, 5.0 KB (added by ramiro, 5 years ago)

First implementation

  • django/db/backends/sqlite3/base.py

    diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py
    a b  
    2424    except ImportError, e1:
    2525        from sqlite3 import dbapi2 as Database
    2626except ImportError, exc:
    27     import sys
    2827    from django.core.exceptions import ImproperlyConfigured
    2928    if sys.version_info < (2, 5, 0):
    3029        module = 'pysqlite2 module'
     
    6261    can_use_chunked_reads = False
    6362
    6463class DatabaseOperations(BaseDatabaseOperations):
     64    def __init__(self, connection):
     65        super(DatabaseOperations, self).__init__()
     66        self.connection = connection
     67
    6568    def date_extract_sql(self, lookup_type, field_name):
    6669        # sqlite doesn't support extract, so we fake it with the user-defined
    6770        # function django_extract that's registered in connect(). Note that
     
    128131        # No field, or the field isn't known to be a decimal or integer
    129132        return value
    130133
     134    def deferrable_sql(self):
     135        """
     136        Returns the SQL necessary to make a constraint "initially deferred"
     137        during a CREATE TABLE statement.
     138        """
     139        if self.connection.supports_fk_constraints:
     140            return " ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED"
     141        return ''
     142
    131143class DatabaseWrapper(BaseDatabaseWrapper):
    132144
    133145    # SQLite requires LIKE statements to include an ESCAPE clause if the value
     
    154166        super(DatabaseWrapper, self).__init__(*args, **kwargs)
    155167
    156168        self.features = DatabaseFeatures()
    157         self.ops = DatabaseOperations()
     169        self.ops = DatabaseOperations(self)
    158170        self.client = DatabaseClient(self)
    159171        self.creation = DatabaseCreation(self)
    160172        self.introspection = DatabaseIntrospection(self)
    161173        self.validation = BaseDatabaseValidation(self)
     174        self.supports_fk_constraints = False
    162175
    163176    def _cursor(self):
     177        new_connection = False
    164178        if self.connection is None:
     179            new_connection = True
    165180            settings_dict = self.settings_dict
    166181            if not settings_dict['NAME']:
    167182                from django.core.exceptions import ImproperlyConfigured
     
    177192            self.connection.create_function("django_date_trunc", 2, _sqlite_date_trunc)
    178193            self.connection.create_function("regexp", 2, _sqlite_regexp)
    179194            connection_created.send(sender=self.__class__, connection=self)
    180         return self.connection.cursor(factory=SQLiteCursorWrapper)
     195        cursor = self.connection.cursor(factory=SQLiteCursorWrapper)
     196        if new_connection:
     197            self.supports_fk_constraints = self._check_fk_constraint_support(cursor)
     198            if self.supports_fk_constraints:
     199                cursor.execute("PRAGMA foreign_keys = ON")
     200        return cursor
    181201
    182202    def close(self):
    183203        # If database is in memory, closing the connection destroys the
     
    186206        if self.settings_dict['NAME'] != ":memory:":
    187207            BaseDatabaseWrapper.close(self)
    188208
     209    def _check_fk_constraint_support(self, cursor):
     210        cursor.execute("PRAGMA foreign_keys")
     211        v = cursor.fetchone()
     212        if v is None:
     213            return False
     214        return True
     215
    189216FORMAT_QMARK_REGEX = re.compile(r'(?![^%])%s')
    190217
    191218class SQLiteCursorWrapper(Database.Cursor):
  • django/db/backends/sqlite3/creation.py

    diff --git a/django/db/backends/sqlite3/creation.py b/django/db/backends/sqlite3/creation.py
    a b  
    6464        if test_database_name and test_database_name != ":memory:":
    6565            # Remove the SQLite database file
    6666            os.remove(test_database_name)
     67
     68    def sql_for_inline_foreign_key_references(self, field, known_models, style):
     69        "Return the SQL snippet defining the foreign key reference for a field"
     70        if self.connection.supports_fk_constraints:
     71            qn = self.connection.ops.quote_name
     72            output = [style.SQL_KEYWORD('REFERENCES') + ' ' + \
     73                style.SQL_TABLE(qn(field.rel.to._meta.db_table)) + ' (' + \
     74                style.SQL_FIELD(qn(field.rel.to._meta.get_field(field.rel.field_name).column)) + ')' +
     75                self.connection.ops.deferrable_sql()
     76            ]
     77            return output, False
     78        return super(DatabaseCreation, self).sql_for_inline_foreign_key_references(field, known_models, style)
     79
     80    def sql_destroy_model(self, model, references_to_delete, style):
     81        "Return the DROP TABLE and restraint dropping statements for a single model"
     82        if self.connection.supports_fk_constraints:
     83            if not model._meta.managed or model._meta.proxy:
     84                return []
     85            # Drop the table now
     86            qn = self.connection.ops.quote_name
     87            output = ['%s %s;' % (style.SQL_KEYWORD('DROP TABLE'),
     88                                  style.SQL_TABLE(qn(model._meta.db_table)))]
     89            return output
     90        return super(DatabaseCreation, self).sql_destroy_model(model, references_to_delete, style)
Back to Top