2014-04-11 6 views
5

Ho due modelli:Convalida Django dati del modello per il calcolo della somma di più righe

class User(models.Model): 
    name = models.CharField(max_length=32) 


class Referral(models.Model): 
    referring_user = models.ForeignKey(User, related_name="referrals") 
    referred_user = models.ForeignKey(User, related_name="referrers") 
    percentage  = models.PositiveIntegerField() 

L'idea è che ogni utente ha n referenti, e dovrebbe avere almeno uno. Ogni referente ha un valore percentageche dovrebbe aggiungere fino al 100% quando aggiunto agli altri referenti.

Quindi l'utente "Alice" potrebbe avere referenti "Bob" (50%) e "Cynthia" (50%) e l'utente "Donald" potrebbe avere un referente: "Erin" (100%).

Il problema che ho è con la convalida. C'è un modo (preferibilmente uno che funziona bene con l'amministratore di Django utilizzando admin.TabularInline) che posso avere la convalida rifiutare il salvataggio di un User se la somma di Refferrals != 100%?

Idealmente, voglio che questo accada a livello di modulo/admin e non eseguendo l'override di User.save(), ma a questo punto non so da dove iniziare. La maggior parte del codice di convalida di Django sembra essere atomico, e la convalida su più righe non è qualcosa che ho già fatto in Django.

+2

Penso di aver fatto la stessa cosa prima, non riesco a ricordare tutto però. Penso che sia ancora necessario scavalcare il clean() dal livello User, ma questa volta devi tirare self.data piuttosto che self.cleaned_data. Solo per la cronaca, accedere a forms.data direttamente non è una buona pratica. –

risposta

1

Dopo Jerry Meng mi ha suggerito di guardare nella proprietà data e non cleaned_data, ho iniziato rovistando admin.ModelAdmin per vedere come avrei potuto accedere a tale metodo. Ho trovato get_form che sembra restituire una classe di modulo, quindi ho scavalcato quel metodo per acquisire la classe di ritorno, la sottoclasse e l'override di .clean().

Una volta all'interno, ho eseguito il looping su self.data, utilizzando un'espressione regolare per trovare i campi rilevanti e quindi ho fatto letteralmente i calcoli.

import re 
from django import forms 
from django.contrib import admin 

class UserAdmin(admin.ModelAdmin): 

    # ... 

    def get_form(self, request, obj=None, **kwargs): 

     parent = admin.ModelAdmin.get_form(self, request, obj=None, **kwargs) 

     class PercentageSummingForm(parent): 
      def clean(self): 

       cleaned_data = parent.clean(self) 

       total_percentage = 0 
       regex = re.compile(r"^referrers-(\d+)-percentage$") 

       for k, v in self.data.items(): 
        match = re.match(regex, k) 
        if match: 
         try: 
          total_percentage += int(v) 
         except ValueError: 
          raise forms.ValidationError(
           "Percentage values must be integers" 
          ) 

       if not total_percentage == 100: 
        raise forms.ValidationError(
         "Percentage values must add up to 100" 
        ) 

       return cleaned_data 

     return PercentageSummingForm 
1

As per the Django docs, clean() è la funzione ufficiale da implementare per i propri scopi. Si potrebbe immaginare una funzione che assomiglia a questo:

from django.core.exceptions import ValidationError 

def clean(self): 
    total_percentage = 0 
    for referrer in self.referrers.all(): 
     total_percentage += referrer.percentage 
    if total_percentage !== 100: 
     raise ValidationError("Referrer percentage does not equal 100") 
+0

Sono consapevole del fatto che '.clean()' è il luogo in cui si esegue la convalida che coinvolge più di una proprietà dello stesso modello, ma in questo caso 'self.referrers.all()' * è potenzialmente vuoto * poiché non hanno " t stato salvato nel momento in cui '.clean()' viene eseguito. –

Problemi correlati