diff --git a/django/db/backends/sqlite3/features.py b/django/db/backends/sqlite3/features.py
index d9ee9c85b8..1198f7e9a1 100644
--- a/django/db/backends/sqlite3/features.py
+++ b/django/db/backends/sqlite3/features.py
@@ -24,7 +24,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
     can_introspect_small_integer_field = True
     supports_transactions = True
     atomic_transactions = False
-    can_rollback_ddl = True
     supports_atomic_references_rename = False
     supports_paramstyle_pyformat = False
     supports_sequence_reset = False
diff --git a/django/db/backends/sqlite3/schema.py b/django/db/backends/sqlite3/schema.py
index ad22d03763..2ae756d144 100644
--- a/django/db/backends/sqlite3/schema.py
+++ b/django/db/backends/sqlite3/schema.py
@@ -88,44 +88,6 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
         else:
             super().alter_db_table(model, old_db_table, new_db_table)
 
-    def alter_field(self, model, old_field, new_field, strict=False):
-        old_field_name = old_field.name
-        table_name = model._meta.db_table
-        _, old_column_name = old_field.get_attname_column()
-        if (new_field.name != old_field_name and
-                self._is_referenced_by_fk_constraint(table_name, old_column_name, ignore_self=True)):
-            if self.connection.in_atomic_block:
-                raise NotSupportedError((
-                    'Renaming the %r.%r column while in a transaction is not '
-                    'supported on SQLite because it would break referential '
-                    'integrity. Try adding `atomic = False` to the Migration class.'
-                ) % (model._meta.db_table, old_field_name))
-            with atomic(self.connection.alias):
-                super().alter_field(model, old_field, new_field, strict=strict)
-                # Follow SQLite's documented procedure for performing changes
-                # that don't affect the on-disk content.
-                # https://sqlite.org/lang_altertable.html#otheralter
-                with self.connection.cursor() as cursor:
-                    schema_version = cursor.execute('PRAGMA schema_version').fetchone()[0]
-                    cursor.execute('PRAGMA writable_schema = 1')
-                    references_template = ' REFERENCES "%s" ("%%s") ' % table_name
-                    new_column_name = new_field.get_attname_column()[1]
-                    search = references_template % old_column_name
-                    replacement = references_template % new_column_name
-                    cursor.execute('UPDATE sqlite_master SET sql = replace(sql, %s, %s)', (search, replacement))
-                    cursor.execute('PRAGMA schema_version = %d' % (schema_version + 1))
-                    cursor.execute('PRAGMA writable_schema = 0')
-                    # The integrity check will raise an exception and rollback
-                    # the transaction if the sqlite_master updates corrupt the
-                    # database.
-                    cursor.execute('PRAGMA integrity_check')
-            # Perform a VACUUM to refresh the database representation from
-            # the sqlite_master table.
-            with self.connection.cursor() as cursor:
-                cursor.execute('VACUUM')
-        else:
-            super().alter_field(model, old_field, new_field, strict=strict)
-
     def _remake_table(self, model, create_field=None, delete_field=None, alter_field=None):
         """
         Shortcut to transform a model from old_model into new_model
@@ -250,6 +212,14 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
             yield
             model._meta.db_table = original_table_name
 
+        schema_rewrite_needed = self._is_referenced_by_fk_constraint(model._meta.db_table, ignore_self=True)
+        if schema_rewrite_needed and self.connection.in_atomic_block:
+            raise NotSupportedError((
+                'Changing the %r table while in a transaction is not '
+                'supported on SQLite because it would break referential '
+                'integrity. Try adding `atomic = False` to the Migration class.'
+            ) % model._meta.db_table)
+
         with altered_table_name(model, model._meta.db_table + "__old"):
             # Rename the old table to make way for the new
             self.alter_db_table(
@@ -271,6 +241,36 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
             # Delete the old table
             self.delete_model(model, handle_autom2m=False)
 
+        # TODO: Should be merged with the above changes
+        #       ... But that causes problems with BaseDatabaseSchemaEditor.execute
+        #           (the transaction check there)
+        if schema_rewrite_needed:
+            with atomic(self.connection.alias):
+                # Follow SQLite's documented procedure for performing changes
+                # that don't affect the on-disk content.
+                # https://sqlite.org/lang_altertable.html#otheralter
+                with self.connection.cursor() as cursor:
+                    # TODO: Is there any reason this doesn't go through BaseDatabaseSchemaEditor.execute
+                    #       to record the queries if sqlmigrate is used? Not that the output would be nice
+                    #       but it would be nearer to the truth
+                    schema_version = cursor.execute('PRAGMA schema_version').fetchone()[0]
+                    cursor.execute('PRAGMA writable_schema = 1')
+                    references_template = ' REFERENCES "%s"'
+                    search = references_template % (model._meta.db_table + "__old")
+                    replacement = references_template % model._meta.db_table
+                    cursor.execute('UPDATE sqlite_master SET sql = replace(sql, %s, %s)', (search, replacement))
+                    cursor.execute('PRAGMA schema_version = %d' % (schema_version + 1))
+                    cursor.execute('PRAGMA writable_schema = 0')
+                    # The integrity check will raise an exception and rollback
+                    # the transaction if the sqlite_master updates corrupt the
+                    # database.
+                    cursor.execute('PRAGMA integrity_check')
+
+            # Perform a VACUUM to refresh the database representation from
+            # the sqlite_master table.
+            with self.connection.cursor() as cursor:
+                cursor.execute('VACUUM')
+
         # Run deferred SQL on correct table
         for sql in self.deferred_sql:
             self.execute(sql)
