﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
31207	ForeignObject.to_fields is allowed to point to non-local remote fields.	un.def	Hasan Ramezani	"It looks like a regression in Django 3.0, possibly related to `SchemaEditor.sql_create_column_inline_fk`.

Setting `django.db.backends.postgresql.schema.DatabaseSchemaEditor.sql_create_column_inline_fk = False` fixes the issue.

== Prerequisites

* Python 3.8.1
* PostgreSQL 9.6.12
* psycopg2 2.8.4
* Django 2.2.9 vs. 3.0.2

== Django 2.2.9

**Step 1**. Create models:

{{{#!python
class BaseModel(models.Model):
    key = models.IntegerField(unique=True)


class ChildModel(BaseModel):
    field = models.IntegerField()


class OtherModel(models.Model):
    field = models.IntegerField()
}}}

**Step 2**. Generate initial migration:

{{{#!python
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='BaseModel',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('key', models.IntegerField(unique=True)),
            ],
        ),
        migrations.CreateModel(
            name='OtherModel',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('field', models.IntegerField()),
            ],
        ),
        migrations.CreateModel(
            name='ChildModel',
            fields=[
                ('basemodel_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='bugapp.BaseModel')),
                ('field', models.IntegerField()),
            ],
            bases=('bugapp.basemodel',),
        ),
    ]
}}}

**Step 3**. Add foreign key from `OtherModel` to `ChildModel`:

{{{#!python
class OtherModel(models.Model):
    field = models.IntegerField()
    ref = models.ForeignKey(ChildModel, on_delete=models.CASCADE, to_field='key', null=True)
}}}

**Step 4**. Generate migration:

{{{#!python
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

    dependencies = [
        ('bugapp', '0001_initial'),
    ]

    operations = [
        migrations.AddField(
            model_name='othermodel',
            name='ref',
            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='bugapp.ChildModel', to_field='key'),
        ),
    ]
}}}

**Step 5**. Inspect migration:

{{{#!sql
BEGIN;
--
-- Add field ref to othermodel
--
ALTER TABLE ""bugapp_othermodel"" ADD COLUMN ""ref_id"" integer NULL;
CREATE INDEX ""bugapp_othermodel_ref_id_9cbf2643"" ON ""bugapp_othermodel"" (""ref_id"");
ALTER TABLE ""bugapp_othermodel"" ADD CONSTRAINT ""bugapp_othermodel_ref_id_9cbf2643_fk_bugapp_basemodel_key"" FOREIGN KEY (""ref_id"") REFERENCES ""bugapp_basemodel"" (""key"") DEFERRABLE INITIALLY DEFERRED;
COMMIT;
}}}

**Step 6**. Migrate:

{{{
Running migrations:
  Applying bugapp.0002_othermodel_ref... OK
  Applying sessions.0001_initial... OK
}}}

== Django 3.0.2

**Steps 1-4** are the same.

**Step 5** produces different SQL code:

{{{#!sql
BEGIN;
--
-- Add field ref to othermodel
--
ALTER TABLE ""bugapp_othermodel"" ADD COLUMN ""ref_id"" integer NULL CONSTRAINT ""bugapp_othermodel_ref_id_9cbf2643_fk_bugapp_basemodel_key"" REFERENCES ""bugapp_childmodel""(""key"") DEFERRABLE INITIALLY DEFERRED; SET CONSTRAINTS ""bugapp_othermodel_ref_id_9cbf2643_fk_bugapp_basemodel_key"" IMMEDIATE;
CREATE INDEX ""bugapp_othermodel_ref_id_9cbf2643"" ON ""bugapp_othermodel"" (""ref_id"");
COMMIT;
}}}

As you see, `ref_id` column references wrong table — `bugapp_childmodel`, not `bugapp_basemodel`: `""ref_id"" ... REFERENCES ""bugapp_childmodel""(""key"")`.

**Step 6** fails:

{{{
  Applying bugapp.0001_initial... OK
Traceback (most recent call last):
  File ""<...>/python3.8/site-packages/django/db/backends/utils.py"", line 86, in _execute
    return self.cursor.execute(sql, params)
psycopg2.errors.UndefinedColumn: column ""key"" referenced in foreign key constraint does not exist


The above exception was the direct cause of the following exception:

<...>

  File ""<...>/python3.8/site-packages/django/db/backends/utils.py"", line 86, in _execute
    return self.cursor.execute(sql, params)
django.db.utils.ProgrammingError: column ""key"" referenced in foreign key constraint does not exist

  Applying bugapp.0002_othermodel_ref...
}}}

== Note

It does not happen if the foreign key has been created with the model and not added later:

**Step 7**. Delete `0001_initial.py`

**Step 8**. Generate migration:

{{{#!python
# Generated by Django 3.0.2 on 2020-01-25 14:46

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='BaseModel',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('key', models.IntegerField(unique=True)),
            ],
        ),
        migrations.CreateModel(
            name='ChildModel',
            fields=[
                ('basemodel_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='bugapp.BaseModel')),
                ('field', models.IntegerField()),
            ],
            bases=('bugapp.basemodel',),
        ),
        migrations.CreateModel(
            name='OtherModel',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('field', models.IntegerField()),
                ('ref', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='bugapp.ChildModel', to_field='key')),
            ],
        ),
    ]
}}}

**Step 9**. Inspect migration:

{{{#!sql
BEGIN;
--
-- Create model BaseModel
--
CREATE TABLE ""bugapp_basemodel"" (""id"" serial NOT NULL PRIMARY KEY, ""key"" integer NOT NULL UNIQUE);
--
-- Create model ChildModel
--
CREATE TABLE ""bugapp_childmodel"" (""basemodel_ptr_id"" integer NOT NULL PRIMARY KEY, ""field"" integer NOT NULL);
--
-- Create model OtherModel
--
CREATE TABLE ""bugapp_othermodel"" (""id"" serial NOT NULL PRIMARY KEY, ""field"" integer NOT NULL, ""ref_id"" integer NULL);
ALTER TABLE ""bugapp_childmodel"" ADD CONSTRAINT ""bugapp_childmodel_basemodel_ptr_id_407e3662_fk_bugapp_ba"" FOREIGN KEY (""basemodel_ptr_id"") REFERENCES ""bugapp_basemodel"" (""id"") DEFERRABLE INITIALLY DEFERRED;
ALTER TABLE ""bugapp_othermodel"" ADD CONSTRAINT ""bugapp_othermodel_ref_id_9cbf2643_fk_bugapp_basemodel_key"" FOREIGN KEY (""ref_id"") REFERENCES ""bugapp_basemodel"" (""key"") DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX ""bugapp_othermodel_ref_id_9cbf2643"" ON ""bugapp_othermodel"" (""ref_id"");
COMMIT;
}}}

Note that the reference is correct now: `(""ref_id"") REFERENCES ""bugapp_basemodel"" (""key"")`."	Bug	closed	Database layer (models, ORM)	3.0	Normal	fixed			Accepted	1	0	0	0	0	0
