1

In my project, I've got different types of Users like this:

User |-Client |-Employee |-Technical Manager |-Service Manager |-Production Manager

No user can access the view of other user by url. A client cannot access profile of a Technical Manager through the url /officer/profile which is assigned for Technical Manager's profile.

In order to do so, In my Client class in the models.py, I used this code snippet:

class Client(models.Model): class Meta: permissions = ( ('view_client', 'view_Client'), )

Then for each view I used a decorator like this: @permission_required(lambda u: u.has_perm('authentication.view_client'))

Then I'm logging in as a technical manager and trying to access with this url: /client/profile

Then I got the error function object is not iterable.

I was creating my user in an app called authentication. The models,py looks like this: '

@python_2_unicode_compatible
class Profile(models.Model):
    user = models.OneToOneField(User)
    user_sex = (('MALE', 'Male'), ('FEMALE', 'Female'))
    sex = models.CharField(max_length=6, default='Male', choices=user_sex)
    address = models.CharField(max_length=250, null=True, blank=True)
    city = models.CharField(max_length=250, null=True, blank=True)
    state = models.CharField(max_length=250, null=True, blank=True)
    country = models.CharField(max_length=250, null=True, blank=True)
    phone = PhoneNumberField(blank=True)
    zip = models.IntegerField(null=True, blank=True)
    about = models.CharField(max_length=250, null=True, blank=True)
    email_confirmed = models.BooleanField(default=False)
    account_type = models.IntegerField(default=-1)

    class Meta:
        db_table = 'auth_profile'


class Employee(models.Model):
    user = models.OneToOneField(User)
    manager = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
    designation = models.CharField(max_length=6)

    class Meta:
        db_table = 'auth_employee'


class Client(models.Model):
    user = models.OneToOneField(User)
    class Meta:
        db_table = 'auth_client'


@receiver(post_save, sender=User)
def update_user_profile(sender, instance, created, **kwargs):
    if created:
        Profile.objects.create(user=instance)
    instance.profile.save()
    if instance.profile.account_type == 0:
        if not Client.objects.filter(user=instance).exists():
            Client.objects.create(user=instance)
            instance.client.save()
        else:
            instance.client.save()
    if instance.profile.account_type == 1 or instance.profile.account_type == 2 or instance.profile.account_type == 3:
        if not Employee.objects.filter(user=instance).exists():
            Employee.objects.create(user=instance)
            instance.employee.save()
        else:
            instance.employee.save()

'

Profile class is kinda big. Please ignore it. Now what I wanted is that I would assign the permission each time a different user has been created of any types.

How to resolve the error and how can I accomplish this, so that one type of user cannot access the view of others' by typing the url?

Edited after the comment from souldeux

Here is one of my views where a client can update his contact info:

@login_required(login_url='/login/')
@permission_required(lambda u: u.has_perm('authentication.view_client'))
def contact(request):
    user = request.user
    if request.method == 'POST':
        # form = ContactForm()
        form = ContactForm(request.POST)
        if form.is_valid():
            user.profile.address = form.cleaned_data.get('address')
            user.profile.city = form.cleaned_data.get('city')
            user.profile.state = form.cleaned_data.get('state')
            user.profile.country = form.cleaned_data.get('country')
            user.profile.phone = form.cleaned_data.get('phone')
            user.profile.zip = form.cleaned_data.get('zip')


            user.save()
            messages.add_message(request,
                                 messages.SUCCESS,
                                 'Your contact was successfully edited.')

    else:

        form = ContactForm(instance=user, initial={
            'address': user.profile.address,
            'city': user.profile.city,
            'state': user.profile.state,
            'country': user.profile.country,
            'phone': user.profile.phone,
            'zip': user.profile.zip,
        })
    return render(request, 'client/client_contact.html', {'form': form})

And the error I got:

TypeError at /client/picture

'function' object is not iterable

Request Method:     GET
Request URL:    http://127.0.0.1:8000/client/picture
Django Version:     1.11.6
Exception Type:     TypeError
Exception Value:    

'function' object is not iterable

Exception Location:     /usr/local/lib/python2.7/dist-packages/django/contrib/auth/models.py in has_perms, line 285
Python Executable:  /usr/bin/python2.7
Python Version:     2.7.12
Python Path:    

['/home/shamsad/PycharmProjects/OpenGMS',
 '/home/shamsad/PycharmProjects/OpenGMS',
 '/usr/lib/python2.7',
 '/usr/lib/python2.7/plat-x86_64-linux-gnu',
 '/usr/lib/python2.7/lib-tk',
 '/usr/lib/python2.7/lib-old',
 '/usr/lib/python2.7/lib-dynload',
 '/home/shamsad/.local/lib/python2.7/site-packages',
 '/usr/local/lib/python2.7/dist-packages',
 '/usr/lib/python2.7/dist-packages',
 '/usr/lib/python2.7/dist-packages/PILcompat',
 '/usr/lib/python2.7/dist-packages/gtk-2.0']

Server time:    Sun, 29 Oct 2017 16:21:25 +0000
sphoenix
  • 3,327
  • 5
  • 22
  • 40

1 Answers1

0

Rather than passing a lambda function in a permission_required decorator, re-write the permission test as a standalone function and apply it via a method_decorator that works on the dispatch method:

https://docs.djangoproject.com/en/dev/topics/class-based-views/intro/#decorating-the-class

How to use permission_required decorators on django class-based views

You can also use @user_passes_test to decorate your function-based views. For instance, perhaps there is a view you want to be visible only to those users associated with a Client object:

def user_has_client(user)
    return hasattr(user, 'Client')

@user_passes_test(user_has_client, login_url='/')
def my_protected_view(request):
    ...

You could check for user permissions in your test as well: https://docs.djangoproject.com/en/1.11/ref/contrib/auth/#django.contrib.auth.models.User.has_perm

souldeux
  • 3,615
  • 3
  • 23
  • 35
  • Do I have to assign permission in the `post signal` each time an user has been created? – sphoenix Oct 29 '17 at 16:39
  • If you want consistent behavior across all users, then yes. Here is how you would do that: https://stackoverflow.com/questions/20361235/django-set-user-permissions-when-user-is-automatically-created – souldeux Oct 29 '17 at 16:42
  • `decorator, re-write the permission test as a standalone function` I didn't quite understand this part, sorry my bad. So far I know that in the model, I have to use the permission in the `meta` class. – sphoenix Oct 29 '17 at 16:45
  • The `permissions` definition in your model defines the permissions users can have in relation to that model. You have defined a `view_client` permission, and now you need to add that permission to the relevant users. Use the `add` and `remove` methods detailed in the last link to set permissions in your signal; then when your view is processing any given request, it can look at request.user and see if that user has the appropriate permissions. Take some time to read over the three links I've provided and the documentation they link; they will fully explain how to solve your problem – souldeux Oct 29 '17 at 16:53
  • I was thingking something like this. So, with the help of this, if another user kind types in the url `/client/contact` he won't be able to view it, right? – sphoenix Oct 29 '17 at 16:56
  • If you implement it properly, yes; you need to read the docs I have linked for you to understand how it works and exactly what you need to do. Again: https://docs.djangoproject.com/en/1.11/topics/class-based-views/intro/#decorating-the-class The examples here use `login_required` as the method decorator but you could easily pass in your own test. – souldeux Oct 29 '17 at 17:00
  • Thanks for your help – sphoenix Oct 29 '17 at 17:01
  • I have edited in a code example and a bit of extra info; I hope this helps. – souldeux Oct 29 '17 at 17:28