Django Formsets - form.is_valid() имеет значение False, предотвращающее проверку набора форм

Я использую набор форм, чтобы пользователи могли подписываться на несколько каналов. Я требую: а) пользователи выбрали подписку, выбрав логическое поле, а также должны пометить подписку и б) пользователь должен подписаться на указанное количество подписок.

В настоящее время приведенный ниже код способен: а) гарантировать, что пользователи пометят подписку, однако некоторые из моих форм is_valid() имеют значение False и, таким образом, предотвращают мою проверку полного набора форм. [править] Кроме того, соответствующее сообщение об ошибке набора форм не отображается.

Ниже приведен код:

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

По запросу код просмотра:

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

Любая помощь будет оценена по достоинству.

Пс. Для полного раскрытия информации этот код основан на http://google.com/search?q=cache:rVtlfQ3QAjwJ:https://www.pointy-stick.com/blog/2009/01/23/.advanced-formset-usage-django/+django+formset

[Решено] См. решение ниже.


person Community    schedule 05.11.2009    source источник
comment
Пожалуйста, покажите также код просмотра.   -  person Daniel Roseman    schedule 05.11.2009
comment
Код просмотра добавлен по запросу.   -  person    schedule 05.11.2009


Ответы (2)


Решено. Ниже приведен краткий обзор решения.

Сообщение об ошибке требовало обработки и формирования специального сообщения об ошибке. В исходном коде для наборов форм я обнаружил, что ошибки, которые относятся ко всей форме, известны как non_form_errors, и на их основе создал пользовательскую ошибку. [примечание: я не смог найти никакой официальной документации по этому поводу, так что кто-то может знать лучший способ]. Код ниже:

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

Метод очистки наборов форм также нуждался в нескольких изменениях. В основном он проверяет, связаны ли формы (пустые формы не являются, поэтому is_valid является ложным в вопросе), и если это так, доступ проверяет значение подписки.

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")

Некоторые могут задаться вопросом, почему я выбрал для доступа к значению формат form['field_name'].data. Это позволяет нам получать необработанное значение и всегда получать количество подписок, что позволяет мне возвращать все соответствующие сообщения для всего набора форм, то есть конкретные проблемы с отдельными формами и проблемы более высокого уровня (например, количество подписок), что означает, что пользователь выиграл не нужно повторно отправлять форму снова и снова, чтобы просмотреть список ошибок.

Наконец, мне не хватало одного важного аспекта моего шаблона — тега {{ formset.non_form_errors }}. Ниже представлен обновленный шаблон:

{% 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 %}
person Community    schedule 06.11.2009

Я попытался обойти свою проблему... это не хорошее решение, это скорее хак. Это позволяет людям продолжить, если они подписываются на необходимое количество каналов (в случае ниже более 1), однако, если количество каналов меньше необходимого, сообщение об ошибке не отображается.

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

Я использую {{ formset.management_form }} в своем шаблоне, поэтому, насколько я знаю, должна отображаться ошибка. Ниже моего шаблона на случай, если я ошибаюсь.

{% 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 %}
person Community    schedule 05.11.2009