Подтвердить что ты не робот

Django: представление на основе класса с помощью mixins и метода отправки

Обычно я использую метод dispatch для представления на основе класса для установки некоторых начальных переменных или добавления некоторой логики на основе пользовательских разрешений.

Например,

from django.views.generic import FormView
from braces.views import LoginRequiredMixin

class GenerateReportView(LoginRequiredMixin, FormView):
    template_name = 'reporting/reporting_form.html'
    form_class = ReportForm

    def get_form(self, form_class):
        form = form_class(**self.get_form_kwargs())
        if not self.request.user.is_superuser:
            form.fields['report_type'].choices = [
                choice for choice in form.fields['report_type'].choices
                if choice[0] != INVOICE_REPORT
            ]
        return form

Он работает так, как ожидалось: когда анонимный пользователь посещает страницы, вызывается метод dispatch LoginRequiredMixin, а затем перенаправляет пользователя на страницу входа в систему.

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

class GenerateReportView(LoginRequiredMixin, FormView):

    def dispatch(self, *args, **kwargs):
        if not (
            self.request.user.is_superuser or
            self.request.user.is_manager
        ):
            raise Http404
        return super(GenerateReportView, self).dispatch(*args, **kwargs)

в некоторых случаях это не работает, потому что методы dispatch для mixins, которые наследует представление, еще не были вызваны. Так, например, чтобы иметь возможность запрашивать разрешения пользователя, я должен повторить проверку с LoginRequiredMixin:

class GenerateReportView(LoginRequiredMixin, FormView):

    def dispatch(self, *args, **kwargs):
        if self.request.user.is_authenticated() and not (
            self.request.user.is_superuser or
            self.request.user.is_manager
        ):
            raise Http404
        return super(GenerateReportView, self).dispatch(*args, **kwargs)

Этот пример прост, но иногда в смешении есть еще более сложная логика: он проверяет разрешения, делает некоторые вычисления и сохраняет их в атрибуте класса и т.д.

Теперь я разрешаю его, копируя некоторый код из mixin (как в примере выше) или копируя код из метода dispatch представления в другой mixin и наследуя его после первого, чтобы выполнить их в (это не так, потому что этот новый mixin используется только одним видом).

Есть ли правильный способ решить такие проблемы?

4b9b3361

Ответ 1

Я бы написал пользовательский класс, который проверяет все разрешения

from django.views.generic import FormView
from braces.views import AccessMixin

class SuperOrManagerPermissionsMixin(AccessMixin):
    def dispatch(self, request, *args, **kwargs):
        if not request.user.is_authenticated():
            return self.handle_no_permission(request)
        if self.user_has_permissions(request):
            return super(SuperOrManagerPermissionsMixin, self).dispatch(
                request, *args, **kwargs)
        raise Http404 #or return self.handle_no_permission

    def user_has_permissions(self, request):
        return self.request.user.is_superuser or self.request.user.is_manager

# a bit simplyfied, but with the same redirect for anonymous and logged users
# without permissions


class SuperOrManagerPermissionsMixin(AccessMixin):
    def dispatch(self, request, *args, **kwargs):
        if self.user_has_permissions(request):
            return super(SuperOrManagerPermissionsMixin, self).dispatch(
                request, *args, **kwargs)
        else:
            return self.handle_no_permission(request)

    def user_has_permissions(self, request):
        return request.user.is_authenticated() and (self.request.user.is_superuser
                                                    or self.request.user.is_manager)


class GenerateReportView(SuperOrManagerPermissionsMixin, FormView):
#Remove next two lines, don't need it
    def dispatch(self, *args, **kwargs):
        #or put some logic here
        return super(GenerateReportView, self).dispatch(*args, **kwargs)

И реализация класса GenerateReportView (SuperOrManagerPermissionsMixin, FormView) не требует переопределения метода отправки

Если вы используете множественное наследование, и один из родительских классов нуждается в некотором улучшении, полезно сначала его улучшить. Он очищает код.

Ответ 2

Это старый пост, но другие люди могут столкнуться, так вот мое предложенное решение.

Когда вы говорите

"[...] Я хочу добавить некоторые разрешения для этого представления или установить некоторые начальные переменные, например [...]"

Вместо того, чтобы устанавливать эти начальные переменные в методе отправки вашего представления, вы можете написать отдельный метод для настройки этих переменных, а затем вызвать этот метод в методе get (и post if needed). Они вызываются после отправки, поэтому настройка начальных переменных не будет конфликтовать с отправкой в ​​ваших миксинах. Поэтому переопределите метод

def set_initial_variables():
    self.hey = something
    return 

def get(blablabla):
    self.set_initial_variables()
    return super(blabla, self).get(blabla)

Это, вероятно, более чистое, чем копирование и вставка кода вашего микшина в вашу диспетчерскую версию.

Ответ 3

В примере, который вы указали, я использовал бы UserPassesTestMixin из django-braces.

class GenerateReportView(UserPassesTestMixin, FormView):
    def test_func(self, user):
        return user.is_superuser or user.is_manager

Если это не подходит для вашей более сложной логики, то создание отдельного микшина звучит как подход OK, поскольку он интуитивно усложняет сложную логику.

ИЗМЕНИТЬ
Начиная с django 1.9, UserPassesTestMixin теперь включен в django: https://docs.djangoproject.com/en/1.11/topics/auth/default/#django.contrib.auth.mixins.UserPassesTestMixin