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 используется только одним видом).
Есть ли правильный способ решить такие проблемы?
Ответы
Ответ 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