citusdata / citusdata/django-multitenant
django.db.utils.ProgrammingError: foreign key referenced-columns list must not contain duplicates
- Dominant language
- Python
- Stars
- 823
- Forks
- 126
- PR merge metrics
- No merged PRs in 30d
Description
Hello all,
**Quick summary**
I'm getting
`django.db.utils.ProgrammingError: foreign key referenced-columns list must not contain duplicates` when I attempt to run any migrations at all on a new starter project.}
I think there must be something basic I'm missing, but I can't find it in issues or documentation.
**Detailed**
I'm trying to get a basic starter case going and I can't get a single migration to work, so I think I'm missing something simple
```
from django_multitenant.models import TenantModel
from django.db import models
class Account(TenantModel):
name = models.CharField(max_length=255)
contact_info = models.TextField()
class TenantMeta:
tenant_field_name = "id"
class Meta:
unique_together = ["id"]
def __str__(self):
return self.name
```
I create a basic Account tenant.
I then try to add it to the users with database backend enabled. I get a basic user migration
```
class User(TenantModelMixin, AbstractBaseUser, PermissionsMixin, IndexedTimeStampedModel):
email = models.EmailField(max_length=255, unique=True)
is_staff = models.BooleanField(
default=False, help_text=_("Designates whether the user can log into this admin site.")
)
is_active = models.BooleanField(
default=True,
help_text=_(
"Designates whether this user should be treated as "
"active. Unselect this instead of deleting accounts."
),
)
name = models.CharField(max_length=255)
# Custom fields
account = models.ForeignKey(
Account,
on_delete=models.CASCADE,
related_name="users",
null=True,
blank=True,
)
tenant_id = "account_id"
objects = UserManager()
USERNAME_FIELD = "email"
class Meta:
unique_together = ["id", "account"]
```
```
class Migration(migrations.Migration):
initial = True
dependencies = [
("auth", "0012_alter_user_first_name_max_length"),
("accounts", "0001_initial"),
]
operations = [
migrations.CreateModel(
name="User",
fields=[
(
"id",
models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
("password", models.CharField(max_length=128, verbose_name="password")),
(
"last_login",
models.DateTimeField(blank=True, null=True, verbose_name="last login"),
),
(
"is_superuser",
models.BooleanField(
default=False,
help_text="Designates that this user has all permissions without explicitly assigning them.",
verbose_name="superuser status",
),
),
(
"created",
model_utils.fields.AutoCreatedField(
db_index=True,
default=django.utils.timezone.now,
editable=False,
verbose_name="created",
),
),
(
"modified",
model_utils.fields.AutoLastModifiedField(
db_index=True,
default=django.utils.timezone.now,
editable=False,
verbose_name="modified",
),
),
("email", models.EmailField(max_length=255, unique=True)),
(
"is_staff",
models.BooleanField(
default=False,
help_text="Designates whether the user can log into this admin site.",
),
),
(
"is_active",
models.BooleanField(
default=True,
help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
),
),
(
"account",
django_multitenant.fields.TenantForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="users",
to="accounts.account",
),
),
(
"groups",
models.ManyToManyField(
blank=True,
help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
related_name="user_set",
related_query_name="user",
to="auth.group",
verbose_name="groups",
),
),
(
"user_permissions",
models.ManyToManyField(
blank=True,
help_text="Specific permissions for this user.",
related_name="user_set",
related_query_name="user",
to="auth.permission",
verbose_name="user permissions",
),
),
],
options={
"unique_together": {("id", "account")},
},
bases=(django_multitenant.mixins.TenantModelMixin, models.Model),
),
]
```
And it fails because of the double account_id: `foreign key referenced-columns list must not contain duplicates`
```
CREATE TABLE "users_user" ("id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, "password" varchar(128) NOT NULL, "last_login" timestamp with time zone NULL, "is_superuser" boolean NOT NULL, "created" timestamp with time zone NOT NULL, "modified" timestamp with time zone NOT NULL, "email" varchar(255) NOT NULL UNIQUE, "is_staff" boolean NOT NULL, "is_active" boolean NOT NULL, "account_id" bigint NULL);
CREATE TABLE "users_user_groups" ("id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, "user_id" bigint NOT NULL, "group_id" integer NOT NULL);
CREATE TABLE "users_user_user_permissions" ("id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, "user_id" bigint NOT NULL, "permission_id" integer NOT NULL);
-- The bad lines:
ALTER TABLE "users_user" ADD CONSTRAINT "users_user_id_account_id_e2e2a14b_uniq" UNIQUE ("id", "account_id");
ALTER TABLE "users_user" ADD CONSTRAINT "users_user_account_id_account_i_985fcd95_fk_accounts_" FOREIGN KEY ("account_id", "account_id") REFERENCES "accounts_account" ("id", "id") DEFERRABLE INITIALLY DEFERRED;
```
I can't find any other issues related to this so I think I'm just missing something very simple. Could someone point me in the right direction?
Contributor guide
Research direction
Start with the Account and User model definitions, especially TenantMeta, TenantForeignKey, and each model's unique_together, then inspect the generated initial migration and the PostgreSQL SQL shown in the report. Trace how the migration creates the foreign key and determine why account_id is emitted twice. Done means the starter project's migrations run successfully without duplicate referenced columns.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- postgresql, python
- Domain
- backend, databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 32/100