Как улучшить обработку исключений в Python / Django - PullRequest
5 голосов
/ 17 апреля 2020

Это пример моей обработки исключений в django проекте:

def boxinfo(request, url: str):
    box = get_box(url)
    try:
        box.connect()
    except requests.exceptions.ConnectionError as e:
        context = {'error_message': 'Could not connect to your box because the host is unknown.'}
        return render(request, 'box/error.html', context)
    except requests.exceptions.RequestException as e:
        context = {'error_message': 'Could not connect to your box because of an unknown error.'}
        return render(request, 'box/error.html', context)
  • Сейчас есть только два исключения, но это должно быть больше для нескольких исключений запроса. Но метод представления уже раздут от этого. Есть ли способ переслать обработку исключений в отдельный метод ошибки?
  • Существует также проблема, которая мне нужна здесь для вызова сообщения рендеринга для каждого, кроме, я бы хотел этого избежать.
  • И здесь я также повторяю для каждого, кроме «не удалось подключиться к вашей коробке, потому что», это должно быть установлено один раз, когда появилось какое-либо исключение.

Я могу решить это чем-то вот так:

try:
    box.connect()
except Exception as e:
    return error_handling(request, e)

-

def error_handling(request, e):
    if type(e).__name__ == requests.exceptions.ConnectionError.__name__:
        context = {'error_message': 'Could not connect to your box because the host is unknown.'}
    elif type(e).__name__ == requests.exceptions.RequestException.__name__:
        context = {'error_message': 'Could not connect to your box because of an unknown error.'}
    else:
        context = {'error_message': 'There was an unkown error, sorry.'}
    return render(request, 'box/error.html', context)

и тогда я, конечно, смогу улучшить сообщение об ошибке. Но в целом, это ли Pythoni c способ обработки исключений с if/else? Например, я не смог бы поймать RequestException здесь, если выброшено ConnectionError, поэтому мне нужно было бы отлавливать ошибку каждого запроса, которая больше похожа на уродливую игру ...

Ответы [ 2 ]

10 голосов
/ 22 апреля 2020

Это вариант использования для декораторов . Если это что-то более общее, применимо ко всем представлениям (скажем, ведение журнала ошибок), вы можете использовать Django ловушку промежуточного программного обеспечения исключений , но здесь это не так.

Что касается проблемы с повторяющейся строкой ошибок, то Pythoni c способ ее решения состоит в том, чтобы иметь постоянную базовую строку со вставкой {replaceable_parts}, чтобы в дальнейшем вы могли .format() их.

Допустим, у нас есть следующий файл decorators.py:

import functools

from django.shortcuts import render
from requests.exceptions import ConnectionError, RequestException


BASE_ERROR_MESSAGE = 'Could not connect to your box because {error_reason}'


def handle_view_exception(func):
    """Decorator for handling exceptions."""
    @functools.wraps(func)
    def wrapper(request, *args, **kwargs):
        try:
            response = func(request, *args, **kwargs)
        except RequestException as e:
            error_reason = 'of an unknown error.'
            if isinstance(e, ConnectionError):
                error_reason = 'the host is unknown.'
            context = {
              'error_message': BASE_ERROR_MESSAGE.format(error_reason=error_reason),
            }
            response = render(request, 'box/error.html', context)
        return response

    return wrapper

Мы используем тот факт, что ConnectionError является подклассом RequestException в библиотеке запросов . Мы также могли бы создать словарь с классами исключений в качестве ключей, но проблема здесь в том, что это не будет обрабатывать наследование классов исключений , которое является тем упущением, которое позже выдает тонкие ошибки. Функция isinstance является более надежным способом выполнения этой проверки.

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

Тогда в ваших взглядах:

from .decorators import handle_view_exception

@handle_view_exception
def boxinfo(request, url: str):
    box = get_box(url)
    box.connect()
    ...

Таким образом, логи обработки ошибок c полностью отделены от ваших представлений, и, что самое главное, их можно использовать повторно.

0 голосов
/ 17 апреля 2020

могли бы вы иметь что-то вроде этого:

views.py

EXCEPTION_MAP = {
    ConnectionError: "Could not connect to your box because the host is unknown.", 
    RequestException: "Could not connect to your box because of an unknown error.",
}

UNKNOWN_EXCEPTION_MESSAGE = "Failed due to an unknown error."


def boxinfo(request, url: str):
    box = get_box(url)
    try:
        box.connect()
    except (ConnectionError, RequestException) as e:
        message = EXCEPTION_MAP.get(type(e)) or UNKNOWN_EXCEPTION_MESSAGE
        context = {'error_message': message}
        return render(request, 'box/error.html', context)

Затем вы можете просто развернуть EXCEPTION_MAP и except () для любых других известных типов исключений, которые вы ' Вы хотите поймать?

, если хотите уменьшить дублирование "Could not connect to your box because ...

Возможно, вы можете сделать:

views.py

BASE_ERROR_STRING = "Could not connect to your box because {specific}"

EXCEPTION_MAP = {
    ConnectionError: "the host is unknown.", 
    RequestException: "of an unknown error.",
}
UNKNOWN_EXCEPTION_MESSAGE = "Failed due to an unknown error."

def boxinfo(request, url: str):
    box = get_box(url)
    try:
        box.connect()
    except (ConnectionError, RequestException) as e:
        specific_message = EXCEPTION_MAP.get(type(e))
        if specific_message:
             message = BASE_ERROR_STRING.format(specific=specific_message)
        else:
             message = UNKNOWN_EXCEPTION_MESSAGE
        context = {'error_message': message}
        return render(request, 'box/error.html', context)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...