Re: [Django] #34716: Class methods from subclasses cannot be used as Field.default. (was: Using subclass method as a callable for a field's default value results in wrong reference in the corresponding migration)

4 views
Skip to first unread message

Django

unread,
Jul 17, 2023, 6:02:02 AM7/17/23
to django-...@googlegroups.com
#34716: Class methods from subclasses cannot be used as Field.default.
---------------------------------+------------------------------------
Reporter: Nicolò Intrieri | Owner: nobody
Type: Bug | Status: new
Component: Migrations | Version: 4.2
Severity: Normal | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
---------------------------------+------------------------------------
Changes (by Mariusz Felisiak):

* stage: Unreviewed => Accepted


Old description:

> {{{
> #!div
>
> Given the following model:
>
> {{{#!python
>
> class Profile(models.Model):
>
> class Capability(models.TextChoices):
> BASIC = ("BASIC", "Basic")
> PROFESSIONAL = ("PROFESSIONAL", "Professional")
>
> @classmethod
> def default(cls) -> list[str]:
> return [cls.BASIC]
>
> capabilities = ArrayField(
> models.CharField(choices=Capability.choices, max_length=30,
> blank=True),
> null=True,
> default=Capability.default
> )
>

> }}}
>
> The resulting migration contained the following:
> {{{#!python
> # ...
> migrations.AddField(
> model_name='profile',
> name='capabilities',
> field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True,
> choices=[('BASIC', 'Basic'), ('PROFESSIONAL', 'Professional')],
> max_length=30), default=appname.models.Capability.default, null=True,
> size=None),
> ),
> # ...
> }}}
> }}}
>

> As you can see, migrations.AddField is passed as argument "default" a
> wrong value "appname.models.Capability.default", which leads to an error
> when trying to migrate. The right value should be
> "appname.models.Profile.Capability.default".

New description:

{{{
#!div

Given the following model:

{{{#!python

class Profile(models.Model):

class Capability(models.TextChoices):
BASIC = ("BASIC", "Basic")
PROFESSIONAL = ("PROFESSIONAL", "Professional")

@classmethod
def default(cls) -> list[str]:
return [cls.BASIC]

capabilities = ArrayField(
models.CharField(choices=Capability.choices, max_length=30,
blank=True),
null=True,
default=Capability.default
)


}}}

The resulting migration contained the following:
{{{#!python
# ...
migrations.AddField(
model_name='profile',
name='capabilities',
field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True,
choices=[('BASIC', 'Basic'), ('PROFESSIONAL', 'Professional')],
max_length=30), default=appname.models.Capability.default, null=True,
size=None),
),
# ...
}}}
}}}


As you can see, migrations.AddField is passed as argument "default" a
wrong value "appname.models.Capability.default", which leads to an error
when trying to migrate. The right value should be
"appname.models.Profile.Capability.default".

--

Comment:

Thanks for the report. It seems that `FunctionTypeSerializer` should use
`__qualname__` instead of `__name__`:
{{{#!diff
diff --git a/django/db/migrations/serializer.py
b/django/db/migrations/serializer.py
index d88cda6e20..06657ebaab 100644
--- a/django/db/migrations/serializer.py
+++ b/django/db/migrations/serializer.py
@@ -168,7 +168,7 @@ class FunctionTypeSerializer(BaseSerializer):
):
klass = self.value.__self__
module = klass.__module__
- return "%s.%s.%s" % (module, klass.__name__,
self.value.__name__), {
+ return "%s.%s.%s" % (module, klass.__qualname__,
self.value.__name__), {
"import %s" % module
}
# Further error checking

}}}

Would you like to prepare a patch? (regression test is required)

--
Ticket URL: <https://code.djangoproject.com/ticket/34716#comment:1>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Reply all
Reply to author
Forward
0 new messages