0

Tried using this (Django: How to set DateField to only accept Today & Future dates) SO post to have form raise error in template when a date field is given value less than current date (which, based on another SO answer (https://stackoverflow.com/a/22513989/8236733), I thought was done via the form field validators (see https://docs.djangoproject.com/en/2.0/ref/forms/validation/#cleaning-a-specific-field-attribute)). However, this does not seem to be working when inputting supposedly invalid values in the form template.

forms.py:

import datetime

from django import forms

from .models import Post


class MyForm(forms.Form):
    title = forms.CharField(max_length=100, required=True, help_text='Give a title',
                            widget=forms.TextInput(attrs={'size': '50rem'}))
    post = forms.CharField(max_length=280, required=True,
                           widget=forms.Textarea(attrs={'rows': 5}))
    expire_date = forms.DateField(  
        widget=forms.TextInput(attrs=
        {
            'class':'datepicker', # to use jquery datepicker
        }),
        required=True
    )
    expire_time = forms.TimeField(
        input_formats=['%H:%M %p'],
        widget=forms.TextInput(attrs=
        {
            'class':'timepicker', # to use jquery timepicker
        }),
        required=True)

    class Meta:
        model = Post
        fields = ('title', 'post', 'expire_date', 'expire_time',)

    def clean_date(self): # see https://docs.djangoproject.com/en/dev/ref/forms/validation/#cleaning-a-specific-field-attribute
        date = self.cleaned_data['expire_date']
        print(date)
        if date < datetime.date.today():
            raise forms.ValidationError("The date cannot be in the past!")
        return date

Relevant template snippet:

 <form method="post">
                {% csrf_token %}
                {% for field in listing_form %}
                <p>
                    {{ field.label_tag }}
                <div class="ui input">{{ field }}</div>
                {% if field.help_text %}
                <small class="ui inverted">{{ field.help_text }}</small>
                {% endif %}
                {% for error in field.errors %}
                <p style="color: red">{{ error }}</p>
                {% endfor %}
                </p>
                {% endfor %}
                <button class="ui inverted button" type="submit">submit</button>
                <div class="ui divider"></div>

                </small>
            </form>

Attempting to submit this form with some past date does not raise any error in the teplate and the form's is_valid() method raises no error in the backend view.

New to django, so is there something I am missing here? Do I need to somehow attach the validation method to the particular field in some way?

lampShadesDrifter
  • 3,925
  • 8
  • 40
  • 102

1 Answers1

1

Apparently it is important that the form method for cleaning / validating a particular form field follow a particular naming convention: clean_<field name>. This is shown implicitly in the docs (https://docs.djangoproject.com/en/2.0/ref/forms/validation/#cleaning-a-specific-field-attribute), but never stated outright. Changing the clean_... method in the form class in the original post to have the following code seems to have fixed the problem.

class MyForm(forms.Form):
    ....
    expire_date = forms.DateField(  
        widget=forms.TextInput(attrs=
        {
            'class':'datepicker', # to use jquery datepicker
        }),
        required=True
    )
    ....
    class Meta:
        model = Post
        fields = ('title', 'post', 'expire_date', 'expire_time',)

    def clean_expire_date(self): 
                date = self.cleaned_data['expire_date']
                print(date)
                if date < datetime.date.today():
                    raise forms.ValidationError("The date cannot be in the past!")
                return date

If anyone knows where in the docs this naming convention is more explicitly specified, please let me know.

lampShadesDrifter
  • 3,925
  • 8
  • 40
  • 102
  • This is absolutely explicitly stated in that very page you link to: "The `clean_()` method is called on a form subclass – where `` is replaced with the name of the form field attribute ... For example, if you wanted to validate that the contents of a `CharField` called `serialnumber` was unique, `clean_serialnumber()` would be the right place to do this." – Daniel Roseman Jun 02 '18 at 11:50
  • @DanielRoseman I'd argue that, looking only at the docs, it's not totally clear that the naming really matters. Eg. the docs say " write a cleaning method that operates on the _recipients_ field, like so: `def clean_recipients(self):`..." Reading this, I had assumed that the method naming was _purely for simplicity's sake_ (did not know how django knew to check the validator when the form was submitted, but I did not assume that the actual name of the function was the key (though now with hindsight, I can see how another who also already knew the answer would write the docs as they are)) – lampShadesDrifter Jun 02 '18 at 12:12