Как запретить пользователю одного вида доступ к представлению другого пользователя через URL-адрес в django?

В моем проекте у меня есть разные типы пользователей, например:

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

Ни один пользователь не может получить доступ к представлению другого пользователя с помощью url. Клиент не может получить доступ к профилю технического менеджера через URL-адрес /officer/profile, назначенный для профиля технического менеджера.

Для этого в моем Client class в models.py я использовал этот фрагмент кода:

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

Затем для каждого представления я использовал такой декоратор: @permission_required(lambda u: u.has_perm('authentication.view_client'))

Затем я вхожу в систему как технический менеджер и пытаюсь получить доступ по этому URL-адресу: /client/profile

Затем я получил ошибку function object is not iterable.

Я создавал своего пользователя в приложении под названием authentication. models,py выглядит так: '

@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()

'

Профильный класс довольно большой. Пожалуйста, игнорируйте это. Теперь я хотел, чтобы я назначал разрешение каждый раз, когда создается другой пользователь любого типа.

Как устранить ошибку и как я могу это сделать, чтобы один тип пользователя не мог получить доступ к представлению других, набрав URL-адрес?

Отредактировано после комментария от souldeux

Вот одно из моих представлений, где клиент может обновить свою контактную информацию:

@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})

И ошибка, которую я получил:

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

person sphoenix    schedule 29.10.2017    source источник
comment
Не могли бы вы опубликовать полную трассировку для вашей ошибки и вашего мнения? Представление — это место, где мы будем проверять, может ли данный запрос получить доступ к запрошенному контенту.   -  person souldeux    schedule 29.10.2017
comment
Я отредактировал свой вопрос.   -  person sphoenix    schedule 29.10.2017
comment
Я также разместил трассировку   -  person sphoenix    schedule 29.10.2017


Ответы (1)


Вместо того, чтобы передавать лямбда-функцию в декораторе permission_required, перепишите тест разрешения как отдельную функцию и примените его через method_decorator, который работает с методом dispatch:

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

Как использовать декораторы, требующие разрешения, на основе классов django просмотры

Вы также можете использовать @user_passes_test для украшения представлений на основе функций. Например, возможно, есть представление, которое вы хотите видеть только для тех пользователей, которые связаны с объектом Client:

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

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

Вы также можете проверить права пользователя в своем тесте: https://docs.djangoproject.com/en/1.11/ref/contrib/auth/#django.contrib.auth.models.User.has_perm

person souldeux    schedule 29.10.2017
comment
Нужно ли назначать разрешение в post signal каждый раз, когда создается пользователь? - person sphoenix; 29.10.2017
comment
Если вы хотите одинакового поведения для всех пользователей, то да. Вот как это сделать: stackoverflow.com/questions/20361235/ - person souldeux; 29.10.2017
comment
decorator, re-write the permission test as a standalone function Я не совсем понял эту часть, извините за ошибку. Пока я знаю, что в модели я должен использовать разрешение в классе meta. - person sphoenix; 29.10.2017
comment
Определение permissions в вашей модели определяет разрешения, которые пользователи могут иметь в отношении этой модели. Вы определили разрешение view_client, и теперь вам нужно добавить это разрешение для соответствующих пользователей. Используйте методы add и remove, описанные в последней ссылке, чтобы установить разрешения в вашем сигнале; затем, когда ваше представление обрабатывает любой заданный запрос, оно может просмотреть request.user и посмотреть, есть ли у этого пользователя соответствующие разрешения. Найдите время, чтобы прочитать три ссылки, которые я предоставил, и документацию, на которую они ссылаются; они полностью объяснят, как решить вашу проблему - person souldeux; 29.10.2017
comment
Я думал о чем-то подобном. Итак, с помощью этого, если другой тип пользователя введет URL-адрес /client/contact, он не сможет его просмотреть, верно? - person sphoenix; 29.10.2017
comment
Если вы реализуете это правильно, да; вам нужно прочитать документы, которые я связал, чтобы вы поняли, как это работает и что именно вам нужно делать. Опять же: docs.djangoproject.com /en/1.11/topics/class-based-views/intro/ В приведенных здесь примерах используется login_required в качестве декоратора метода, но вы можете легко пройти свой собственный тест. - person souldeux; 29.10.2017
comment
Спасибо за вашу помощь - person sphoenix; 29.10.2017
comment
Я отредактировал пример кода и немного дополнительной информации; Надеюсь, это поможет. - person souldeux; 29.10.2017