Opened 10 years ago
Closed 10 years ago
#26564 closed Bug (duplicate)
Migration for renaming M2M field intermediate model causes exception
| Reported by: | MohammadJafar MashhadiEbrahim | Owned by: | nobody |
|---|---|---|---|
| Component: | Migrations | Version: | 1.9 |
| Severity: | Normal | Keywords: | |
| Cc: | Triage Stage: | Accepted | |
| Has patch: | no | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description (last modified by )
There was Models Service, Application and ServiceActivation. ServiceActivation is intermediate model between Service and Application. I renamed ServiceActivation to ServiceInstance and theses migrations were generated:
migrations.RenameModel(
old_name='ServiceActivation',
new_name='ServiceInstance',
),
migrations.AlterField(
model_name='application',
name='services',
field=models.ManyToManyField(related_name='user_applications', through='core.ServiceInstance', to='core.Service'),
),
I moved two operations to two separate migration files, the second is dependent on the first to ensure that sequence of running is correct. RenameModel operation runs normally but AlterField does not. Stack trace is as the following:
$ python manage.py migrate
Operations to perform:
Apply all migrations: core
Running migrations:
Applying core.0008_renamed_service_activation_model_2...Traceback (most recent call last):
File "manage.py", line 10, in <module>
execute_from_command_line(sys.argv)
File "/home/mjafar/Documents/project/venv/lib/python3.4/site-packages/django/core/management/__init__.py", line 353, in execute_from_command_line
utility.execute()
File "/home/mjafar/Documents/project/venv/lib/python3.4/site-packages/django/core/management/__init__.py", line 345, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/home/mjafar/Documents/project/venv/lib/python3.4/site-packages/django/core/management/base.py", line 348, in run_from_argv
self.execute(*args, **cmd_options)
File "/home/mjafar/Documents/project/venv/lib/python3.4/site-packages/raven/contrib/django/management/__init__.py", line 41, in new_execute
return original_func(self, *args, **kwargs)
File "/home/mjafar/Documents/project/venv/lib/python3.4/site-packages/django/core/management/base.py", line 399, in execute
output = self.handle(*args, **options)
File "/home/mjafar/Documents/project/venv/lib/python3.4/site-packages/django/core/management/commands/migrate.py", line 200, in handle
executor.migrate(targets, plan, fake=fake, fake_initial=fake_initial)
File "/home/mjafar/Documents/project/venv/lib/python3.4/site-packages/django/db/migrations/executor.py", line 92, in migrate
self._migrate_all_forwards(plan, full_plan, fake=fake, fake_initial=fake_initial)
File "/home/mjafar/Documents/project/venv/lib/python3.4/site-packages/django/db/migrations/executor.py", line 121, in _migrate_all_forwards
state = self.apply_migration(state, migration, fake=fake, fake_initial=fake_initial)
File "/home/mjafar/Documents/project/venv/lib/python3.4/site-packages/django/db/migrations/executor.py", line 198, in apply_migration
state = migration.apply(state, schema_editor)
File "/home/mjafar/Documents/project/venv/lib/python3.4/site-packages/django/db/migrations/migration.py", line 123, in apply
operation.database_forwards(self.app_label, schema_editor, old_state, project_state)
File "/home/mjafar/Documents/project/venv/lib/python3.4/site-packages/django/db/migrations/operations/fields.py", line 201, in database_forwards
schema_editor.alter_field(from_model, from_field, to_field)
File "/home/mjafar/Documents/project/venv/lib/python3.4/site-packages/django/db/backends/base/schema.py", line 465, in alter_field
old_field.remote_field.through._meta.auto_created and
AttributeError: 'str' object has no attribute '_meta'
I read the code in schema.py around the line number that exception occuress, hoping to find a workaround but unfortunately i couldn't. Updated to Django 1.9.5 but bug still exists.
Change History (7)
comment:1 by , 10 years ago
| Description: | modified (diff) |
|---|
comment:2 by , 10 years ago
comment:3 by , 10 years ago
Could you please give a complete set of minimal models to reproduce it? #22931 seems to describe the same problem at a high level, although the exception is different (if it is the same issue, the exception may have changed since 1.7).
comment:4 by , 10 years ago
Initial state of models:
from django.db import models
class ModelA(models.Model):
field1 = models.CharField(max_length=5)
set_of_b = models.ManyToManyField('ModelB', through='OldNamedThrough')
class ModelB(models.Model):
field1 = models.CharField(max_length=6)
field2 = models.IntegerField()
class OldNamedThrough(models.Model):
a_ref = models.ForeignKey(ModelA)
b_ref = models.ForeignKey(ModelB)
extra_param = models.IntegerField()
and after changes:
class ModelA(models.Model):
field1 = models.CharField(max_length=5)
set_of_b = models.ManyToManyField('ModelB', through='NewNamedThrough')
class ModelB(models.Model):
field1 = models.CharField(max_length=6)
field2 = models.IntegerField()
class NewNamedThrough(models.Model):
a_ref = models.ForeignKey(ModelA)
b_ref = models.ForeignKey(ModelB)
extra_param = models.IntegerField()
This is the generated migration:
operations = [
migrations.RenameModel(
old_name='OldNamedThrough',
new_name='NewNamedThrough',
),
migrations.AlterField(
model_name='modela',
name='set_of_b',
field=models.ManyToManyField(through='main_app.NewNamedThrough', to='main_app.ModelB'),
),
]
And the exception is thrown at migration time. I think this is not about how the migration is generated and the operations themselves, it's about how they're applied.
As i mentioned above, in the proccess of applying AlterField operation, in line 465 of django/db/backends/base/schema.py code tries to access _meta attribute of old_field.remote_field.through but in this model, "through" model name is given as a string and is not resolved to the class itself yet, so it causes that exception.
comment:5 by , 10 years ago
| Triage Stage: | Unreviewed → Accepted |
|---|
Perfect. Reproduced at 3b383085fb89a48e756383e7cd5d3bd867353ba1, thanks!
comment:7 by , 10 years ago
| Resolution: | → duplicate |
|---|---|
| Status: | new → closed |
I checked
type(old_field.remote_field.through)and it is a string.That's because in models i used class name as a string not pointer to class name. I mean i defined through filed like this:
services = models.ManyToManyField('Service', through='ServiceActivation')not like this:
services = models.ManyToManyField('Service', through=ServiceActivation)The code checks
_metaonold_field.remote_field.throughfield, since it's still a string and is not resolved to corresponding class it raises exception above (AttributeError: 'str' object has no attribute '_meta').Since this is a no-op migration i can just remove the migration without breaking my website as a workaround, But i think this definitely is a bug in migrations not something that just happened to me.