#26475: Using functools.partial in model field options causes creation of
unnecessary migration on every 'makemigrations' call
----------------------------+--------------------
Reporter: un-def | Owner: nobody
Type: Bug | Status: new
Component: Migrations | Version: 1.9
Severity: Normal | Keywords:
Triage Stage: Unreviewed | Has patch: 0
Easy pickings: 0 | UI/UX: 0
----------------------------+--------------------
Consider this simple model:
{{{
from django.db import models
from functools import partial
def concat(*args):
return ''.join(args)
class TestModel(models.Model):
test_field = models.CharField(
max_length=128,
default=partial(concat, 'foo', 'bar')
)
}}}
Every time you run `./manage.py makemigration` it leads to creation of
migration:
{{{
$ ./manage.py makemigrations
Migrations for 'test_app':
0008_auto_....py:
- Alter field test_field on testmodel
$ ./manage.py makemigrations
Migrations for 'test_app':
0009_auto_....py:
- Alter field test_field on testmodel
$ ./manage.py makemigrations
Migrations for 'test_app':
0010_auto_....py:
- Alter field test_field on testmodel
}}}
All created migrations are equal.
Cause: when project state is rendered from previous migration file,
`functools.partial` instance is created (`migrations.AlterField(...
default=functools.partial(djtest_app.models.concat, *('foo', 'bar'),
**{}), ...)`). After that each model field is deconstructed to dict and
previous and current field states (i.e. dicts) are compared
[https://github.com/django/django/blob/2cd2d188516475ddf256e6267cd82c495fb5c430/django/db/migrations/autodetector.py#L862]
But different instances of `functools.partial` with same arguments are not
equal — I guess that `partial` doesn't implement own `__eq__`/`__ne__`
methods, so comparison is made by object `id()` (memory address), and
false field changes is detected.
Possible solution (workaround): we can use own `partial` subclass in
migration files. Our subclass implements custom `__eq__`/`__ne__` magic
methods:
{{{
class Partial(functools.partial):
def __eq__(self, other):
return (self.func == other.func and
self.args == other.args and
self.keywords == other.keywords)
def __ne__(self, other):
return not self.__eq__(other)
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/26475>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/049.e7f481edc0f24e40c3bb0c2baf100b50%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.