diff --git a/django/db/backends/schema.py b/django/db/backends/schema.py index e33956763c17b..5ac8d51a3da7a 100644 --- a/django/db/backends/schema.py +++ b/django/db/backends/schema.py @@ -227,7 +227,7 @@ def create_model(self, model): }) # Make the table sql = self.sql_create_table % { - "table": model._meta.db_table, + "table": self.quote_name(model._meta.db_table), "definition": ", ".join(column_sqls) } self.execute(sql, params) diff --git a/tests/schema/models.py b/tests/schema/models.py index 44fecf7ebd6bd..c4c8225970491 100644 --- a/tests/schema/models.py +++ b/tests/schema/models.py @@ -1,5 +1,6 @@ from django.apps.registry import Apps from django.db import models +from django.utils.encoding import python_2_unicode_compatible # Because we want to test creation and deletion of these as separate things, # these models are all inserted into a separate Apps so the main test @@ -102,3 +103,15 @@ class BookWithLongName(models.Model): class Meta: apps = new_apps + + +# Based on tests/reserved_names/models.py +@python_2_unicode_compatible +class Thing(models.Model): + when = models.CharField(max_length=1, primary_key=True) + + class Meta: + db_table = 'select' + + def __str__(self): + return self.when diff --git a/tests/schema/tests.py b/tests/schema/tests.py index 66450956efacf..e0000e2b9dbc6 100644 --- a/tests/schema/tests.py +++ b/tests/schema/tests.py @@ -3,13 +3,13 @@ import unittest from django.test import TransactionTestCase -from django.db import connection, DatabaseError, IntegrityError +from django.db import connection, DatabaseError, IntegrityError, OperationalError from django.db.models.fields import IntegerField, TextField, CharField, SlugField from django.db.models.fields.related import ManyToManyField, ForeignKey from django.db.transaction import atomic from .models import (Author, AuthorWithM2M, Book, BookWithLongName, BookWithSlug, BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename, - UniqueTest) + UniqueTest, Thing) class SchemaTests(TransactionTestCase): @@ -26,6 +26,7 @@ class SchemaTests(TransactionTestCase): models = [ Author, AuthorWithM2M, Book, BookWithLongName, BookWithSlug, BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename, UniqueTest, + Thing ] # Utility functions @@ -683,3 +684,26 @@ def test_foreign_key_index_long_names_regression(self): column_name, connection.introspection.get_indexes(connection.cursor(), BookWithLongName._meta.db_table), ) + + def test_creation_deletion_reserved_names(self): + """ + Tries creating a model's table, and then deleting it when it has a + SQL reserved name. + """ + # Create the table + with connection.schema_editor() as editor: + try: + editor.create_model(Thing) + except OperationalError as e: + self.fail("Errors when applying initial migration for a model " + "with a table named after a SQL reserved word: %s" % e) + # Check that it's there + list(Thing.objects.all()) + # Clean up that table + with connection.schema_editor() as editor: + editor.delete_model(Thing) + # Check that it's gone + self.assertRaises( + DatabaseError, + lambda: list(Thing.objects.all()), + )