什么是可以从调用函数的位置访问的状态代码的替代方法?例外? (Python,Django)

时间:2022-04-12 16:04:12

I would like to raise an exception in a function, and then check somewhere else (in the Django view and my unit tests) if it was raised. The following code uses status codes, and it works. But I can't figure out how to do the same thing with exceptions - which, everyone seems to agree, are the right way to do this kind of thing.

我想在函数中引发异常,然后检查其他地方(在Django视图和我的单元测试中)是否被引发。以下代码使用状态代码,它可以工作。但我无法弄清楚如何用异常做同样的事情 - 每个人似乎都同意,这是做这种事情的正确方法。

It is important to me to use custom error messages. Not to print them, but to detect and use them in the code (mainly to forward them to the end user with Django messages).

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

I have no idea how I would check in add_foo_view if an exception was raised in utils.add_foo.

如果在utils.add_foo中引发异常,我不知道如何检查add_foo_view。

In the unit test I have tried things like assertWarnsRegex(Warning, 'blah went wrong'), but that did not bother to check if the message is actually the same.

在单元测试中,我尝试了一些像assertWarnsRegex(警告,'blah出错')这样的东西,但是没有费心去检查消息是否实际上是相同的。

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.')

I use Python 3.5.2 and Django 1.11.4.

我使用Python 3.5.2和Django 1.11.4。

Edit: I am not actually sure if exceptions would be the correct choice here. I often read, that exceptions are only for things that are unexpected. But the cases I am catching here are wrong inputs by the user, which are very much expected. So my question is not really how to make this with exceptions, but how to make this the right and pythonic way. In any case I want the validation to happen in the separate utils place (plain Python, no Django), and not in the view.

编辑:我不确定异常是否是正确的选择。我经常读到,例外仅适用于意外事件。但我在这里捕获的案例是用户的错误输入,这是非常期待的。所以我的问题不是如何通过例外来实现这一点,而是如何使这成为正确和pythonic的方式。无论如何,我希望验证发生在单独的utils位置(普通Python,没有Django),而不是在视图中。

2 个解决方案

#1


0  

You can use the 'raise' statement to raise an exception, like:

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

raise Exception("Bar has no eggs.")

You can also create custom exceptions by inheriting from the Exception in-built class, like:

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

class MyException(Exception):
    pass

then you can do:

然后你可以这样做:

raise MyException("Bar has no eggs.")

And you can catch the raised exceptions using a try-except block:

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

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

So in your views.py you can do:

所以在你的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')

#2


0  

An explicit success message returned by add_foo may seem clearer, but is probably not a good idea. The function does not need to return anything. If no exception is raised, it can be assumed that it was successful.

add_foo返回的显式成功消息可能看起来更清晰,但可能不是一个好主意。该函数不需要返回任何内容。如果没有引发异常,则可以认为它是成功的。

I can create a custom exception and add the message property in the __init__. This way it can be accessed as e.message, which in my case will be relayed to the index page in views.py.

我可以创建一个自定义异常并在__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')

#1


0  

You can use the 'raise' statement to raise an exception, like:

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

raise Exception("Bar has no eggs.")

You can also create custom exceptions by inheriting from the Exception in-built class, like:

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

class MyException(Exception):
    pass

then you can do:

然后你可以这样做:

raise MyException("Bar has no eggs.")

And you can catch the raised exceptions using a try-except block:

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

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

So in your views.py you can do:

所以在你的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')

#2


0  

An explicit success message returned by add_foo may seem clearer, but is probably not a good idea. The function does not need to return anything. If no exception is raised, it can be assumed that it was successful.

add_foo返回的显式成功消息可能看起来更清晰,但可能不是一个好主意。该函数不需要返回任何内容。如果没有引发异常,则可以认为它是成功的。

I can create a custom exception and add the message property in the __init__. This way it can be accessed as e.message, which in my case will be relayed to the index page in views.py.

我可以创建一个自定义异常并在__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')