Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Groups per object using Django and django-guardian object permissions

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?

like image 678
Jeffrey Vandenborne Avatar asked May 04 '14 18:05

Jeffrey Vandenborne


People also ask

What are object level permissions Django?

What are Object Level Permissions in DRF? In DRF, object level permissions are used to decide if a user should be allowed to interact with a particular object. The object is usually simply a model instance.

How do I check permissions in Django?

To test which users have basic permissions, you can use the following code. view: user. has_perm('product. view_order') Adding permissions to restrict a function to only users that have that particular permission can be done by using a Django built-in decorator, permission_required .

How do permissions work in Django?

By default, Django automatically gives add, change, and delete permissions to all models, which allow users with the permissions to perform the associated actions via the admin site. You can define your own permissions to models and grant them to specific users.


1 Answers

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...

like image 107
cchristelis Avatar answered Oct 03 '22 01:10

cchristelis