Question

I'm currently creating a structure where I have employees which belong to a company. Within this company I need to be able to create several groups. Ranks if you will. You could assign less permissions to lower ranks and more permissions to higher ranks.

I want to go for object level permissions and I noticed the django-guardian project gave me exactly what I needed. It works with the native User and Group objects so I'm now trying to find a way to implement the native group object in a company object.

Problems I face is that name in group is unique. So if 2 companies add the same group, errors will occur.

I found an implementation that works in a way but seems quite 'hacky' to me. In my company I declared a group variable that references Group:

class Company(models.Model):
    ...
    groups = models.ManyToManyField(Group, through='CompanyRole')

CompanyRole basically houses the group name and a reference to company and group

class CompanyRole(models.Model):
    group = models.ForeignKey(Group)
    company = models.ForeignKey(Company)
    real_name = models.CharField(max_length=60, verbose_name=_('Real name'))

    objects = CompanyGroupManager()

I created a custom manager with a convenient method to add a new 'company group'

class CompanyGroupManager(models.Manager):
    def create_group(self, company, group_name):
        un_group_name = str(company.id) + '#' + group_name
        group = Group.objects.create(name=un_group_name)
        company_group = self.model(
            real_name=group_name, 
            company=company, 
            group=group
        )

        company_group.save(using=self._db)
        return company_group

Here's the part I don't really feel confortable about. In order to change the problem with the unique name on the Group model I used a combination of the company id, a hash sign and the actual group name to avoid clashes.

Now my question is: are there better methods in my scenario, am I missing something or is this a good way of accomplishing what I need?

Was it helpful?

Solution

Unfortunately there is no way of getting around the unique requirement, that is because this field is used as the id: https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.Field.unique

Your options are the following:

1) Mocking the model.

You would basically just create a new Group model that doesn't have the unique requirement. The downside here is that you'd need to use it everywhere, so if this requires updating 3rd party apps, it might not be worth it.

2) make the name you unique. (As you did)

Make sure that you document your convention well, so that all future coders will know what they are looking at.Something like "company name"#"group name" could make more intuitive sense than an id. If the a hash might appear in either then use a more certain delimiter ("__" is a relatively common way of connecting related concepts in django, I might go for this).

I would recommend that you add the following to make it easy for you to access the name.

def get_name(self):
    # Explain how you get the group name from your uniqueified name
    return self.name.split('#')[1] 

Group.add_to_class('get_name', get_name)

When you access your group's name in your app, just do:

my_group.get_name()

You might also want to put the generating the uniqueified name into an overridden version of the save(). This would give you nicer split between model and view...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top