Создание пользовательских исключений, которые Django реагирует на

Для моего сайта я создал абстрактную модель, которая реализует разрешения на чтение на уровне модели. Эта часть системы завершена и работает правильно. Одним из методов, предоставляемых разрешенной моделью, является is_safe(user), который может вручную протестировать, если пользователю разрешено просматривать эту модель или нет.

Мне бы хотелось добавить метод, влияющий на continue_if_safe, который может быть вызван для любого экземпляра модели, и вместо того, чтобы возвращать логическое значение, например is_safe, сначала будет проверяться, можно ли просмотреть модель или нет, то в случае False он перенаправляет пользователя либо на страницу входа в систему, если они еще не вошли в систему, либо возвращают ошибку 403, если они вошли в систему.

Идеальное использование:

model = get_object_or_404(Model, slug=slug)
model.continue_if_safe(request.user)
# ... remainder of code to be run if it safe down here ...

Я заглянул в то, как работает get_object_or_404, и он выдает ошибку Http404, которая, кажется, имеет смысл. Однако проблема заключается в том, что, похоже, не существует эквивалентного перенаправления или 403 ошибки. Какой лучший способ сделать это?

(нерабочий) метод continue_if_safe:

def continue_if_safe(self, user):

    if not self.is_safe(user):
        if user.is_authenticated():
            raise HttpResponseForbidden()
        else:
            raise HttpResponseRedirect('/account/')

    return

Изменить - Решение

Код для окончательного решения, если другие "штабелеры" нуждаются в помощи с этим:

В абстрактной модели:

def continue_if_safe(self, user):
    if not self.is_safe(user):
        raise PermissionDenied()
    return

Представления пойманы промежуточным программным обеспечением:

class PermissionDeniedToLoginMiddleware(object):
    def process_exception(self, request, exception):
        if type(exception) == PermissionDenied:
            if not request.user.is_authenticated():
                return HttpResponseRedirect('/account/?next=' + request.path)
        return None

Использование в представлении (очень короткое и сладкое):

model = get_object_or_404(Model, slug=slug)
model.continue_if_safe(request.user)

Ответы

Ответ 1

Для запрещенной (403) ошибки вы можете создать исключение PermissionDenied (от django.core.exceptions).

Для поведения перенаправления нет встроенного способа справиться с этим так, как вы описываете в своем вопросе. Вы можете написать собственное промежуточное программное обеспечение, которое поймает ваше исключение и перенаправит его в process_exception.

Ответ 2

Я создал небольшое промежуточное программное обеспечение, которое возвращает все возвращаемые методы рендеринга класса Exception. Теперь вы можете использовать произвольное исключение (с методами визуализации) в любом из ваших представлений.

class ProductNotFound(Exception):
    def render(self, request):
        return HttpResponse("You could ofcourse use render_to_response or any response object")

pip install -e git+http://github.com/jonasgeiregat/django-excepted.git#egg=django_excepted

И добавьте django_excepted.middleware.ExceptionHandlingMiddleware к вашему MIDDLEWARE_CLASSES.

Ответ 3

Django раздражает решение для этого:

from annoying.exceptions import Redirect

...
    raise Redirect('/')  # or a url name, etc

Ответ 4

Вы хотите использовать декораторы для этого. Посмотрите login_required для примера. В основном декоратор позволит вам проверить безопасность, а затем вернуться HttpResponseRedirect(), если это не безопасно.

Ваш код будет выглядеть примерно так:

@safety_check:
def some_view(request):
    #Do Stuff