2008-12-21 9 views
0

Nous avons une application Web qui prend des entrées utilisateur ou des recherches de base de données pour former des opérations sur certaines ressources physiques. La conception peut être simplement présenté comme schéma suivant:Aide au refactoring de code - comment réorganiser les validations

entrée utilisateur < => modèle objet < => stockage base de données

validations sont nécessaires à la demande provenant de l'entrée d'utilisateur, mais pas quand on vient de visites de consultation de base de données (car si un enregistrement existe, ces attributs doivent déjà avoir été validés auparavant). J'essaye de refactoriser le code de sorte que les validations se produisent dans le constructeur d'objet au lieu de l'ancienne (quelques routines séparées de validation)

Comment décideriez-vous quelle est la meilleure façon? (La différence fondamentale de la méthode 1 (ancienne) et 2 est que les validations dans 1 ne sont pas obligatoires et découplées de l'instanciation d'objet mais 2 les lie et les rend obligatoires pour toutes les requêtes)

Voici deux exemples d'extraits de code pour Design 1 et 2:

Méthode 1:

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

Méthode 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 

Dans une demande de traitement par lots: Méthode 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) 

Méthode 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. 

Avantages et inconvénients que je peux voir ici sont:

  1. Méthode 1 suit plus proche de la logique métier. Aucune validation redondante ne vérifie quand les objets proviennent de la recherche db. Les routines de validation sont mieux maintenables et lues.
  2. La méthode 2 rend facile et propre pour l'appelant. Les validations sont obligatoires même si elles proviennent de la recherche db. Les validations sont moins maintenables et lues.
+0

Je vous suggère de raccourcir votre question, résumer et reformater le code (le l'éditeur incorporé a un bouton "code"). –

+0

Vous devriez certainement raccourcir la question. Aussi est-ce vraiment un projet django. Si c'est le cas, vous devriez expliquer pourquoi la validation n'est pas implémentée sur les formulaires (comme le fait Django). – muhuk

+0

C'est. A l'origine le développeur n'utilisait pas de formulaires mais de Context avec des données, des formulaires construits du coté client. Quand je l'ai pris, ce design n'a pas été changé. –

Répondre

1

Faire une validation dans le constructeur n'est vraiment pas la "méthode Django". Puisque les données que vous devez valider proviennent du côté client, l'utilisation de new forms (probablement avec un ModelForm) est la méthode la plus idiomatique à valider car elle englobe toutes vos préoccupations dans une seule API: elle fournit des valeurs de validation raisonnables (avec la possibilité pour personnaliser facilement), plus les formulaires de modèles intègrent le côté de saisie de données (le formulaire html) avec la validation de données (model.save()).

Cependant, il semble que vous ayez ce qui peut être un désordre d'un projet hérité; Il se peut que vous n'ayez pas le temps de réécrire toute la gestion de votre formulaire pour utiliser de nouveaux formulaires, du moins au début. Donc voici mes pensées:

Tout d'abord, il n'est pas «non-Djangonic» de mettre une certaine validation dans le modèle lui-même - après tout, les soumissions de formulaire html peuvent ne pas être la seule source de nouvelles données. Vous pouvez remplacer la méthode save() ou utiliser signals pour nettoyer les données lors de la sauvegarde ou pour lancer une exception sur les données non valides. À long terme, Django aura une validation de modèle, mais ce n'est pas encore le cas; dans l'intervalle, vous devriez considérer cela comme une "sécurité" pour vous assurer que vous ne commettez pas de données invalides dans votre base de données. En d'autres termes, vous devez toujours valider champ par champ avant de valider afin de savoir quelle erreur afficher pour vos utilisateurs sur une entrée non valide.

Ce que je suggère est ceci. Créez de nouvelles classes de formulaires pour chaque élément que vous devez valider, même si vous ne les utilisez pas initialement. Malcolm Tredinnick décrit une technique pour faire la validation du modèle en utilisant les crochets fournis dans le système de formulaires. Lisez-le dessus (c'est vraiment très simple et élégant), et accrochez-vous à vos modèles. Une fois que vous aurez défini et travaillé les nouvelles classes, vous verrez que ce n'est pas très compliqué - et que cela simplifiera grandement votre code - si vous supprimez vos modèles de formulaires existants et la validation correspondante, et gérez les POST de votre formulaire en utilisant cadre de formulaires. Il y a un peu de courbe d'apprentissage, mais l'API des formulaires est extrêmement bien pensée et vous serez reconnaissant à quel point votre code sera plus propre.

0

Merci Daniel pour votre réponse. En particulier pour l'API newforms, je vais certainement passer du temps à creuser dedans et voir si je peux l'adopter pour les meilleurs avantages à long terme. Mais juste pour faire mon travail pour cette itération (respecter mon délai avant EOY), je serais probablement toujours de rester avec la structure actuelle, après tout, l'un ou l'autre chemin va me faire ce que je veux, juste que Je veux le rendre sain et sain autant que possible sans trop casser. Cela ressemble à faire des validations dans le modèle n'est pas une très mauvaise idée, mais dans un autre sens, mon ancienne façon de faire des validations dans les vues sur la requête semble aussi proche de l'idée de les encapsuler dans l'API newforms la validation est découplée de la création du modèle). Pensez-vous qu'il est juste de garder mon ancien design? Il est plus logique pour moi d'y toucher avec l'API newforms au lieu de les jongler en ce moment ...

(Bon, j'ai reçu cette suggestion de refactoring de la part de mon réviseur de code, mais je ne suis pas vraiment sûr que mon ancienne façon viole tout mvc patterns ou trop compliqué à maintenir je pense que mon chemin fait plus de sens mais mon critique a pensé que la validation et la création de modèle ensemble avaient plus de sens ...)

+0

De façon très générale, essayez de garder autant de logique que possible en dehors du constructeur. Avoir une logique dans le constructeur rend les tests unitaires beaucoup plus difficiles, et de solides tests unitaires sont vraiment importants pour tout refactoring ou maintenance. –