From 101a85a5a06585ba16ecb25860146d034a8a55ec Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Wed, 22 Nov 2023 13:41:32 +0100 Subject: [PATCH] Fixed #34985 -- Fixed GeneratedFields.contribute_to_class() crash when apps are not populated. Thanks Paolo Melchiorre for the report. Regression in f333e3513e8bdf5ffeb6eeb63021c230082e6f95. --- django/db/models/fields/generated.py | 9 ++++----- tests/model_fields/test_generatedfield.py | 24 +++++++++++++++++++---- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/django/db/models/fields/generated.py b/django/db/models/fields/generated.py index 9a73b7fe37..95d19582de 100644 --- a/django/db/models/fields/generated.py +++ b/django/db/models/fields/generated.py @@ -13,7 +13,6 @@ class GeneratedField(Field): db_returning = True _query = None - _resolved_expression = None output_field = None def __init__(self, *, expression, output_field, db_persist=None, **kwargs): @@ -48,9 +47,6 @@ class GeneratedField(Field): super().contribute_to_class(*args, **kwargs) 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. for lookup_name, lookup in self.output_field.get_class_lookups().items(): self.register_lookup(lookup, lookup_name=lookup_name) @@ -59,7 +55,10 @@ class GeneratedField(Field): compiler = connection.ops.compiler("SQLCompiler")( 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): databases = kwargs.get("databases") or [] diff --git a/tests/model_fields/test_generatedfield.py b/tests/model_fields/test_generatedfield.py index 04d52f6799..9e5d9d87c3 100644 --- a/tests/model_fields/test_generatedfield.py +++ b/tests/model_fields/test_generatedfield.py @@ -1,3 +1,4 @@ +from django.apps import apps from django.db import IntegrityError, connection from django.db.models import ( CharField, @@ -33,6 +34,25 @@ class BaseGeneratedFieldTests(SimpleTestCase): 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): with self.assertRaisesMessage(ValueError, "GeneratedField must be blank."): GeneratedField( @@ -217,10 +237,6 @@ class GeneratedFieldTestMixin: db_parameters = field.db_parameters(connection) self.assertEqual(db_parameters["collation"], collation) 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): db_type_parameters = self.output_field_db_collation_model._meta.get_field(