在Python中使用装饰器处理异常

GoB*_*ear 6 python

我正在使用 django 框架编写一个应用程序,并正在考虑编写类似休息控制器的东西,因为将异常处理逻辑保留在一个地方似乎是一个好主意。我不知道执行此操作的正确方法是什么,但我想出了一个装饰器,其中包含一堆可能由各种方法引发的异常,因此,每个方法都使用该装饰器。

def exception_handler(function):
    def wrapper(*args, **kwargs):
        try:
            return function(*args, **kwargs)
        except Error1 as error:
            return Response(
                {"Error": str(error)},
                status=status.HTTP_400_BAD_REQUEST
            )
        except Error2 as error:
            return Response(
                {"Error": str(error)},
                status=status.HTTP_404_NOT_FOUND
            )
        except Error3 as error:
            return Response(
                {"Error": str(error)},
                status=status.HTTP_503_SERVICE_UNAVAILABLE
            )
     return wrapper
Run Code Online (Sandbox Code Playgroud)

其中Error1Error2Error3只是一些抽象错误。在我的应用程序中确实有更多这样的情况。

简单的控制器(又名 django 视图)可能看起来像这样:

class DeviceView(viewsets.ModelViewSet):
    lookup_field = 'id'
    serializer_class = DeviceSerializer

    @exception_handler
    def create(self, request):
        serializer = self.serializer_class(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save(data=request.data)
        return Response(status=status.HTTP_200_OK)
Run Code Online (Sandbox Code Playgroud)

因此,基本上,如果抛出任何异常,都会以适当的方式进行处理。我在这里看到的问题之一是,如果不是我提出带有所需消息的异常:

if some_condition:
    raise SomeException("Something happened")
Run Code Online (Sandbox Code Playgroud)

这将是默认的,我通常更愿意更改它。因此,我开始对控制向客户显示哪些消息感到有些不舒服。我现在能想到的最好的办法是:

try:
    this_function_throws_someexception(args)
except SomeException:
    raise SomeException("Here is the message I want to show to the client")
Run Code Online (Sandbox Code Playgroud)

这意味着我必须重新引发抛出的异常以及我希望引发的消息。我的第一个问题是“这是处理这个问题的最佳方法吗?”。第二个问题是:“整个异常处理方法好吗?”

Mr-*_*ams 2

书籍和专业人士推荐使用的方法是进行mixin双重继承

文档:将 Mixin 与 CBV 结合使用

当使用DOUBLE INHERITANCE时,Mixin 上写入的所有内容都将重叠/覆盖并与第一个导入合并。

class A:  
    function gives a  
    no var defined  
    exception 1

class Bmixin:  
    function gives b
    var is b

class X(A, Bmixin):
    function gives b
    exception 1
    var is b (first was a but then was overriden by the Mixin because the 
    function is named equally)
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,X 类拥有两个类中的所有内容,并且覆盖了两个类中类似定义的内容。

通常,您定义一个基本的 ErrorHandling Mixin 和一些更具体的 Mixin,然后根据最终视图的需要堆叠尽可能多的 Mixin。

A 类(ViewStuff、ErrorBasicMixin、ErrorMoreSpecificMixin):

首先,你将所有的东西放在一个类上,最后以 Mixin 的名称命名(只是一个约定,但非常有用),其中包括你想要添加到类中的额外内容,在本例中是所有异常。

例如

class ErrorHandlingMixing():   #No need of inheritance inside brackets "()"
    #code
Run Code Online (Sandbox Code Playgroud)

大多数人为 mixins 制作不同的 .py 文件,例如 mixins.py

然后将其导入到您的视图中:

from .mixins import ErrorHandlingMixing
Run Code Online (Sandbox Code Playgroud)

并在创建视图类时进行双重继承

class User (AmbstractBaseUser, PermissionsMixin):
    #code
Run Code Online (Sandbox Code Playgroud)

还有很多已经为此创建的 mixin,例如 PermissionsMixin

from django.contrib.auth.models import PermissionsMixin
Run Code Online (Sandbox Code Playgroud)

我会给你一个来自 github 的 mixins.py 的例子

我想说装饰器方法更倾向于决定是否应该处理视图,例如使用权限。我认为你可以尝试使用装饰器来做到这一点,但 Mixins 看起来更干净,重复性更少,并且是专业人士的默认方法。

亲切的问候