可以从调用函数的地方访问的状态代码有哪些替代方案?例外? (Python, 姜戈)

What are alternatives to status codes that can be accessed from where a function is called? Exceptions? (Python, Django)

我想在函数中引发异常,然后检查其他地方(在 Django 视图和我的单元测试中)是否引发异常。下面的代码使用了状态代码,并且它有效。但是我不知道如何用例外做同样的事情——每个人似乎都同意,这是做这种事情的正确方法。

使用自定义错误消息对我来说很重要。不是打印它们,而是在代码中检测和使用它们(主要是用 Django 消息将它们转发给最终用户)。

如果在 utils.add_foo 中出现异常,我不知道如何检查 add_foo_view

在单元测试中,我尝试了 <a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertWarnsRegex" rel="nofollow noreferrer">assertWarnsRegex</a>(Warning, 'blah went wrong') 之类的操作,但没有费心检查消息是否正确其实是一样的

views.py:

from django.contrib import messages

from .utils import add_foo


def add_foo_view(request):
    if request.method == 'POST':

        status = add_foo(request.POST['bar'])
        if not status == 'Bar was added.':
            messages.error(request, status)

        return render(request, 'index.html')
    else:
        return render(request, 'add_foo.html')

utils.py:

def add_foo(bar):

    if not spamifyable(bar):
        return 'Bar can not be spamified.'

    try:
        eggs = Egg.objects.get(baz=bar)
    except:
        return 'Bar has no eggs.'

    do_things(bar)

    return 'Bar was added.'

tests.py:

def test_bar_without_eggs(self):

    status = add_foo(eggless_bar)

    assertEqual(status, 'Bar has no eggs.')

我使用 Python 3.5.2 和 Django 1.11.4。

编辑: 我实际上不确定异常是否是此处的正确选择。我经常读到,例外只适用于意想不到的事情。但是我在这里遇到的情况是用户的错误输入,这是意料之中的。 所以我的问题不是真正的如何使它有例外,而是如何使它成为正确的 pythonic 方式。 无论如何我希望验证在单独的 utils 地方(普通 Python,没有 Django),而不是在视图中。

您可以使用 'raise' 语句引发异常,例如:

raise Exception("Bar has no eggs.")

您还可以通过继承内置异常 class 来创建自定义异常,例如:

class MyException(Exception):
    pass

那么你可以这样做:

raise MyException("Bar has no eggs.")

并且您可以使用 try-except 块捕获引发的异常:

try:
    function_that_raises_exception(args)
except MyException:
    function_to _handle_exception(args)

所以在你的views.py中你可以这样做:

from django.contrib import messages

from .utils import add_foo, MyException


def add_foo_view(request):
    if request.method == 'POST':
        try:
            add_foo(request.POST['bar'])
        except MyException as e:
            messages.error(request, str(e))

        return render(request, 'index.html')
    else:
        return render(request, 'add_foo.html')

add_foo 编辑的 return 明确的成功消息可能看起来更清晰,但可能不是一个好主意。该函数不需要 return 任何东西。如果没有抛出异常,则可以认为是成功的。

我可以创建自定义例外并在 __init__ 中添加 message 属性。这样它就可以作为 e.message 访问,在我的例子中,它将被转发到 views.py.

中的索引页面

utils.py

class AddFooException(Exception):

    def __init__(self, message):
        self.message = message


def add_foo(bar):

    if not spamifyable(bar):
        raise AddFooException('Bar can not be spamified.')

    try:
        eggs = Egg.objects.get(baz=bar)
    except:
        raise AddFooException('Bar has no eggs.')

    do_things(bar)

views.py

from django.contrib import messages

from .utils import add_foo, AddFooException


def add_foo_view(request):
    if request.method == 'POST':
        bar = request.POST['bar']

        try:
            add_foo(bar)
        except AddFooException as e:
            messages.error(request, e.message)


        return render(request, 'index.html')
    else:
        return render(request, 'add_foo.html')