# Buggy code
def change_foreign_keys(
    apps, schema_editor, from_app, from_model_name, to_app, to_model_name
):
    from django.db import models

    FromModel = apps.get_model(from_app, from_model_name)
    ToModel = apps.get_model(to_app, to_model_name)

    fields = FromModel._meta.get_fields(include_hidden=True)

    for rel in fields:
        if not hasattr(rel, "field") or not isinstance(rel.field, models.ForeignKey):
            continue
        fk_field = rel.field

        f_name, f_field_name, pos_args, kw_args = fk_field.deconstruct()

        # fk_field might have been the old or new one. We need to fix things up.
        old_field_kwargs = kw_args.copy()
        old_field_kwargs["to"] = FromModel
        old_field = fk_field.__class__(*pos_args, **old_field_kwargs)
        old_field.model = fk_field.model

        new_field_kwargs = kw_args.copy()
        new_field_kwargs["to"] = ToModel
        new_field = fk_field.__class__(*pos_args, **new_field_kwargs)
        new_field.model = fk_field.model

        if fk_field.model._meta.auto_created:
            # If this is a FK that is part of an M2M on the model itself,
            # we've already dealt with this, by virtue of the data migration
            # that populates the auto-created M2M field.
            if fk_field.model._meta.auto_created in [ToModel, FromModel]:
                continue

            # In this case (FK fields that are part of an autogenerated M2M),
            # the column name in the new M2M might be different to that in the
            # old M2M. This makes things much harder, and involves duplicating
            # some Django logic.

            # Build a correct ForeignKey field, as it should
            # have been on FromModel
            old_field.name = from_model_name.lower()
            old_field.column = "{0}_id".format(old_field.name)

            # build a correct ForeignKey field, as it should
            # be on ToModel
            new_field.name = to_model_name.lower()
            new_field.column = "{0}_id".format(new_field.name)
        else:
            old_field.name = fk_field.name
            old_field.column = fk_field.column
            new_field.name = fk_field.name
            new_field.column = fk_field.column

        schema_editor.alter_field(fk_field.model, old_field, new_field, strict=True)


# Traceback (ValueError)
Operations to perform:
Apply all migrations: accounts, addons, admin, auth, authtoken, checks, contenttypes, lang, memory, permissions, screenshots, sessions, sites, social_django, trans, weblate_auth, wladmin
Running migrations:
Applying weblate_auth.0007_auto_20180509_0739...Traceback (most recent call last):
File "./manage.py", line 31, in <module>
execute_from_command_line(sys.argv)
File "/usr/local/lib/python2.7/dist-packages/django/core/management/__init__.py", line 363, in execute_from_command_line
utility.execute()
File "/usr/local/lib/python2.7/dist-packages/django/core/management/__init__.py", line 355, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/usr/local/lib/python2.7/dist-packages/django/core/management/base.py", line 283, in run_from_argv
self.execute(*args, **cmd_options)
File "/usr/local/lib/python2.7/dist-packages/django/core/management/base.py", line 330, in execute
output = self.handle(*args, **options)
File "/usr/local/lib/python2.7/dist-packages/django/core/management/commands/migrate.py", line 204, in handle
fake_initial=fake_initial,
File "/usr/local/lib/python2.7/dist-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 "/usr/local/lib/python2.7/dist-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 "/usr/local/lib/python2.7/dist-packages/django/db/migrations/executor.py", line 244, in apply_migration
state = migration.apply(state, schema_editor)
File "/usr/local/lib/python2.7/dist-packages/django/db/migrations/migration.py", line 129, in apply
operation.database_forwards(self.app_label, schema_editor, old_state, project_state)
File "/usr/local/lib/python2.7/dist-packages/django/db/migrations/operations/special.py", line 193, in database_forwards
self.code(from_state.apps, schema_editor)
File "/usr/share/weblate/weblate/auth/migrations/0007_auto_20180509_0739.py", line 14, in forwards
"weblate_auth", "Group")
File "/usr/share/weblate/weblate/auth/migrations/0007_auto_20180509_0739.py", line 83, in change_foreign_keys
schema_editor.alter_field(fk_field.model, old_field, new_field, strict=True)
File "/usr/local/lib/python2.7/dist-packages/django/db/backends/base/schema.py", line 515, in alter_field
old_db_params, new_db_params, strict)
File "/usr/local/lib/python2.7/dist-packages/django/db/backends/base/schema.py", line 529, in _alter_field
old_field.column,
ValueError: Found wrong number (0) of foreign key constraints for weblate_auth_autogroup.group_id

# Fixed code
def change_foreign_keys(
    apps, schema_editor, from_app, from_model_name, to_app, to_model_name
):
    FromModel = apps.get_model(from_app, from_model_name)
    ToModel = apps.get_model(to_app, to_model_name)

    fields = FromModel._meta.get_fields(include_hidden=True)

    for rel in fields:
        if not hasattr(rel, "field") or not isinstance(rel.field, models.ForeignKey):
            continue
        fk_field = rel.field

        f_name, f_field_name, pos_args, kw_args = fk_field.deconstruct()

        # fk_field might have been the old or new one. We need to fix things up.
        old_field_kwargs = kw_args.copy()
        old_field_kwargs["to"] = FromModel
        old_field = fk_field.__class__(*pos_args, **old_field_kwargs)
        old_field.model = fk_field.model

        new_field_kwargs = kw_args.copy()
        new_field_kwargs["to"] = ToModel
        new_field = fk_field.__class__(*pos_args, **new_field_kwargs)
        new_field.model = fk_field.model

        if fk_field.model._meta.auto_created:
            # If this is a FK that is part of an M2M on the model itself,
            # we've already dealt with this, by virtue of the data migration
            # that populates the auto-created M2M field.
            if fk_field.model._meta.auto_created in [ToModel, FromModel]:
                continue

            # In this case (FK fields that are part of an autogenerated M2M),
            # the column name in the new M2M might be different to that in the
            # old M2M. This makes things much harder, and involves duplicating
            # some Django logic.

            # Build a correct ForeignKey field, as it should
            # have been on FromModel
            old_field.name = from_model_name.lower()
            old_field.column = "{0}_id".format(old_field.name)

            # build a correct ForeignKey field, as it should
            # be on ToModel
            new_field.name = to_model_name.lower()
            new_field.column = "{0}_id".format(new_field.name)
        else:
            old_field.name = fk_field.name
            old_field.column = fk_field.column
            new_field.name = fk_field.name
            new_field.column = fk_field.column

        print(to_model_name, old_field_kwargs, new_field_kwargs)
        schema_editor.alter_field(fk_field.model, old_field, new_field, strict=True)