mirror of
https://github.com/django/django.git
synced 2025-10-24 14:16:09 +00:00
Fixed #31777 -- Added support for database collations to Char/TextFields.
Thanks Simon Charette and Mariusz Felisiak for reviews.
This commit is contained in:
committed by
Mariusz Felisiak
parent
ba6b32e5ef
commit
e387f191f7
@@ -185,6 +185,14 @@ class SchemaTests(TransactionTestCase):
|
||||
counts['indexes'] += 1
|
||||
return counts
|
||||
|
||||
def get_column_collation(self, table, column):
|
||||
with connection.cursor() as cursor:
|
||||
return next(
|
||||
f.collation
|
||||
for f in connection.introspection.get_table_description(cursor, table)
|
||||
if f.name == column
|
||||
)
|
||||
|
||||
def assertIndexOrder(self, table, index, order):
|
||||
constraints = self.get_constraints(table)
|
||||
self.assertIn(index, constraints)
|
||||
@@ -3224,3 +3232,147 @@ class SchemaTests(TransactionTestCase):
|
||||
with connection.schema_editor(atomic=True) as editor:
|
||||
editor.alter_db_table(Foo, Foo._meta.db_table, 'renamed_table')
|
||||
Foo._meta.db_table = 'renamed_table'
|
||||
|
||||
@isolate_apps('schema')
|
||||
@skipUnlessDBFeature('supports_collation_on_charfield')
|
||||
def test_db_collation_charfield(self):
|
||||
collation = connection.features.test_collations['non_default']
|
||||
|
||||
class Foo(Model):
|
||||
field = CharField(max_length=255, db_collation=collation)
|
||||
|
||||
class Meta:
|
||||
app_label = 'schema'
|
||||
|
||||
self.isolated_local_models = [Foo]
|
||||
with connection.schema_editor() as editor:
|
||||
editor.create_model(Foo)
|
||||
|
||||
self.assertEqual(
|
||||
self.get_column_collation(Foo._meta.db_table, 'field'),
|
||||
collation,
|
||||
)
|
||||
|
||||
@isolate_apps('schema')
|
||||
@skipUnlessDBFeature('supports_collation_on_textfield')
|
||||
def test_db_collation_textfield(self):
|
||||
collation = connection.features.test_collations['non_default']
|
||||
|
||||
class Foo(Model):
|
||||
field = TextField(db_collation=collation)
|
||||
|
||||
class Meta:
|
||||
app_label = 'schema'
|
||||
|
||||
self.isolated_local_models = [Foo]
|
||||
with connection.schema_editor() as editor:
|
||||
editor.create_model(Foo)
|
||||
|
||||
self.assertEqual(
|
||||
self.get_column_collation(Foo._meta.db_table, 'field'),
|
||||
collation,
|
||||
)
|
||||
|
||||
@skipUnlessDBFeature('supports_collation_on_charfield')
|
||||
def test_add_field_db_collation(self):
|
||||
with connection.schema_editor() as editor:
|
||||
editor.create_model(Author)
|
||||
|
||||
collation = connection.features.test_collations['non_default']
|
||||
new_field = CharField(max_length=255, db_collation=collation)
|
||||
new_field.set_attributes_from_name('alias')
|
||||
with connection.schema_editor() as editor:
|
||||
editor.add_field(Author, new_field)
|
||||
columns = self.column_classes(Author)
|
||||
self.assertEqual(
|
||||
columns['alias'][0],
|
||||
connection.features.introspected_field_types['CharField'],
|
||||
)
|
||||
self.assertEqual(columns['alias'][1][8], collation)
|
||||
|
||||
@skipUnlessDBFeature('supports_collation_on_charfield')
|
||||
def test_alter_field_db_collation(self):
|
||||
with connection.schema_editor() as editor:
|
||||
editor.create_model(Author)
|
||||
|
||||
collation = connection.features.test_collations['non_default']
|
||||
old_field = Author._meta.get_field('name')
|
||||
new_field = CharField(max_length=255, db_collation=collation)
|
||||
new_field.set_attributes_from_name('name')
|
||||
new_field.model = Author
|
||||
with connection.schema_editor() as editor:
|
||||
editor.alter_field(Author, old_field, new_field, strict=True)
|
||||
self.assertEqual(
|
||||
self.get_column_collation(Author._meta.db_table, 'name'),
|
||||
collation,
|
||||
)
|
||||
with connection.schema_editor() as editor:
|
||||
editor.alter_field(Author, new_field, old_field, strict=True)
|
||||
self.assertIsNone(self.get_column_collation(Author._meta.db_table, 'name'))
|
||||
|
||||
@skipUnlessDBFeature('supports_collation_on_charfield')
|
||||
def test_alter_field_type_and_db_collation(self):
|
||||
with connection.schema_editor() as editor:
|
||||
editor.create_model(Note)
|
||||
|
||||
collation = connection.features.test_collations['non_default']
|
||||
old_field = Note._meta.get_field('info')
|
||||
new_field = CharField(max_length=255, db_collation=collation)
|
||||
new_field.set_attributes_from_name('info')
|
||||
new_field.model = Note
|
||||
with connection.schema_editor() as editor:
|
||||
editor.alter_field(Note, old_field, new_field, strict=True)
|
||||
columns = self.column_classes(Note)
|
||||
self.assertEqual(
|
||||
columns['info'][0],
|
||||
connection.features.introspected_field_types['CharField'],
|
||||
)
|
||||
self.assertEqual(columns['info'][1][8], collation)
|
||||
with connection.schema_editor() as editor:
|
||||
editor.alter_field(Note, new_field, old_field, strict=True)
|
||||
columns = self.column_classes(Note)
|
||||
self.assertEqual(columns['info'][0], 'TextField')
|
||||
self.assertIsNone(columns['info'][1][8])
|
||||
|
||||
@skipUnlessDBFeature(
|
||||
'supports_collation_on_charfield',
|
||||
'supports_non_deterministic_collations',
|
||||
)
|
||||
def test_ci_cs_db_collation(self):
|
||||
cs_collation = connection.features.test_collations.get('cs')
|
||||
ci_collation = connection.features.test_collations.get('ci')
|
||||
try:
|
||||
if connection.vendor == 'mysql':
|
||||
cs_collation = 'latin1_general_cs'
|
||||
elif connection.vendor == 'postgresql':
|
||||
cs_collation = 'en-x-icu'
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute(
|
||||
"CREATE COLLATION IF NOT EXISTS case_insensitive "
|
||||
"(provider = icu, locale = 'und-u-ks-level2', "
|
||||
"deterministic = false)"
|
||||
)
|
||||
ci_collation = 'case_insensitive'
|
||||
# Create the table.
|
||||
with connection.schema_editor() as editor:
|
||||
editor.create_model(Author)
|
||||
# Case-insensitive collation.
|
||||
old_field = Author._meta.get_field('name')
|
||||
new_field_ci = CharField(max_length=255, db_collation=ci_collation)
|
||||
new_field_ci.set_attributes_from_name('name')
|
||||
new_field_ci.model = Author
|
||||
with connection.schema_editor() as editor:
|
||||
editor.alter_field(Author, old_field, new_field_ci, strict=True)
|
||||
Author.objects.create(name='ANDREW')
|
||||
self.assertIs(Author.objects.filter(name='Andrew').exists(), True)
|
||||
# Case-sensitive collation.
|
||||
new_field_cs = CharField(max_length=255, db_collation=cs_collation)
|
||||
new_field_cs.set_attributes_from_name('name')
|
||||
new_field_cs.model = Author
|
||||
with connection.schema_editor() as editor:
|
||||
editor.alter_field(Author, new_field_ci, new_field_cs, strict=True)
|
||||
self.assertIs(Author.objects.filter(name='Andrew').exists(), False)
|
||||
finally:
|
||||
if connection.vendor == 'postgresql':
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute('DROP COLLATION IF EXISTS case_insensitive')
|
||||
|
||||
Reference in New Issue
Block a user