Search code examples
djangopropertiesdjango-modelsdjango-contenttypes

django user XOR group model relationship


I'm thinking on the best way to set a relationship with django's built-in User or Group.

By 'or' I mean that the model instances must be exclusively owned by an User or by a Group.

I think the question should be easy to understand by having a look at my model above.

This is my current implementation. I've been looking at GenericRelations but they don't seen appropiate when restricted to such a small number of models.

EDIT: Refactor with an abstract model.

class OwnedModel(models.Model):

    _owner_user = models.ForeignKey(User, null=True, related_name='%(class)s')

    _owner_group = models.ForeignKey(Group, null=True, related_name='%(class)s')

    class Meta:
        abstract = True

    @property 
    def owner(self):
        return self._owner_user or self._owner_group

    @owner.setter
    def owner(self, value):
        if isinstance(value, User):
            self._owner_user = value
            if self._owner_group:
                self._owner_group = None
        elif isinstance(value, Group):
            self._owner_group = value
            if self._owner_user:
                self._owner_user = None
        else:
            raise ValueError

class RemoteAccess(OwnedModel):
    SSH = 's'
    SUPPORTED_PROTOCOLS = (
        (SSH, "ssh"),
    )

    host = models.CharField(_('host name'), max_length=255, validators=[full_domain_validator])

    protocol = models.CharField(_('protocol'), max_length=1, choices=SUPPORTED_PROTOCOLS, default=SSH)

    credential = models.OneToOneField(RemoteCredential)

My main issues with my current implementation are:

  • How to force it to set an User or Group when creating the instance? Could an __init__ override be the way to go?
  • Is there a better/cleaner/more-efficient implementation?

Thanks!


Solution

  • I would override the save() method instead and define a custom exception

    def save(self, *args, **kwargs):
        if self._owner_user is None and self._owner_group is None:
            raise NoOwner
        super(OwnedModel, self).save(*args, **kwargs)
    

    Overriding the init might cause problems in certain cases where you don't actually have the owner until the save (such as in forms etc.)

    I don't think there is a cleaner way to do this and stick with the Django User/Group.