2009-11-05 13 views
0

Je suis en train d'utiliser un formset pour permettre aux utilisateurs de s'abonner à plusieurs flux. J'ai besoin a) Les utilisateurs ont choisi un abonnement en sélectionnant un champ booléen, et sont également tenus de baliser l'abonnement et b) un utilisateur doit s'abonner à un nombre spécifié d'abonnements.Django Formsets - form.is_valid() is False empêchant la validation du formset

Actuellement, le code ci-dessous est capable de: a) garantir que les utilisateurs balises un abonnement, mais certains de mes formulaires is_valid() sont faux et empêchant ainsi ma validation du formset complet. [edit] En outre, le message d'erreur formset pertinent ne s'affiche pas.

est Ci-dessous le code:

from django import forms 
from django.forms.formsets import BaseFormSet 
from tagging.forms import TagField 
from rss.feeder.models import Feed 


class FeedForm(forms.Form): 
    subscribe = forms.BooleanField(required=False, initial=False) 
    tags = TagField(required=False, initial='') 

    def __init__(self, *args, **kwargs): 
     feed = kwargs.pop("feed") 
     super(FeedForm, self).__init__(*args, **kwargs) 
     self.title = feed.title 
     self.description = feed.description 

    def clean(self): 
     """apply our custom validation rules""" 
     data = self.cleaned_data 
     feed = data.get("subscribe") 
     tags = data.get("tags") 
     tag_len = len(tags.split()) 
     self._errors = {} 
     if feed == True and tag_len < 1: 
      raise forms.ValidationError("No tags specified for feed") 
     return data 



class FeedFormSet(BaseFormSet): 

    def __init__(self, *args, **kwargs): 
     self.feeds = list(kwargs.pop("feeds")) 
     self.req_subs = 3 # TODO: convert to kwargs arguement 
     self.extra = len(self.feeds) 
     super(FeedFormSet, self).__init__(*args, **kwargs) 

    # WARNING! Using undocumented. see for details... 
    def _construct_form(self, i, **kwargs): 
     kwargs["feed"] = self.feeds[i] 
     return super(FeedFormSet, self)._construct_form(i, **kwargs) 


    def clean(self): 
     """Checks that only a required number of Feed subscriptions are present""" 
     if any(self.errors): 
      # Do nothing, don't bother doing anything unless all the FeedForms are valid 
      return 
     total_subs = 0 
     for i in range(0, self.extra): 
      form = self.forms[i] 
      feed = form.cleaned_data 
      subs = feed.get("subscribe") 
      if subs == True: 
       total_subs += 1 
     if total_subs != self.req_subs: 
      raise forms.ValidationError("More subscriptions...") # TODO more informative 
     return form.cleaned_data 

Comme demandé, le code de la vue:

from django.forms import formsets 
from django.http import Http404 
from django.http import HttpResponseRedirect 
from django.shortcuts import render_to_response 

from rss.feeder.forms import FeedForm 
from rss.feeder.forms import FeedFormSet 
from rss.feeder.models import Feed 

FeedSet = formsets.formset_factory(FeedForm, FeedFormSet) 

def feeds(request): 
    if request.method == "POST": 
     formset = create_feed_formset(request.POST) 
     if formset.is_valid(): 
      # submit the results 
      return HttpResponseRedirect('/feeder/thanks/') 
    else: 
     formset = create_feed_formset() 
    return render_to_response('feeder/register_step_two.html', {'formset': formset})  


def create_feed_formset(data=None): 
    """Create and populate a feed formset""" 
    feeds = Feed.objects.order_by('id') 
    if not feeds: 
     # No feeds found, we should have created them 
     raise Http404('Invalid Step') 
    return FeedSet(data, feeds=feeds)  # return the instance of the formset 

Toute aide serait appréciée.

Ps. Pour la divulgation complète, ce code est basé sur http://google.com/search?q=cache:rVtlfQ3QAjwJ:https://www.pointy-stick.com/blog/2009/01/23/advanced-formset-usage-django/+django+formset

[Résolu] Voir la solution ci-dessous.

+0

Veuillez également afficher le code de la vue. –

+0

Afficher le code ajouté comme demandé. –

Répondre

2

Résolu. Voici un court passage de la solution.

Signaler l'erreur requise pour manipuler et formater un message d'erreur spécial. Dans le code source pour formsets j'ai trouvé les erreurs qui s'appliquent à un formulaire entier sont connus comme non_form_errors et produit une erreur personnalisée basée sur cela. [note: Je n'ai pas pu trouver de document officiel à ce sujet, donc quelqu'un pourrait peut-être mieux savoir]. Le code est ci-dessous:

def append_non_form_error(self, message): 
    errors = super(FeedFormSet, self).non_form_errors() 
    errors.append(message) 
    raise forms.ValidationError(errors) 

La méthode cleanetsets a également nécessité quelques modifications. Fondamentalement, il vérifie si les formulaires sont liés (les vides ne le sont pas, donc is_valid est faux dans la question) et si c'est le cas, accède aux vérifications de la valeur d'abonnement.

def clean(self): 
    """Checks that only a required number of Feed subscriptions are present""" 
    count = 0 
    for form in self.forms: 
     if form.is_bound: 
      if form['subscribe'].data: 
       count += 1 
    if count > 0 and count != self.required: 
     self.append_non_form_error("not enough subs") 

Certains pourraient se demander pourquoi choisir d'accéder à la valeur en utilisant le formulaire [ 'field_name']. Format de données. Cela nous permet de récupérer la valeur brute et de toujours compter sur les abonnements, ce qui me permet de renvoyer tous les messages pertinents pour le formulaire complet, c'est-à-dire les problèmes spécifiques aux formulaires individuels et aux problèmes de niveau supérieur (nombre d'abonnements). Ne devez pas soumettre le formulaire encore et encore pour travailler à travers la liste des erreurs.

Enfin, il me manquait un aspect crucial de mon modèle, la balise {{formset.non_form_errors}}.Voici le modèle mis à jour:

{% extends "base.html" %} 
{% load i18n %} 

{% block content %} 
<form action="." method="post"> 
{{ formset.management_form }} 
{{ formset.non_form_errors }} 
    <ol> 
     {% for form in formset.forms %} 
     <li><p>{{ form.title }}</p> 
    <p>{{ form.description }}</p> 
     {{ form.as_p }} 
     </li> 
     {% endfor %} 
    </ol> 
    <input type="submit"> 
</form> 

{% endblock %} 
0

J'ai fait une tentative pour contourner mon problème ... c'est pas une bonne solution, c'est vraiment un hack. Il permet aux gens de continuer s'ils s'abonnent au nombre requis de flux (dans le cas ci-dessous plus de 1), mais s'il est inférieur au nombre requis de flux, il ne montre pas le message d'erreur soulevé.

def clean(self): 
    count = 0 
    for i in range(0, self.extra): 
     form = self.forms[i] 
     try: 
      if form.cleaned_data: 
       count += 1 
     except AttributeError: 
      pass 
    if count > 1: 
     raise forms.ValidationError('not enough subscriptions') 
    return form.cleaned_data 

je ne l'utilise {{}} formset.management_form dans mon modèle afin autant que je sache l'erreur doit afficher. Ci-dessous mon modèle au cas où je suis égaré.

{% extends "base.html" %} 
{% load i18n %} 

{% block content %} 
<form action="." method="post"> 
    {{ formset.management_form }} 
    <ol> 
     {% for form in formset.forms %} 
     {{ form.as_p }} 
     </li> 
     {% endfor %} 
    </ol> 
    <input type="submit"> 
</form> 

{% endblock %} 
Questions connexes