代码重构帮助 - 如何重新组织验证

时间:2008-12-21 09:40:28

标签: python django optimization refactoring web-applications

我们有一个Web应用程序,它接受用户输入或数据库查找,以针对某些物理资源形成一些操作。设计可以简单地显示如下图:

用户输入< =>模型对象< =>数据库存储

来自用户输入的请求需要验证,但是当来自数据库查找命中时不需要验证(因为如果存在记录,那么这些属性之前必须已经过验证)。我试图重构代码,以便验证发生在对象构造函数而不是旧的方式(单独的几个验证例程)

您如何决定哪种方式更好? (方法1(旧方法)和2的根本区别在于1中的验证不是强制性的,并且与对象实例化分离,但是2将它们绑定并使它们对所有请求都是强制的)

以下是设计1和2的两个示例代码段:

方法1:

# For processing single request.
# Steps: 1. Validate all incoming data. 2. instantiate the object.
ValidateAttribures(request) # raise Exceptions if failed
resource = Resource(**request)

方法2:

# Have to extract out this since it does not have anything to do with
# the object.
# raise Exceptions if some required params missing.
# steps: 1. Check whether its a batching request. 2. instantiate the object.
#           (validations are performed inside the constructor)
CheckIfBatchRequest(request) 
resource = Resource(**request) # raise Exceptions when validations failed

在批量请求中: 方法1:

# steps: 1. validate each request and return error to the client if any found.
#        2. perform the object instantiate and creation process. Exceptions are
#           captured.
#        3. when all finished, email out any errors.
for request in batch_requests:
    try:    
        ValidateAttribute(request)
    except SomeException, e:
        return ErrorPage(e)
errors = []
for request in batch_requests:
    try:
        CreatResource(Resource(**request), request)
    except CreationError, e:
        errors.append('failed to create with error: %s', e)
email(errors)

方法2:

# steps: 1. validate batch job related data from the request.
#        2. If success, create objects for each request and do the validations.
#        3. If exception, return error found, otherwise, 
#           return a list of pairs with (object, request)
#        4. Do the creation process and email out any errors if encountered.
CheckIfBatchRequest(request)
request_objects = []
for request in batch_requests:
    try:
        resource = Resource(**request)
    except SomeException, e:
        return ErrorPage(e)
    request_objects.append((resource, request))
email(CreateResource(request_objects)) # the CreateResource will also need to be refactored.

我在这里可以看到的优点和缺点是:

  1. 方法1更接近业务逻辑。没有冗余验证检查对象何时来自db查找。验证程序可以更好地维护和阅读。
  2. 方法2使呼叫者轻松清洁。即使从db查找,验证也是必需的。验证不易维护和阅读。

2 个答案:

答案 0 :(得分:1)

在构造函数中进行验证确实不是“Django方式”。由于您需要验证的数据来自客户端,因此使用new forms(可能带有ModelForm)是最常用的验证方法,因为它将您的所有问题都包含在一个API中:提供合理的验证默认值(具有轻松定制的能力),加上模型表单将数据输入端(html表单)与数据提交(model.save())集成。

然而,听起来你可能有一个遗留项目的混乱;至少在最初阶段,重写所有表单处理以使用新表单可能超出了您的时间范围。所以这是我的想法:

首先,在模型本身中进行一些验证不是“非Djangonic” - 毕竟,html表单提交可能不是新数据的唯一来源。您可以覆盖save()方法或使用signals清除保存数据或在无效数据上抛出异常。从长远来看,Django将进行模型验证,但它还没有;在此期间,您应该将此视为“安全”,以确保您不会向数据库提交无效数据。换句话说,您仍需要在提交之前逐字段验证,以便了解在无效输入时向用户显示的错误。

我建议的是这个。为您需要验证的每个项目创建新的表单类,即使您最初没有使用它们也是如此。 Malcolm Tredinnick概述了使用表单系统中提供的钩子进行模型验证的技术。阅读(它非常简单和优雅),并加入你的模型。一旦你定义并运行了newforms类,你会发现它并不是很困难 - 实际上会大大简化你的代码 - 如果你撕掉现有的表单模板和相应的验证,并使用形式框架。有一点学习曲线,但表格API经过深思熟虑,你会感激它会使你的代码更清晰。

答案 1 :(得分:0)

感谢Daniel的回复。特别是对于newforms API,我肯定会花时间深入研究它,看看我是否可以采用它来获得更好的长期效益。但只是为了让我的工作完成这个迭代(在EOY之前达到我的截止日期),我可能仍然必须坚持当前的遗留结构,毕竟,无论哪种方式都会让我达到我想要的,只是我希望尽可能保持理智和干净,不要打破太多。

所以听起来像在模型中进行验证并不是一个太糟糕的想法,但从另一个意义上讲,我在针对请求的视图中进行验证的旧方法似乎也接近于将它们封装在newforms API中的概念(数据验证)与模型创建分离)。你认为保留旧设计是否可以?使用newforms API触摸它而不是现在处理它们更有意义......

(好吧,我从我的代码审查员那里得到了这个重构建议,但我真的不太确定我的旧方式违反任何m​​vc模式或太复杂而无法维护。我认为我的方式更有意义,但我的审稿人认为绑定验证和模型创造在一起更有意义......)