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

Python 代码重构帮助-如何重新组织验证,python,django,optimization,refactoring,web-applications,Python,Django,Optimization,Refactoring,Web Applications,我们有一个web应用程序,它接受用户输入或数据库查找,以形成针对某些物理资源的一些操作。设计可以简单地表示为下图: 用户输入模型对象数据库存储 对于来自用户输入的请求,需要进行验证,但对于来自数据库查找命中的请求,则不需要进行验证(因为如果存在记录,则这些属性之前必须已经过验证)。我正在尝试重构代码,以便验证在对象构造函数中进行,而不是以旧的方式(几个单独的验证例程) 你如何决定哪种方式更好?(方法1(旧方法)和方法2的根本区别在于,方法1中的验证不是强制性的,并且与对象实例化分离,但方法2将它

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

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

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

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

下面是design 1和design 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)
# 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:

# 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
# 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:

# 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:

# 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
# 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更接近于业务逻辑。当对象来自数据库查找时,不会进行冗余验证检查。验证例程具有更好的可维护性和可读性
  • 方法2使调用者变得简单和干净。即使来自数据库查找,验证也是必需的。验证的可维护性和可读性较差

  • 在构造函数中进行验证实际上不是“Django方式”。由于需要验证的数据来自客户端,因此使用(可能与)是最惯用的验证方法,因为它将所有关注点封装到一个API中:它提供合理的验证默认值(具有轻松定制的能力),加上模型表单集成了数据输入端(html表单)使用数据提交(model.save())

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

    首先,对模型本身进行一些验证并不是“非Djangonic的”——毕竟,html表单提交可能不是新数据的唯一来源。您可以重写save()方法,也可以使用来清除保存时的数据,或者对无效数据引发异常。从长远来看,Django将进行模型验证,但目前还没有;在此期间,您应该考虑这是一个“安全”,以确保您不提交无效的数据到您的数据库。换句话说,在提交之前,您仍然需要逐个字段进行验证,以便知道在无效输入时向用户显示什么错误


    我的建议是这样的。为需要验证的每个项目创建新的表单类,即使最初没有使用它们。概述了使用表单系统中提供的挂钩进行模型验证的技术。仔细阅读(它真的非常简单和优雅),然后加入你的模型。一旦定义了newforms类并开始工作,您就会发现,如果您删除现有的表单模板和相应的验证,并使用表单框架处理表单帖子,这并不困难,事实上也会大大简化您的代码。这是一个学习过程,但是forms API经过了非常周密的考虑,您将非常感激它能使您的代码更加简洁。

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

    因此,听起来在模型中进行验证不是一个坏主意,但从另一个意义上讲,我在视图中针对请求进行验证的旧方法似乎也接近于将它们封装在newforms API中的概念(数据验证与模型创建分离)。你认为保留我的旧设计可以吗?对我来说,用NewFormsAPI来处理这个问题比现在处理它们更有意义


    (我从我的代码评审员那里得到了这个重构建议,但我真的不太确定我的旧方法是否违反了任何mvc模式或太复杂而无法维护。我认为我的方法更有意义,但我的评审员认为将验证和模型创建绑定在一起更有意义…

    我建议你缩短你的问题,总结并重新格式化代码(嵌入式编辑器中有“代码”标记按钮)。您肯定应该缩短问题。这也是一个真正的django项目。如果是的话,您应该解释为什么没有在表单上实现验证(就像Django方式一样)。最初,开发人员不使用表单,而是使用数据上下文,表单构建在客户端。当我拿它的时候,这个设计没有改变。是的,这个问题太不清楚了,无法回答。上面的代码片段脱离上下文,引用了未解释的名称、方法和对象。我甚至不知道你想做什么。这段代码有点Java味道。作为一个非常普遍的观点,尽量在构造函数之外保留尽可能多的逻辑。在构造函数中包含逻辑会使单元测试变得更加困难,而强大的单元测试对于任何重构或维护都非常重要。