Search code examples
djangodjango-modelsdjango-permissions

django permissions not being respected


I am trying to show a button if the logged in user has the right permissions.

The code in the template:

  {% if perms.django_apo_training.can_add %}
   <a href="{% url 'training:trainee_add' %}">     
        <button type="button" class="btn btn-success">Add Trainee</button>    
   </a>
   {% endif %}

I can print to the webpage to debug what the permissions the user has:

  <p> {{ perms.django_apo_training }} </p>

It shows:

{'training.view_trainee', 'training.add_trainee', 'training.delete_trainee', 'training.change_trainee'}

but

perms.django_apo_training.can_add 

always returns false, not sure what what I am missing?

Even double checked in the admin console:

enter image description here

(if I grant super user status to the the user in question, then the if statement works and returns true?)

Here is the model for the trainee stuff

# Create your models here.
class Trainee(models.Model):
    objects = models.Manager()

    first_name = models.CharField(max_length=200)
    last_name = models.CharField(max_length = 200)
    institution = models.CharField(max_length=200, blank=True)
    contact_phone = models.CharField(max_length=50, blank=True)
    contact_email = models.CharField(max_length=100, blank=True)
    trained_date = models.DateField('date trained')

    class Meta:
        ordering = ['institution']

    def __str__(self):
        return "Trainee: " + str(self.first_name) + " " + str(self.last_name) + "   " + str(self.institution)

Secondly... even once this works, how do I make sure that only those with that permission and logged in can get to (it is a lot more than an @login required decorator)

http://localhost:8000/training/add/

Lastly : I have also created model to extend the user model using the one-to-one model:

    user = models.OneToOneField(User, on_delete=models.CASCADE)

Inside this APOUser model I call it, I have other fields that I would love to key off of for these permissions (specifically what is contained in the on_site_status), is there some set way or example/recipe of how one might do that? (the full model is here)

class APOUser(models.Model):
    objects = models.Manager()

    user = models.OneToOneField(User, on_delete=models.CASCADE)

    institution =  models.ForeignKey("mainpage.Institution", on_delete=models.SET_NULL, null=True)
    on_site_status = models.ForeignKey("mainpage.SiteStatus", on_delete=models.SET_NULL,  null=True)
    refer_to_as = models.TextField(max_length = 30, blank=True, null=True) #if the above is custom
    room_preference  = models.ForeignKey("housing.Room", on_delete=models.SET_NULL,  null=True)

Solution

  • The standard permissions created follow the format <app_label>.view_<model_name>. So in the product app with the following models a total of 8 permissions will be automatically created.

    class Category(models.Model):
        ...
    
    class Product(models.Model):
        ...
    
    # permissions for the Category model
    'product.view_category'
    'product.add_category'
    'product.change_category'
    'product.delete_category'
    
    # permissions for the Product model
    'product.view_product'
    'product.add_product'
    'product.change_product'
    'product.delete_product'
    

    In your example perms.django_apo_training.can_add will always return False because there is no permission can_add - unless you have a model called Add and have created a custom permission. The correct pattern should be perms.training.add_training.

    It's also important to note that checking the permissions for a superuser will always return True even if the permission does not exist.

    For protected views with permissions and custom user fields you can subclass UserPassesTestMixin.

    from django.contrib.auth.mixins import UserPassesTestMixin
    
    def permissions_check(user):
        approved_profile_status = ['ADMIN', 'PURCHASER']
        permission = 'training.add_trainee'
    
        is_approved = user.profile.on_site_status in approved_profile_status
        has_perm = user.has_perm(permission)
        return is_approved or has_perm
    
    
    class PermissionsMixin(UserPassesTestMixin):
        def test_func(self):
            return permissions_check(self.request.user)
    
    
    class TraineeListView(PermissionsMixin, ListView):
        ...