简体   繁体   中英

Custom validation for formset in Django

I can't figure out how to make a custom validation for my formset. I'm trying to prevent users to select more than 12 times the same year, but when I print it, the cleaned_data comes in as a different dictionary for each form.

I would like to have all forms grouped into 1 dictionary to check if one year appears more than 12 times, or to write this in a better way.

My code:

forms.py

class SellerResultForm(forms.ModelForm):

    class Meta:
        model = SellerResult
        fields = ('month', 'year', 'result',)
        widgets = {
            'month': forms.Select(attrs={'class': 'form-control',}),
            'year': forms.Select(attrs={'class': 'form-control',}),
            'result': forms.TextInput(attrs={'class': 'form-control',}),
        }

    def has_changed(self): #used for saving data from initial
        changed_data = super(SellerResultForm, self).has_changed()
        return bool(self.initial or changed_data)

    def clean(self):
        cleaned_data = super(SellerResultForm, self).clean()
        print(cleaned_data)
        # prints a set of dictionaries
        # {'month': 4, 'year': 2017, 'id': 1, 'result': 1000}
        # {'month': 5, 'year': 2017, 'id': 1, 'result': 1000}
        # {'month': 6, 'year': 2017, 'id': 1, 'result': 1000}

views.py

def seller_result(request, user_id):

    SellerResultFormSet = modelformset_factory(SellerResult, form=SellerResultForm, extra=1, max_num=1)

    queryset = SellerResult.objects.filter(seller=user_id,).order_by('year', 'month')
    formset = SellerResultFormSet(request.POST or None,
                                           queryset=queryset,
                                           initial=[
                                           {'month': datetime.now().month,
                                           'year': datetime.now().year,
                                           'result': 1000,}])

    if formset.is_valid():
        instances = formset.save(commit=False)
        for instance in instances:
            instance.seller_id = user_id
            instance.save()

    context = {
        'formset': formset,
        }
    return render(request, 'app/seller_result.html', context)

Managed to make it work, full working code below:

forms.py

class SellerResultForm(forms.ModelForm):

    class Meta:
        model = SellerResult
        fields = ('month', 'year', 'result',)
        widgets = {
            'month': forms.Select(attrs={'class': 'form-control',}),
            'year': forms.Select(attrs={'class': 'form-control',}),
            'result': forms.TextInput(attrs={'class': 'form-control',}),
        }

    def has_changed(self): #used for saving data from initial
        changed_data = super(SellerResultForm, self).has_changed()
        return bool(self.initial or changed_data)

    #no clean method here anymore

class BaseSellerResultFormSet(BaseModelFormSet):
    def clean(self):
        super(BaseSellerResultFormSet, self).clean()

        years = []
        for form in self.forms:
            year = form.cleaned_data['year']
            years.append(year)
        if years.count(2017) > 12:
            raise forms.ValidationError('You selected more than 12 months for 2017')

I have then quite struggled to get this ValidationError to render in my template, the errors are available with {{ formset.non_form_errors }} and not {{ formset.errors }} as I expected initially.

Override the clean method of the formset. self.forms will contain all of the forms.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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