Code refactoring help - how to refactor checks

We have a web application that uses user logins or database searches to create some operations on some physical resources. The design can be simply represented as the following diagram:

user input <=> model object <=> database store

validations are required with a query coming from user input, but NOT when coming from database lookup queries (since, if a record exists, these attributes must be validated earlier). I'm trying to refactor my code so that checks are done in the object's constructor instead of the old one (several separate check routines)

How would you decide which path is best? (The main difference between method 1 (old way) and 2 is that checks in 1 are optional and not associated with an object instance, but 2 binds them and makes them mandatory for all requests)

Here are two code examples for design 1 and 2:

Method 1:

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

      

Method 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

      

In a batch request: Method 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)

      

Method 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.

      

Pros and cons as I see here:

  • Method 1 follows closer to business logic. No redundant checks checking objects when looking up db. Verification procedures are better maintained and read.
  • Method 2 simplifies and cleans up the caller. Validations are required even if from db search. Checks are less repairable and less readable.
0


source to share


2 answers


Doing validation in the constructor really isn't the "Django way". Since the data needed for validation comes from the client side, using new forms (perhaps with ModelForm ) is the most idiomatic method of validation, as it brings all your concerns into one API: it provides reasonable defaults (with the ability to easily customize). plus the form model combine the data entry side (html form) with the data commit (model.save ()).

However, it looks like you have what might be a clutter of a legacy project; you can go beyond your time to rewrite all your form processing to use the new forms, at least initially. So here are my thoughts:



First of all, it's not "non-Djangonic" to put some validation into the model itself. After all, html form submissions may not be the only source of new data. You can override the save () method or use signals to clear data when saving or throw exceptions from invalid data. Long-term, Django will have model validation, but it doesn't exist yet; in the interim, you should consider it "safe" to ensure that you don't pass invalid data to your database. In other words, you still need to validate field by field before committing so you know what error to display invalid input to your users.

I would suggest this. Create new form classes for each element you want to validate, even if you don't use them at first. Malcolm Tredinnick outlined a technique for validating a model using hooks provided in a form system. Read this (it's really quite simple and elegant) and take a look at your models. Once you've defined and worked your newforms classes, you'll see that it's not very difficult - and will actually simplify your code significantly - if you rip out existing form templates and appropriate validation and process your form POST messages using forms. There is a bit of a learning curve, but the Form APIs are very well thought out and you will appreciate how much they do your code.

+1


source


Thanks to Daniel for your answer. Specifically for the newforms API, I'll definitely take the time to get my head around it and see if I can embrace it for longer term benefits. But just for the sake of getting my work done for this iteration (my due date is before EOY), I will probably still have to stick to the existing deprecation structure, after all, any way will get me to what I want, I want to do it's as smart and clean as possible without over-hacking.

It looks like doing validations in the model is not a bad idea, but in another sense, my old way of doing validations in views against a request is also close to the notion of encapsulating them inside the newforms API (data validation is separate from model creation). Do you think it's okay to keep my old design? I find it more helpful to touch this with the newforms API instead of directly manipulating them right now ...



(Well, I got this refactoring suggestion from my code reviewer, but I'm really not that sure if my old way breaks any mvc templates or is too hard to maintain. I think my path makes more sense, but my reviewer thought that mandatory validation and model creation together makes sense ...)

0


source







All Articles