简体   繁体   English

Django Formsets-form.is_valid()为False,无法进行表单集验证

[英]Django Formsets - form.is_valid() is False preventing formset validation

I'm am utilizing a formset to enable users subscribe to multiple feeds. 我正在利用一个表单集来使用户订阅多个提要。 I require a) Users chose a subscription by selecting a boolean field, and are also required to tag the subscription and b) a user must subscribe to an specified number of subscriptions. 我要求a)用户通过选择布尔值字段选择订阅,并且还需要标记订阅,并且b)用户必须订阅指定数量的订阅。

Currently the below code is capable of a) ensuring the users tags a subscription, however some of my forms is_valid() are False and thus preventing my validation of the full formset. 目前,以下代码能够a)确保用户标记订阅,但是我的某些表单is_valid()为False,因此无法验证完整的表单集。 [edit] Also, the relevant formset error message fails to display. [edit]另外,相关的表单集错误消息也无法显示。

Below is the 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

As requested, the view code: 根据要求,查看代码:

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

Any help would be appreciated. 任何帮助,将不胜感激。

Ps. PS。 For full disclosure, this code is based on http://google.com/search?q=cache:rVtlfQ3QAjwJ:https://www.pointy-stick.com/blog/2009/01/23/advanced-formset-usage-django/+django+formset 为了全面披露,此代码基于http://google.com/search?q=cache:rVtlfQ3QAjwJ:https://www.pointy-stick.com/blog/2009/01/23/advanced-formset-usage -django / + + django的表单集

[Solved] See solution below. [已解决]请参阅下面的解决方案。

Solved. 解决了。 Below is a quick run through of the solution. 以下是该解决方案的快速介绍。

Reporting the error required manipulating and formating a special error message. 报告错误需要处理和格式化特殊的错误消息。 In the source code for formsets I found the errors that apply to a whole form are known as non_form_errors and produced a custom error based on this. 在表单集的源代码中,我发现适用于整个表单的错误称为non_form_errors并在此基础上产生了自定义错误。 [note: I couldn't find any authoritive documentation on this, so someone might know a better way]. [注意:我找不到关于此的任何权威性文档,所以有人可能知道更好的方法]。 The code is below: 代码如下:

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

The formsets clean method also needed a few tweaks. 表单集的清洁方法也需要进行一些调整。 Basically it checks the if the forms is bound (empty ones aren't, hence is_valid is false in the question) and if so accesses checks there subscribe value. 基本上,它检查表单是否绑定(不是空表单,因此is_valid在问题中为false),如果是,则访问检查是否有订阅值。

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

Some might wonder why I choose to access the value using the form['field_name'].data format. 有人可能会奇怪,为什么我选择使用form ['field_name']。data格式访问值。 This allows us to retrieve the raw value and always get a count on subscriptions, allowing me to return all relevant messages for the entire formset, ie specific problems with individual forms and higher level problems (like number of subscriptions), meaning that the user won't have to resubmit the form over and over to work through the list of errors. 这使我们能够检索原始值并始终获得订阅计数,从而使我可以返回整个表单集的所有相关消息,即单个表单的特定问题和更高级别的问题(例如订阅数),这意味着用户赢得了不必一遍又一遍地重新提交表格以遍历错误列表。

Finally, I was missing one crucial aspect of my template, the {{ formset.non_form_errors }} tag. 最后,我错过了模板的一个关键方面{{formset.non_form_errors}}标签。 Below is the updated template: 以下是更新的模板:

{% 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 %}

I made attempt to circumvent my problem...it is not a good solution, it's very much so a hack. 我试图避开我的问题...这不是一个好的解决方案,它非常容易被破解。 It allows people to proceed if they subscribe to the required number of feeds (in the case below more than 1), however if less than the required number of feeds, it fails to show the error message raised. 如果人们订阅了所需数目的提要(在以下情况下,数目大于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

I do use {{ formset.management_form }} in my template so as far as I know the error should display. 据我所知,我确实在模板中使用{{formset.management_form}}。 Below my template in case I'm misguided. 在模板下方,以防万一我被误导了。

{% 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 %}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM