我正在构建一个Django应用程序,并希望就我的代码中处理错误和错误的正确方法提供一些建议。
以下是我遇到的问题的常见情况:用户购买产品。要处理购买,我的观点需要执行许多操作:
User
对象。Order
对象并将其分配给新创建的用户。Product
对象并将其添加到新创建的订单中。当没有错误发生时,这一切都很好 - 但我发现偶然的错误在我的代码中是不可避免的,我希望我的应用程序优雅地处理错误,而不是直接崩溃。例如,如果由于任何原因无法创建Order
对象,则视图应向用户显示错误并删除先前创建的User
对象。并且,它应该抛出一个优雅的错误消息,而不是直接崩溃并为用户提供Http 500错误。
我能想到的唯一方法是使用一系列非常复杂的嵌套try / except子句,如下所示。但是以这种方式设计我的代码是非常混乱和耗时的,并且它不是正确的做事方式。我知道必须有更好的方法来设计Django和Python中的正确错误处理,但我不太确定它是什么。
我非常感谢有关如何在这种情况下更好地构建代码的任何建议。
示例代码:
try:
# Create a new user
u = User(email='test@test.com')
u.save()
try:
# Create a new order
o = Order(user=u, name='Order name')
o.save()
try:
# Create a new product
p = Product(order=o, name='Product name')
p.save()
# If a product cannot be created, print an error message and try deleting the user and order that were previously created
except:
messages.add_message(request, messages.ERROR, 'Product could not be created')
# If deleting the order doesn't work for any reason (for example, o.save() didn't properly save the user), 'pass' to ensure my application doesn't crash
try:
o.delete()
# I use these 'except: pass' clauses to ensure that if an error occurs, my app doesn't serve a Http 500 error and instead shows the user a graceful error
except:
pass
# If deleting the user doesn't work for any reason (for example, u.save() didn't properly save the user), 'pass' to ensure my application doesn't crash
try:
u.delete()
except:
pass
# If an order cannot be created, print an error message and try deleting the user that was previously created
except:
messages.add_message(request, messages.ERROR, 'Order could not be created')
# If deleting the user doesn't work for any reason (for example, u.save() didn't properly save the user), 'pass' to ensure my application doesn't crash
try:
u.delete()
except:
pass
# If the user cannot be created, throw an error
except:
messages.add_message(request, messages.ERROR, 'User could not be created')
答案 0 :(得分:3)
我建议使用transaction.atomic
块,它应该包含你的模型创建(django docs中的link):
try:
with transaction.atomic():
create_your_objects()
except IntegrityError:
handle_exception()
这样,在发生任何问题的情况下,上下文管理器内完成的任何更改都将自动回滚。
P.S。实际上这是django默认处理每个视图的方式,但是对于你的情况,因为你期望它失败然后你可以摆脱500错误并且仍然得到干净的数据库以防发生问题而不需要丢弃每个创建的对象。
答案 1 :(得分:-1)
这个怎么样?将您尝试创建的每件内容分配给None
。如果实例化它们(调用构造函数)会抛出异常,它们将保持None
。另一方面,如果它们被正确实例化,但未正确保存,则其主键将为None
。因此:
u = None
o = None
p = None
try:
# Create a new user
u = User(email='test@test.com')
u.save()
# Create a new order
o = Order(user=u, name='Order name')
o.save()
# Create a new product
p = Product(order=o, name='Product name')
p.save()
except:
if u == None or u.pk == None:
messages.add_message(request, messages.ERROR, 'User could not be created')
else if o == None or o.pk == None:
messages.add_message(request, messages.ERROR, 'Order could not be created')
else if p == None or p.pk == None:
messages.add_message(request, messages.ERROR, 'Product could not be created')
你可以通过循环遍历u,o和p的列表来进一步改进这一点,而不是像我那样重复代码,但这应该给你一般的想法。
顺便说一句:函数式编程提供了一些见解,因为纯函数式编程禁止异常抛出。这些语言有Maybe
monad(名称可以变化),它基本上是一个包装类,它有两个可能的值:对象本身,或None
,它有一个方法get_or_else
获取参数foo
,并返回它存储的对象(如果存在),如果它为null,则返回foo
。你可以在Python中轻松实现这一点,但我相信它被认为是unpythonic。