2009-10-12 7 views
11

Dans un formulaire personnalisé, comment peut-on valider l'unicité du champ d'un modèle (c'est-à-dire, a unique=True ensemble)?Comment valider/nettoyer() un champ unique = True sans utiliser ModelForm?

Je sais que ModelForm de django exécute automatiquement une fonction validate_unique() qui est appelée dans la méthode clean() du BaseModelForm - donc, lors de l'utilisation ModelForm, ce sera possible de traiter correctement (comme dans l'Admin).

Cependant, je crée ma propre forme à partir de zéro et je me demande comment je peux gérer cela moi-même? Je pense que mon plus grand bloc d'achoppement est de savoir quel objet est attaché à la forme lorsque les données sont en cours de nettoyage ...

Certains code:

class UserProfile(CreatedModifiedModel): 
    user   = models.ForeignKey(User, unique=True) 
    display_name = models.CharField('Display Name',max_length=30, 
         blank=True,unique=True) 

class EditUserProfileForm(forms.Form): 
    display_name = forms.CharField(required=False,max_length=30) 

    # "notifications" are created from a different model, not the UserProfile 
    notifications = forms.MultipleChoiceField(
         label="Email Notifications", 
         required=False, 
         widget=forms.CheckboxSelectMultiple,) 

    def clean_display_name(self): 
     # how do I run my own validate_unique() on this form? 
     # how do I know which UserProfile object I am working with? 

    # more code follows, including the __init__ which sets up the notifications 
+1

Y a-t-il une raison pour laquelle vous créez un formulaire personnalisé au lieu d'un ModelForm? – tghw

+0

J'ai mis à jour le code pour montrer le champ "notifications" dont j'ai besoin qui est d'une application différente, mais manipulé sur le même EditUserProfileForm ... espérons que cela a du sens. Je ne pense pas que je peux faire un ModelForm à partir de plusieurs sources de modèles ... – thornomad

Répondre

15

validation unique est difficile d'obtenir tout à fait raison, donc je recommandons d'utiliser un ModelForm de toute façon:

class EditUserProfileForm(forms.ModelForm): 
    # "notifications" are created from a different model, not the UserProfile 
    notifications = forms.MultipleChoiceField(
         label="Email Notifications", 
         required=False, 
         widget=forms.CheckboxSelectMultiple,) 

    class Meta: 
     model = UserProfile 
     fields = ('display_name',) 

Faire une forme à partir de plusieurs modèles n'est pas facile, mais dans ce cas, vous pouvez simplement ajouter le champ notifications sur la ModelForm et retirez-la .cleaned_data comme d'habitude:

# view 
if request.method == 'POST': 
    form = EditUserProfileForm(request.POST, instance=user_profile) 
    if form.is_valid(): 
     user_profile = form.save() 
     notifications = form.cleaned_data['notifications'] 
     # Do something with notifications. 

Voilà comment je le ferais, mais si vous définissez vous-même sur la validation unique, vous pouvez toujours faire quelque chose comme:

def clean_display_name(self): 
    display_name = self.cleaned_data['display_name'] 
    if UserProfile.objects.filter(display_name=display_name).count() > 0: 
     raise ValidationError('This display name is already in use.') 
    return display_name 

Il y a deux problèmes que je vois ici. Tout d'abord, vous pouvez rencontrer des problèmes de concurrence, où deux personnes soumettent le même nom, toutes deux passent des vérifications uniques, mais une erreur DB se produit alors. L'autre problème est que vous ne pouvez pas modifier un profil d'utilisateur parce que vous n'avez pas d'ID à exclure de la recherche. Il faudrait stocker dans votre __init__ et ensuite l'utiliser dans le nettoyage:

def __init__(self, *args, **kwargs): 
    ... 
    if 'instance' in kwargs: 
     self.id = kwargs['instance'].id 
    ... 

def clean_display_name(self): 
    display_name = self.cleaned_data['display_name'] 
    qs = UserProfile.objects.filter(display_name=display_name) 
    if self.id: 
     qs = qs.exclude(pk=self.id) 
    if qs.count() > 0: 
     raise ValidationError('This display name is already in use.') 
    return display_name 

Mais à ce moment-là, vous êtes juste dupliquer la logique ModelForms.

+1

Hey - merci pour cela. Je n'étais pas sûr d'ajouter des champs à un ModelForm qui ne correspond pas au modèle ... Mais je vais essayer pour les débutants. Si j'ai des problèmes rendra compte. – thornomad

+0

tghw merci frère, bonne réponse – PyDroid

Questions connexes