djangodjango-modelsgeneric-foreign-key

GenericForeignKeys in Intermediate Models


I'm attempting to create an intermediate model, Permissions, between 'auth.Group' and any other custom models; this will serve as permissions or a means of what is visible to which groups.

I have been able to create an intermediate model, ExamplePermissions, between 'auth.Group' and one model.

    class Example(TimeStampable, Ownable, Model):
        groups = models.ManyToManyField('auth.Group', through='ExamplePermissions', related_name='examples')
        name = models.CharField(max_length=255)
        ...
        # Used for chaining/mixins
        objects = ExampleQuerySet.as_manager()

        def __str__(self):
            return self.name

    class ExamplePermissions(Model):
        example = models.ForeignKey(Example, related_name='group_details')
        group = models.ForeignKey('auth.Group', related_name='example_details')
        write_access = models.BooleanField(default=False)

        def __str__(self):
            return ("{0}'s Example {1}").format(str(self.group), str(self.example))

However, the issue is that this opposes reusability. To create a model that allows any custom model to be associated with it, I implemented a GenericForeignKey in place of a ForeignKey as follows:

    class Dumby(Model):
        groups = models.ManyToManyField('auth.Group', through='core.Permissions', related_name='dumbies')
        name = models.CharField(max_length=255)

        def __str__(self):
            return self.name

    class Permissions(Model):
        # Used to generically relate a model with the group model
        content_type = models.ForeignKey(ContentType, related_name='group_details')
        object_id = models.PositiveIntegerField()
        content_object = GenericForeignKey('content_type', 'object_id')
        #
        group = models.ForeignKey('auth.Group', related_name='content_details')
        write_access = models.BooleanField(default=False)

        def __str__(self):
            return ("{0}'s Content {1}".format(str(self.group), str(self.content_object)))

Upon attempting to make migrations, it errors with:
core.Permissions: (fields.E336) The model is used as an intermediate model by 'simulations.Dumby.groups', but it does not have a foreign key to 'Dumby' or 'Group'.

At first glance, using a GenericForeignKey in an intermediate table seems like a dead end. If this is the case, is there some generally accepted way of handling such a situation besides the cumbersome and redundant approach of creating a custom intermediate model for each custom model?


Solution

  • Do not use ManyToManyField when using GenericForeignKey in your intermediate model; instead, use GenericRelation, so your groups field would be simply declared as:

    groups = generic.GenericRelation(Permissions)
    

    See reverse generic relations for more details.