#28714: Django Makemigrations don't check if field name from indexes exists on
model before creating a migration.
-------------------------------------+-------------------------------------
Reporter: Gabriel | Owner: nobody
Type: Bug | Status: new
Component: Migrations | Version: 1.11
Severity: Normal | Resolution:
Keywords: indexes, migrate, | Triage Stage:
makemigrations | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Description changed by Gabriel:
Old description:
> When using makemigration with indexes on class Meta in the Model Django
> don't check whether the field name in fields parameter exists on Model.
>
> Because of this both the migration files (in migrations/*.py) and the
> model class on model.py will have a index that referrer to a non existing
> field, so when **manage.py migrate** is run, the expection
> FieldDoesNotExist will be raised, but in this moment the programmer will
> have to edit both the migration file and model class to fix the error,
> especially if is not possible to remove the migrations file or is more
> convenient to edit the migration in projects with many Apps and many
> dependency between those Apps.
>
> Would be more convenient if makemigration before generate the new
> migration files did this verification.
>
> Example of model's code with wrong field name ("''names''") on indexes:
>
> {{{
> class ItemType(models.Model):
> name = models.CharField(_('Name'), unique=True, max_length=255)
>
> class Meta:
> managed = True
> db_table = 'item_type'
> ordering = ['name']
>
> indexes = [
> models.Index(fields=['id'],
> name='item_type_index_1'),
> models.Index(fields=['-id'],
> name='item_type_index_2'),
> models.Index(fields=['names'],
> name='item_type_index_3'),
> models.Index(fields=['-names'],
> name='item_type_index_4')
> ]
> }}}
>
> This code will not raise any exception on **makemigrations**, only the
> generated code on migration file will when **migrate** is used:
>
> {{{
> migrations.AddIndex(
> model_name='itemtype',
> index=models.Index(fields=['names'],
> name='item_type_index_3'),
> ),
> migrations.AddIndex(
> model_name='itemtype',
> index=models.Index(fields=['-names'],
> name='item_type_index_4'),
> ),
> }}}
>
> Traceback (manage.py migrate):
>
> {{{
> Traceback (most recent call last):
> File "D:\Project-2015\cultural\manage.py", line 10, in <module>
> execute_from_command_line(sys.argv)
> File "C:\Python36\lib\site-
> packages\django\core\management\__init__.py", line 364, in
> execute_from_command_line
> utility.execute()
> File "C:\Python36\lib\site-
> packages\django\core\management\__init__.py", line 356, in execute
> self.fetch_command(subcommand).run_from_argv(self.argv)
> File "C:\Python36\lib\site-packages\django\core\management\base.py",
> line 283, in run_from_argv
> self.execute(*args, **cmd_options)
> File "C:\Python36\lib\site-packages\django\core\management\base.py",
> line 330, in execute
> output = self.handle(*args, **options)
> File "C:\Python36\lib\site-
> packages\django\core\management\commands\migrate.py", line 204, in handle
> fake_initial=fake_initial,
> File "C:\Python36\lib\site-packages\django\db\migrations\executor.py",
> line 115, in migrate
> state = self._migrate_all_forwards(state, plan, full_plan, fake=fake,
> fake_initial=fake_initial)
> File "C:\Python36\lib\site-packages\django\db\migrations\executor.py",
> line 145, in _migrate_all_forwards
> state = self.apply_migration(state, migration, fake=fake,
> fake_initial=fake_initial)
> File "C:\Python36\lib\site-packages\django\db\migrations\executor.py",
> line 244, in apply_migration
> state = migration.apply(state, schema_editor)
> File "C:\Python36\lib\site-packages\django\db\migrations\migration.py",
> line 129, in apply
> operation.database_forwards(self.app_label, schema_editor, old_state,
> project_state)
> File "C:\Python36\lib\site-
> packages\django\db\migrations\operations\models.py", line 788, in
> database_forwards
> schema_editor.add_index(model, self.index)
> File "C:\Python36\lib\site-packages\django\db\backends\base\schema.py",
> line 331, in add_index
> self.execute(index.create_sql(model, self))
> File "C:\Python36\lib\site-packages\django\db\models\indexes.py", line
> 65, in create_sql
> sql_parameters = self.get_sql_create_template_values(model,
> schema_editor, using)
> File "C:\Python36\lib\site-packages\django\db\models\indexes.py", line
> 48, in get_sql_create_template_values
> fields = [model._meta.get_field(field_name) for field_name, order in
> self.fields_orders]
> File "C:\Python36\lib\site-packages\django\db\models\indexes.py", line
> 48, in <listcomp>
> fields = [model._meta.get_field(field_name) for field_name, order in
> self.fields_orders]
> File "C:\Python36\lib\site-packages\django\db\models\options.py", line
> 619, in get_field
> raise FieldDoesNotExist("%s has no field named '%s'" %
> (self.object_name, field_name))
> django.core.exceptions.FieldDoesNotExist: ItemType has no field named
> 'names'
> }}}
>
> Attached is this model example with a incorrect index field name that
> will not raise exception on makemigration only on migrate.
>
> This was checked on Django 1.11.5 with Python 3.6.2 on Windows 10 64bits.
>
> To reproduce this error:
> 1. Add the Item App to a existing Django project
> 2. Run manage.py makemigrations item
> 3. Run manage.py migrate
New description:
When using makemigration with indexes on class Meta in the Model Django
don't check whether the field name in fields parameter exists on Model.
Because of this both the migration files (in migrations/*.py) and the
model class on model.py will have a index that referrer to a non existing
field, so when **manage.py migrate** is run, the expection
FieldDoesNotExist will be raised, but in this moment the programmer will
have to edit both the migration file and model class to fix the error,
especially if is not possible to remove the migrations file or is more
convenient to edit the migration in projects with many Apps and many
dependency between those Apps.
Would be more convenient if makemigration before generate the new
migration files did this verification.
Example of model's code with wrong field name ("''names''") on indexes:
{{{
class ItemType(models.Model):
name = models.CharField(_('Name'), unique=True, max_length=255)
class Meta:
managed = True
db_table = 'item_type'
ordering = ['name']
indexes = [
models.Index(fields=['id'],
name='item_type_index_1'),
models.Index(fields=['-id'],
name='item_type_index_2'),
models.Index(fields=['names'],
name='item_type_index_3'),
models.Index(fields=['-names'],
name='item_type_index_4')
]
}}}
This code will not raise any exception on **makemigrations**, only the
generated code on migration file will when **migrate** is used:
{{{
migrations.AddIndex(
model_name='itemtype',
index=models.Index(fields=['names'],
name='item_type_index_3'),
),
migrations.AddIndex(
model_name='itemtype',
index=models.Index(fields=['-names'],
name='item_type_index_4'),
),
}}}
Traceback (manage.py migrate):
{{{
Traceback (most recent call last):
File "D:\Project-2015\cultural\manage.py", line 10, in <module>
execute_from_command_line(sys.argv)
File "C:\Python36\lib\site-packages\django\core\management\__init__.py",
line 364, in execute_from_command_line
utility.execute()
File "C:\Python36\lib\site-packages\django\core\management\__init__.py",
line 356, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "C:\Python36\lib\site-packages\django\core\management\base.py",
line 283, in run_from_argv
self.execute(*args, **cmd_options)
File "C:\Python36\lib\site-packages\django\core\management\base.py",
line 330, in execute
output = self.handle(*args, **options)
File "C:\Python36\lib\site-
packages\django\core\management\commands\migrate.py", line 204, in handle
fake_initial=fake_initial,
File "C:\Python36\lib\site-packages\django\db\migrations\executor.py",
line 115, in migrate
state = self._migrate_all_forwards(state, plan, full_plan, fake=fake,
fake_initial=fake_initial)
File "C:\Python36\lib\site-packages\django\db\migrations\executor.py",
line 145, in _migrate_all_forwards
state = self.apply_migration(state, migration, fake=fake,
fake_initial=fake_initial)
File "C:\Python36\lib\site-packages\django\db\migrations\executor.py",
line 244, in apply_migration
state = migration.apply(state, schema_editor)
File "C:\Python36\lib\site-packages\django\db\migrations\migration.py",
line 129, in apply
operation.database_forwards(self.app_label, schema_editor, old_state,
project_state)
File "C:\Python36\lib\site-
packages\django\db\migrations\operations\models.py", line 788, in
database_forwards
schema_editor.add_index(model, self.index)
File "C:\Python36\lib\site-packages\django\db\backends\base\schema.py",
line 331, in add_index
self.execute(index.create_sql(model, self))
File "C:\Python36\lib\site-packages\django\db\models\indexes.py", line
65, in create_sql
sql_parameters = self.get_sql_create_template_values(model,
schema_editor, using)
File "C:\Python36\lib\site-packages\django\db\models\indexes.py", line
48, in get_sql_create_template_values
fields = [model._meta.get_field(field_name) for field_name, order in
self.fields_orders]
File "C:\Python36\lib\site-packages\django\db\models\indexes.py", line
48, in <listcomp>
fields = [model._meta.get_field(field_name) for field_name, order in
self.fields_orders]
File "C:\Python36\lib\site-packages\django\db\models\options.py", line
619, in get_field
raise FieldDoesNotExist("%s has no field named '%s'" %
(self.object_name, field_name))
django.core.exceptions.FieldDoesNotExist: ItemType has no field named
'names'
}}}
Attached is this model example with a incorrect index field name that will
not raise exception on makemigration only on migrate.
This was checked on Django 1.11.5 with Python 3.6.2 on Windows 10 64bits.
To reproduce this bug:
1. Add the Item App to a existing Django project
2. Run manage.py makemigrations item
3. Run manage.py migrate
--
--
Ticket URL: <https://code.djangoproject.com/ticket/28714#comment:1>
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/066.3446c6b28f38764111c5bae975da6e98%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.