mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Fixed #26648 -- Added a system check for invalid related_query_name's containing underscores.
This commit is contained in:
		
				
					committed by
					
						 Tim Graham
						Tim Graham
					
				
			
			
				
	
			
			
			
						parent
						
							effb4ed6f5
						
					
				
				
					commit
					686a593aaa
				
			| @@ -9,6 +9,7 @@ from django.core import checks, exceptions | ||||
| from django.db import connection, router | ||||
| from django.db.backends import utils | ||||
| from django.db.models import Q | ||||
| from django.db.models.constants import LOOKUP_SEP | ||||
| from django.db.models.deletion import CASCADE, SET_DEFAULT, SET_NULL | ||||
| from django.db.models.query_utils import PathInfo | ||||
| from django.db.models.utils import make_model_tuple | ||||
| @@ -115,6 +116,7 @@ class RelatedField(Field): | ||||
|     def check(self, **kwargs): | ||||
|         errors = super(RelatedField, self).check(**kwargs) | ||||
|         errors.extend(self._check_related_name_is_valid()) | ||||
|         errors.extend(self._check_related_query_name_is_valid()) | ||||
|         errors.extend(self._check_relation_model_exists()) | ||||
|         errors.extend(self._check_referencing_to_swapped_model()) | ||||
|         errors.extend(self._check_clashes()) | ||||
| @@ -148,6 +150,35 @@ class RelatedField(Field): | ||||
|             ] | ||||
|         return [] | ||||
|  | ||||
|     def _check_related_query_name_is_valid(self): | ||||
|         if self.remote_field.is_hidden(): | ||||
|             return [] | ||||
|         rel_query_name = self.related_query_name() | ||||
|         errors = [] | ||||
|         if rel_query_name.endswith('_'): | ||||
|             errors.append( | ||||
|                 checks.Error( | ||||
|                     "Reverse query name '%s' must not end with an underscore." | ||||
|                     % (rel_query_name,), | ||||
|                     hint=("Add or change a related_name or related_query_name " | ||||
|                           "argument for this field."), | ||||
|                     obj=self, | ||||
|                     id='fields.E308', | ||||
|                 ) | ||||
|             ) | ||||
|         if LOOKUP_SEP in rel_query_name: | ||||
|             errors.append( | ||||
|                 checks.Error( | ||||
|                     "Reverse query name '%s' must not contain '%s'." | ||||
|                     % (rel_query_name, LOOKUP_SEP), | ||||
|                     hint=("Add or change a related_name or related_query_name " | ||||
|                           "argument for this field."), | ||||
|                     obj=self, | ||||
|                     id='fields.E309', | ||||
|                 ) | ||||
|             ) | ||||
|         return errors | ||||
|  | ||||
|     def _check_relation_model_exists(self): | ||||
|         rel_is_missing = self.remote_field.model not in self.opts.apps.get_models() | ||||
|         rel_is_string = isinstance(self.remote_field.model, six.string_types) | ||||
|   | ||||
| @@ -206,6 +206,10 @@ Related Fields | ||||
| * **fields.E307**: The field ``<app label>.<model>.<field name>`` was declared | ||||
|   with a lazy reference to ``<app label>.<model>``, but app ``<app label>`` | ||||
|   isn't installed or doesn't provide model ``<model>``. | ||||
| * **fields.E308**: Reverse query name ``<related query name>`` must not end | ||||
|   with an underscore. | ||||
| * **fields.E309**: Reverse query name ``<related query name>`` must not contain | ||||
|   ``'__'``. | ||||
| * **fields.E310**: No subset of the fields ``<field1>``, ``<field2>``, ... on | ||||
|   model ``<model>`` is unique. Add ``unique=True`` on any of those fields or | ||||
|   add at least a subset of them to a unique_together constraint. | ||||
|   | ||||
| @@ -714,7 +714,7 @@ class RelativeFieldTests(SimpleTestCase): | ||||
|             pass | ||||
|  | ||||
|         for invalid_related_name in invalid_related_names: | ||||
|             Child = type(str('Child_%s') % str(invalid_related_name), (models.Model,), { | ||||
|             Child = type(str('Child%s') % str(invalid_related_name), (models.Model,), { | ||||
|                 'parent': models.ForeignKey('Parent', models.CASCADE, related_name=invalid_related_name), | ||||
|                 '__module__': Parent.__module__, | ||||
|             }) | ||||
| @@ -723,7 +723,7 @@ class RelativeFieldTests(SimpleTestCase): | ||||
|             errors = Child.check() | ||||
|             expected = [ | ||||
|                 Error( | ||||
|                     "The name '%s' is invalid related_name for field Child_%s.parent" | ||||
|                     "The name '%s' is invalid related_name for field Child%s.parent" | ||||
|                     % (invalid_related_name, invalid_related_name), | ||||
|                     hint="Related name must be a valid Python identifier or end with a '+'", | ||||
|                     obj=field, | ||||
| @@ -743,7 +743,6 @@ class RelativeFieldTests(SimpleTestCase): | ||||
|             '_starts_with_underscore', | ||||
|             'contains_%s_digit' % digit, | ||||
|             'ends_with_plus+', | ||||
|             '_', | ||||
|             '_+', | ||||
|             '+', | ||||
|         ] | ||||
| @@ -813,6 +812,32 @@ class RelativeFieldTests(SimpleTestCase): | ||||
|             ), | ||||
|         ]) | ||||
|  | ||||
|     def test_invalid_related_query_name(self): | ||||
|         class Target(models.Model): | ||||
|             pass | ||||
|  | ||||
|         class Model(models.Model): | ||||
|             first = models.ForeignKey(Target, models.CASCADE, related_name='contains__double') | ||||
|             second = models.ForeignKey(Target, models.CASCADE, related_query_name='ends_underscore_') | ||||
|  | ||||
|         self.assertEqual(Model.check(), [ | ||||
|             Error( | ||||
|                 "Reverse query name 'contains__double' must not contain '__'.", | ||||
|                 hint=("Add or change a related_name or related_query_name " | ||||
|                       "argument for this field."), | ||||
|                 obj=Model._meta.get_field('first'), | ||||
|                 id='fields.E309', | ||||
|             ), | ||||
|             Error( | ||||
|                 "Reverse query name 'ends_underscore_' must not end with an " | ||||
|                 "underscore.", | ||||
|                 hint=("Add or change a related_name or related_query_name " | ||||
|                       "argument for this field."), | ||||
|                 obj=Model._meta.get_field('second'), | ||||
|                 id='fields.E308', | ||||
|             ), | ||||
|         ]) | ||||
|  | ||||
|  | ||||
| @isolate_apps('invalid_models_tests') | ||||
| class AccessorClashTests(SimpleTestCase): | ||||
|   | ||||
		Reference in New Issue
	
	Block a user