mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	[5.0.x] Fixed #34985 -- Fixed GeneratedFields.contribute_to_class() crash when apps are not populated.
Thanks Paolo Melchiorre for the report. Regression inf333e3513e. Backport of101a85a5a0from main
This commit is contained in:
		| @@ -13,7 +13,6 @@ class GeneratedField(Field): | |||||||
|     db_returning = True |     db_returning = True | ||||||
|  |  | ||||||
|     _query = None |     _query = None | ||||||
|     _resolved_expression = None |  | ||||||
|     output_field = None |     output_field = None | ||||||
|  |  | ||||||
|     def __init__(self, *, expression, output_field, db_persist=None, **kwargs): |     def __init__(self, *, expression, output_field, db_persist=None, **kwargs): | ||||||
| @@ -48,9 +47,6 @@ class GeneratedField(Field): | |||||||
|         super().contribute_to_class(*args, **kwargs) |         super().contribute_to_class(*args, **kwargs) | ||||||
|  |  | ||||||
|         self._query = Query(model=self.model, alias_cols=False) |         self._query = Query(model=self.model, alias_cols=False) | ||||||
|         self._resolved_expression = self.expression.resolve_expression( |  | ||||||
|             self._query, allow_joins=False |  | ||||||
|         ) |  | ||||||
|         # Register lookups from the output_field class. |         # Register lookups from the output_field class. | ||||||
|         for lookup_name, lookup in self.output_field.get_class_lookups().items(): |         for lookup_name, lookup in self.output_field.get_class_lookups().items(): | ||||||
|             self.register_lookup(lookup, lookup_name=lookup_name) |             self.register_lookup(lookup, lookup_name=lookup_name) | ||||||
| @@ -59,7 +55,10 @@ class GeneratedField(Field): | |||||||
|         compiler = connection.ops.compiler("SQLCompiler")( |         compiler = connection.ops.compiler("SQLCompiler")( | ||||||
|             self._query, connection=connection, using=None |             self._query, connection=connection, using=None | ||||||
|         ) |         ) | ||||||
|         return compiler.compile(self._resolved_expression) |         resolved_expression = self.expression.resolve_expression( | ||||||
|  |             self._query, allow_joins=False | ||||||
|  |         ) | ||||||
|  |         return compiler.compile(resolved_expression) | ||||||
|  |  | ||||||
|     def check(self, **kwargs): |     def check(self, **kwargs): | ||||||
|         databases = kwargs.get("databases") or [] |         databases = kwargs.get("databases") or [] | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
|  | from django.apps import apps | ||||||
| from django.db import IntegrityError, connection | from django.db import IntegrityError, connection | ||||||
| from django.db.models import ( | from django.db.models import ( | ||||||
|     CharField, |     CharField, | ||||||
| @@ -33,6 +34,25 @@ class BaseGeneratedFieldTests(SimpleTestCase): | |||||||
|                 db_persist=False, |                 db_persist=False, | ||||||
|             ) |             ) | ||||||
|  |  | ||||||
|  |     @isolate_apps("model_fields") | ||||||
|  |     def test_contribute_to_class(self): | ||||||
|  |         class BareModel(Model): | ||||||
|  |             pass | ||||||
|  |  | ||||||
|  |         new_field = GeneratedField( | ||||||
|  |             expression=Lower("nonexistent"), | ||||||
|  |             output_field=IntegerField(), | ||||||
|  |             db_persist=True, | ||||||
|  |         ) | ||||||
|  |         apps.models_ready = False | ||||||
|  |         try: | ||||||
|  |             # GeneratedField can be added to the model even when apps are not | ||||||
|  |             # fully loaded. | ||||||
|  |             new_field.contribute_to_class(BareModel, "name") | ||||||
|  |             self.assertEqual(BareModel._meta.get_field("name"), new_field) | ||||||
|  |         finally: | ||||||
|  |             apps.models_ready = True | ||||||
|  |  | ||||||
|     def test_blank_unsupported(self): |     def test_blank_unsupported(self): | ||||||
|         with self.assertRaisesMessage(ValueError, "GeneratedField must be blank."): |         with self.assertRaisesMessage(ValueError, "GeneratedField must be blank."): | ||||||
|             GeneratedField( |             GeneratedField( | ||||||
| @@ -217,10 +237,6 @@ class GeneratedFieldTestMixin: | |||||||
|         db_parameters = field.db_parameters(connection) |         db_parameters = field.db_parameters(connection) | ||||||
|         self.assertEqual(db_parameters["collation"], collation) |         self.assertEqual(db_parameters["collation"], collation) | ||||||
|         self.assertEqual(db_parameters["type"], field.output_field.db_type(connection)) |         self.assertEqual(db_parameters["type"], field.output_field.db_type(connection)) | ||||||
|         self.assertNotEqual( |  | ||||||
|             db_parameters["type"], |  | ||||||
|             field._resolved_expression.output_field.db_type(connection), |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|     def test_db_type_parameters(self): |     def test_db_type_parameters(self): | ||||||
|         db_type_parameters = self.output_field_db_collation_model._meta.get_field( |         db_type_parameters = self.output_field_db_collation_model._meta.get_field( | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user