Opened 17 months ago

Closed 16 months ago

Last modified 16 months ago

#22293 closed Bug (fixed)

ERROR: relation "app_taggedcountryoforigin" already exists

Reported by: blueyed Owned by: nobody
Component: Migrations Version: master
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

There is a problem in db.backends.schema.alter_field, with the call to _alter_many_to_many:

it might try to rename a DB table to the same name.

With django-taggit, changing arguments for the TaggableManager a migration is created, which triggers a RENAME of a DB table, although the name is not changed (e.g. when changing blank).

The migration:

operations = [

migrations.AlterField(

model_name='model',
name='country_of_origin',
field=taggit.managers.TaggableManager(through=app.models.TaggedCountryOfOrigin, blank=True, help_text=u'A comma-separated list of tags.', verbose_name=u'Country of origin'),

),

The SQL:

    % manage.py sqlmigrate app 0010
    ALTER TABLE "app_taggedcountryoforigin" RENAME TO "app_taggedcountryoforigin";
    ALTER TABLE "app_taggedcountryoforigin" DROP CONSTRAINT app__tag_id_5f04ec758472f8f8_fk_app_countryoforigintag_id;
    ALTER TABLE "app_taggedcountryoforigin" ADD CONSTRAINT app_taggedcountryoforigin_tag_id_5f04ec758472f8f8_fk FOREIGN KEY ("tag_id") REFERENCES "app_countryoforigintag" ("id") DEFERRABLE INITIALLY DEFERRED;

The SQL error: ERROR: relation "app_taggedcountryoforigin" already exists

The code path is as follows:

ipdb> l

128 elif isinstance(to_field.rel.to, six.string_types):
129 to_field.rel.to = from_field.rel.to

--> 130 schema_editor.alter_field(from_model, from_field, to_field)

131
132 def database_backwards(self, app_label, schema_editor, from_state, to_state):
133 self.database_forwards(app_label, schema_editor, from_state, to_state)
134
135 def describe(self):
136 return "Alter field %s on %s" % (self.name, self.model_name)
137
138 def eq(self, other):

ipdb> from_model
<class 'fake.Model'>
ipdb> from_field
<taggit.managers.TaggableManager: country_of_origin>
ipdb> to_field
<taggit.managers.TaggableManager: country_of_origin>
ipdb>

…/django-master/django/db/backends/schema.py(767)_alter_many_to_many()

766 # Rename the through table

--> 767 self.alter_db_table(old_field.rel.through, old_field.rel.through._meta.db_table, new_field.rel.through._meta.db_table)

768 # Repoint the FK to the other side

The code that calls it (from database_forwards):

if router.allow_migrate(schema_editor.connection.alias, to_model):

from_field = from_model._meta.get_field_by_name(self.name)[0]
to_field = to_model._meta.get_field_by_name(self.name)[0]
# If the field is a relatedfield with an unresolved rel.to, just
# set it equal to the other field side. Bandaid fix for AlterField
# migrations that are part of a RenameModel change.
if from_field.rel and from_field.rel.to:

if isinstance(from_field.rel.to, six.string_types):

from_field.rel.to = to_field.rel.to

elif isinstance(to_field.rel.to, six.string_types):

to_field.rel.to = from_field.rel.to

schema_editor.alter_field(from_model, from_field, to_field)

(This gets triggered by my patch to taggit (http://github.com/blueyed/django-taggit/compare/rel.through._meta.auto_created-for-1.7?expand=1), where I setup auto_created.

I came up with this in an attempt to fix another taggit issue with Django 1.7 (https://github.com/alex/django-taggit)/issues/211 - "changed through model: ValueError: Cannot alter field ... - they are not compatible types".)

I will provide a fix for this, but did not manage to come with a test case (for the test suite).

I would really appreciate it, if

Change History (4)

comment:1 Changed 17 months ago by blueyed

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset

Sorry for the layout mess (please feel free to fix/edit it).

Here is a proposed fix for it: https://github.com/django/django/pull/2448

comment:2 Changed 17 months ago by aaugustin

  • Triage Stage changed from Unreviewed to Accepted

taggit is known to abuse the internals, but this looks like something Django should prevent.

comment:3 Changed 16 months ago by Tim Graham <timograham@…>

  • Resolution set to fixed
  • Status changed from new to closed

In 1edfa155e34dde549ef0e93414f2846dce59e5f7:

Fixed #22293 -- Avoided renaming many-to-many tables to themselves.

Fixed this for both implementations of _alter_many_to_many, instead of
in alter_db_table itself (more implementations).

comment:4 Changed 16 months ago by Tim Graham <timograham@…>

In 21eaad68e6f26b9f88437c63d584c0a9fb39c9be:

[1.7.x] Fixed #22293 -- Avoided renaming many-to-many tables to themselves.

Fixed this for both implementations of _alter_many_to_many, instead of
in alter_db_table itself (more implementations).

Backport of 1edfa155e3 from master

Note: See TracTickets for help on using tickets.
Back to Top