mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Fixed #30613 -- Moved index name validation to system checks.
This commit is contained in:
		| @@ -1567,9 +1567,32 @@ class Model(metaclass=ModelBase): | ||||
|  | ||||
|     @classmethod | ||||
|     def _check_indexes(cls): | ||||
|         """Check the fields of indexes.""" | ||||
|         """Check the fields and names of indexes.""" | ||||
|         errors = [] | ||||
|         for index in cls._meta.indexes: | ||||
|             # Index name can't start with an underscore or a number, restricted | ||||
|             # for cross-database compatibility with Oracle. | ||||
|             if index.name[0] == '_' or index.name[0].isdigit(): | ||||
|                 errors.append( | ||||
|                     checks.Error( | ||||
|                         "The index name '%s' cannot start with an underscore " | ||||
|                         "or a number." % index.name, | ||||
|                         obj=cls, | ||||
|                         id='models.E033', | ||||
|                     ), | ||||
|                 ) | ||||
|             if len(index.name) > index.max_name_length: | ||||
|                 errors.append( | ||||
|                     checks.Error( | ||||
|                         "The index name '%s' cannot be longer than %d " | ||||
|                         "characters." % (index.name, index.max_name_length), | ||||
|                         obj=cls, | ||||
|                         id='models.E034', | ||||
|                     ), | ||||
|                 ) | ||||
|         fields = [field for index in cls._meta.indexes for field, _ in index.fields_orders] | ||||
|         return cls._check_local_fields(fields, 'indexes') | ||||
|         errors.extend(cls._check_local_fields(fields, 'indexes')) | ||||
|         return errors | ||||
|  | ||||
|     @classmethod | ||||
|     def _check_local_fields(cls, fields, option): | ||||
|   | ||||
| @@ -33,28 +33,10 @@ class Index: | ||||
|             for field_name in self.fields | ||||
|         ] | ||||
|         self.name = name or '' | ||||
|         if self.name: | ||||
|             errors = self.check_name() | ||||
|             if len(self.name) > self.max_name_length: | ||||
|                 errors.append('Index names cannot be longer than %s characters.' % self.max_name_length) | ||||
|             if errors: | ||||
|                 raise ValueError(errors) | ||||
|         self.db_tablespace = db_tablespace | ||||
|         self.opclasses = opclasses | ||||
|         self.condition = condition | ||||
|  | ||||
|     def check_name(self): | ||||
|         errors = [] | ||||
|         # Name can't start with an underscore on Oracle; prepend D if needed. | ||||
|         if self.name[0] == '_': | ||||
|             errors.append('Index names cannot start with an underscore (_).') | ||||
|             self.name = 'D%s' % self.name[1:] | ||||
|         # Name can't start with a number on Oracle; prepend D if needed. | ||||
|         elif self.name[0].isdigit(): | ||||
|             errors.append('Index names cannot start with a number (0-9).') | ||||
|             self.name = 'D%s' % self.name[1:] | ||||
|         return errors | ||||
|  | ||||
|     def _get_condition_sql(self, model, schema_editor): | ||||
|         if self.condition is None: | ||||
|             return None | ||||
| @@ -122,7 +104,8 @@ class Index: | ||||
|             'Index too long for multiple database support. Is self.suffix ' | ||||
|             'longer than 3 characters?' | ||||
|         ) | ||||
|         self.check_name() | ||||
|         if self.name[0] == '_' or self.name[0].isdigit(): | ||||
|             self.name = 'D%s' % self.name[1:] | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return "<%s: fields='%s'%s>" % ( | ||||
|   | ||||
| @@ -313,6 +313,10 @@ Models | ||||
|   ``<model>``. | ||||
| * **models.E032**: constraint name ``<constraint>`` is not unique amongst | ||||
|   models: ``<model list>``. | ||||
| * **models.E033**: The index name ``<index>`` cannot start with an underscore | ||||
|   or a number. | ||||
| * **models.E034**: The index name ``<index>`` cannot be longer than | ||||
|   ``<max_length>`` characters. | ||||
|  | ||||
| Security | ||||
| -------- | ||||
|   | ||||
| @@ -296,6 +296,39 @@ class IndexesTests(SimpleTestCase): | ||||
|  | ||||
|         self.assertEqual(Bar.check(), []) | ||||
|  | ||||
|     def test_name_constraints(self): | ||||
|         class Model(models.Model): | ||||
|             class Meta: | ||||
|                 indexes = [ | ||||
|                     models.Index(fields=['id'], name='_index_name'), | ||||
|                     models.Index(fields=['id'], name='5index_name'), | ||||
|                 ] | ||||
|  | ||||
|         self.assertEqual(Model.check(), [ | ||||
|             Error( | ||||
|                 "The index name '%sindex_name' cannot start with an " | ||||
|                 "underscore or a number." % prefix, | ||||
|                 obj=Model, | ||||
|                 id='models.E033', | ||||
|             ) for prefix in ('_', '5') | ||||
|         ]) | ||||
|  | ||||
|     def test_max_name_length(self): | ||||
|         index_name = 'x' * 31 | ||||
|  | ||||
|         class Model(models.Model): | ||||
|             class Meta: | ||||
|                 indexes = [models.Index(fields=['id'], name=index_name)] | ||||
|  | ||||
|         self.assertEqual(Model.check(), [ | ||||
|             Error( | ||||
|                 "The index name '%s' cannot be longer than 30 characters." | ||||
|                 % index_name, | ||||
|                 obj=Model, | ||||
|                 id='models.E034', | ||||
|             ), | ||||
|         ]) | ||||
|  | ||||
|  | ||||
| @isolate_apps('invalid_models_tests') | ||||
| class FieldNamesTests(SimpleTestCase): | ||||
|   | ||||
| @@ -63,20 +63,6 @@ class SimpleIndexesTests(SimpleTestCase): | ||||
|         with self.assertRaisesMessage(ValueError, 'Index.condition must be a Q instance.'): | ||||
|             models.Index(condition='invalid', name='long_book_idx') | ||||
|  | ||||
|     def test_max_name_length(self): | ||||
|         msg = 'Index names cannot be longer than 30 characters.' | ||||
|         with self.assertRaisesMessage(ValueError, msg): | ||||
|             models.Index(fields=['title'], name='looooooooooooong_index_name_idx') | ||||
|  | ||||
|     def test_name_constraints(self): | ||||
|         msg = 'Index names cannot start with an underscore (_).' | ||||
|         with self.assertRaisesMessage(ValueError, msg): | ||||
|             models.Index(fields=['title'], name='_name_starting_with_underscore') | ||||
|  | ||||
|         msg = 'Index names cannot start with a number (0-9).' | ||||
|         with self.assertRaisesMessage(ValueError, msg): | ||||
|             models.Index(fields=['title'], name='5name_starting_with_number') | ||||
|  | ||||
|     def test_name_auto_generation(self): | ||||
|         index = models.Index(fields=['author']) | ||||
|         index.set_name_with_model(Book) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user