AlASAD WAIL
AlASAD WAIL

Reputation: 825

Create foreign key from auth_group_permissions table in django

I want to make foreign key from auth_group_permissions table.(the M2M table between permission and group) I know that I can use through, but it isn't appropriate if I make modify in Django library.

I used Django guardian library, so customize group model is not good idea.

I want like this:

class MyModel(models.Model):
    my_field = models.ForeignKey(???,on_delete=models.CASCAD)

Is there any appropriate solution.

Upvotes: 1

Views: 543

Answers (1)

Nick Po
Nick Po

Reputation: 158

First solution: manage this relationship not automatically

class MyModel(models.Model):
    grouppermission_id = models.PositiveIntegerField()
    some_field = models.TextField()

grouppermissions_instance = group_instance.permissions.through.objects.filter(permission=permission_instance, group=group_instance.id).first()

grouppermissions_settings = MyModel.create(grouppermissions_instance.pk, "extra settings")

Second solution: You can create your own intermediate model and elegancy slip it to Group model from your app models.py and make migrations in your app not in django.

in my account.models:

class GroupPermission(models.Model):
    group = models.ForeignKey(Group, on_delete=models.CASCADE, db_index=False)
    permission = models.ForeignKey(Permission, on_delete=models.CASCADE)
    timedelta = models.DurationField(
        "Timedelta to restrict access.", null=True, blank=True
    )

    class Meta:
        unique_together = ("group", "permission")
        app_label = "auth"

    def __str__(self):
        return f"{self.group} | {self.permission} | {self.timedelta}"


Permission.group_set.field.remote_field.through = GroupPermission
    

account.migrations:

0011

class Migration(migrations.Migration):

    dependencies = [
        ('account', '0009_some_migrations_prev'),
        ('auth', '0012_alter_user_first_name_max_length'),
    ]

    operations = [
        migrations.CreateModel(
            name='GroupPermission',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('timedelta', models.DurationField(verbose_name='Timedelta to restrict access.', blank=True, null=True)),
                ('group', models.ForeignKey(db_index=False, on_delete=django.db.models.deletion.CASCADE, to='auth.group')),
                ('permission', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.permission')),
            ],
            options={
                'unique_together': {('group', 'permission')},
            },
        ),
        migrations.RunSQL(sql="""INSERT INTO auth_grouppermission SELECT "id", NULL, "group_id", "permission_id" FROM auth_group_permissions;""", reverse_sql="")
    ]

    def mutate_state(self, project_state, preserve=True):
        """
        This is a workaround that allows to store ``auth``
        migration outside the directory it should be stored.
        """
        app_label = self.app_label
        self.app_label = "auth"
        state = super(Migration, self).mutate_state(project_state, preserve)
        self.app_label = app_label
        return state

    def apply(self, project_state, schema_editor, collect_sql=False):
        """
        Same workaround as described in ``mutate_state`` method.
        """
        app_label = self.app_label
        self.app_label = "auth"
        state = super(Migration, self).apply(project_state, schema_editor, collect_sql)
        self.app_label = app_label
        return state

    def unapply(self, project_state, schema_editor, collect_sql=False):
        """
        Same workaround as described in ``mutate_state`` method.
        """
        app_label = self.app_label
        self.app_label = "auth"
        state = super(Migration, self).unapply(project_state, schema_editor, collect_sql)
        self.app_label = app_label
        return state

0012

class Migration(migrations.Migration):

    dependencies = [
        ("account", "0011_some_migrations_prev"),
        ("auth", "0012_alter_user_first_name_max_length"),
    ]

    operations = [
        migrations.RunSQL(
            sql="DROP TABLE auth_group_permissions CASCADE",
            reverse_sql="""
            BEGIN;
                CREATE TABLE IF NOT EXISTS "auth_group_permissions" ("id" serial NOT NULL PRIMARY KEY, "group_id" integer NOT NULL, "permission_id" integer NOT NULL);
                ALTER TABLE "auth_group_permissions" ADD CONSTRAINT "auth_group_permissions_group_id_2fcb7b02_fk_auth_group_id" FOREIGN KEY ("group_id") REFERENCES "auth_group" ("id") DEFERRABLE INITIALLY DEFERRED;
                ALTER TABLE "auth_group_permissions" ADD CONSTRAINT "auth_group_permissions_permission_id_3dd4a24a_fk_auth_perm" FOREIGN KEY ("permission_id") REFERENCES "auth_permission" ("id") DEFERRABLE INITIALLY DEFERRED;
                BEGIN;
                    INSERT INTO "auth_group_permissions" SELECT "id", "group_id", "permission_id" FROM "auth_grouppermission";
                COMMIT;
                BEGIN;
                    CREATE INDEX "auth_group_permissions_group_id_2fcb7b02" ON "auth_group_permissions" ("group_id");
                    CREATE INDEX "auth_group_permissions_permission_id_3dd4a24a" ON "auth_group_permissions" ("permission_id");
                COMMIT;
            COMMIT;
            """,
        ),
        migrations.AddField(
            model_name="group",
            name="permissions",
            field=models.ManyToManyField(
                blank=True,
                through="auth.GroupPermission",
                to="auth.Permission",
                verbose_name="permissions",
            ),
        ),
    ]

    def mutate_state(self, project_state, preserve=True):
        """
        This is a workaround that allows to store ``auth``
        migration outside the directory it should be stored.
        """
        app_label = self.app_label
        self.app_label = "auth"
        state = super(Migration, self).mutate_state(project_state, preserve)
        self.app_label = app_label
        return state

    def apply(self, project_state, schema_editor, collect_sql=False):
        """
        Same workaround as described in ``mutate_state`` method.
        """
        app_label = self.app_label
        self.app_label = "auth"
        state = super(Migration, self).apply(project_state, schema_editor, collect_sql)
        self.app_label = app_label
        return state

    def unapply(self, project_state, schema_editor, collect_sql=False):
        """
        Same workaround as described in ``mutate_state`` method.
        """
        app_label = self.app_label
        self.app_label = "auth"
        state = super(Migration, self).unapply(project_state, schema_editor, collect_sql)
        self.app_label = app_label
        return state

Upvotes: 1

Related Questions