Mary Lohvi
Mary Lohvi

Reputation: 11

Django Form clean sees Date objects as NoneType. POST method in views.py does see it as a date

So I am currently working on the project in Django. The user should be able to set start date and the end date, based on this set interval an analysis should be calculated.

The current problem is, when the data comes to validation, it sees input data as an NoneType.

Note: I did not upload the entire html code. Only form relevant code.

forms.py

from django import forms
from tempus_dominus.widgets import DatePicker
import datetime


class AnalysisInformationForm(forms.Form):
    start = forms.DateField(help_text="Enter a date between 2019-03-01 and now.",
                            input_formats=["%d/%m/%Y"],
                            widget=DatePicker(
                                options={
                                    'minDate': '2019-03-01',
                                    'maxDate': datetime.datetime.utcnow().date().strftime("%Y-%m-%d")
                                })
                            )
    end = forms.DateField(help_text="Enter a date between the chosen start date and now",
                          input_formats=["%d/%m/%Y"],
                          widget=DatePicker(
                              options={
                                  'minDate': '2019-03-01',
                                  'maxDate': datetime.datetime.utcnow().date().strftime("%Y-%m-%d")
                              })
                          )

    def clean(self):
        cleaned_data = super(AnalysisInformationForm, self).clean()
        start = cleaned_data.get('start')
        end = cleaned_data.get('end')
        print(start, end)
        if start >= end:
            raise forms.ValidationError('Invalid dates input. The start day must be set earlier than the end date. ')
        return cleaned_data

views.py

def index(request):
    if request.method == 'POST':
        form = AnalysisInformationForm(request.POST)
        if form.is_valid():
            pass
    else:
        form = AnalysisInformationForm()
    return render(request, 'stm/index.html', {'form': form})

index.html

<head>
        <link rel="stylesheet" href="https://netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.css">
        <link rel="stylesheet" href="{%  static 'css/bootstrap.css' %}"><link>
        <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
</head>
        {{ form.media }}
<body>
<div class="form-group"></div>
                        {% load widget_tweaks %}
                        <form method="post" novalidate>
                            {% csrf_token %}

                            {% for hidden_field in form.hidden_fields %}
                                {{ hidden_field }}
                            {% endfor %}

                            {% if form.non_field_errors %}
                                <div class="alert alert-danger" role="alert">
                                  {% for error in form.non_field_errors %}
                                    {{ error }}
                                  {% endfor %}
                                </div>
                            {% endif %}

                            {% for field in form.visible_fields %}
                                <div class="input-group">
                                {{ field.label_tag }}
                                {% if form.is_bound %}
                                    {% if field.errors %}
                                        {% render_field field class="form-control is-invalid" %}
                                        {% for error in field.errors %}
                                            <div class="invalid-feedback">
                                              {{ error }}
                                            </div>
                                        {% endfor %}
                                    {% else %}
                                        {% render_field field class="form-control is-valid" %}
                                    {% endif %}
                                {% else %}
                                    {% render_field field class="form-control" %}
                                {% endif %}
                                {% if field.help_text %}
                                    <small class="form-text text-muted">{{ field.help_text }}</small>
                                {% endif %}
                                </div>
                            {% endfor %}
                            <div class="input-group m-3">
                                <input type="submit" class="btn btn-outline-secondary btn-block" value="Continue">
                            </div>
                        </form>
</body>

These tutorials brought me to this solution:

https://simpleisbetterthancomplex.com/article/2017/08/19/how-to-render-django-form-manually.html#rendering-bootstrap-4-forms

https://pypi.org/project/django-tempus-dominus/

https://pypi.org/project/django-widget-tweaks/

I also found a similar question on stack overflow, though no answer: Django DateField form generates None in cleaned_data

I also tried to do the following to check whether the actual data is coming through. so in views.py I've changed the following: Before:

form = AnalysisInformationForm(request.POST)

After:

form = request.POST['start']
print(form)

In between the Errors I found that print statement. And surprise, surprise, the given start date did match the actual input. But after the data has been cleaned it becomes a NoneType value.

Any suggestions what might be the cause?

The expected result is for the data to be able to get validated as shown in the code. The start date must be set earlier than the end date and if not show the error message.

Upvotes: 0

Views: 989

Answers (4)

Mary Lohvi
Mary Lohvi

Reputation: 11

So after trying several things. I've come to the following solution: Firstly in forms.py I've updated the clean function to match the following. Found this clean function somewhere here on stackoverflow, but forgot to save the link to it, didn't know if this would work:

    def clean(self):
        data = self.cleaned_data
        print(data)
        start = data.get('start')
        end = data.get('end')
        if start >= end:
            raise forms.ValidationError('Invalid dates input. The start day must be set earlier than the end date. ')
        return data

Then I tried several date input formats, such as ["%y/%m/%d"](19/04/02 format, not iso), ["%Y/%m/%d"](2019/04/02 format, not iso), ["%y-%m-%d"](19-04-02 format, not iso), ["%Y-%m-%dT%H:%M:%S.%fZ"](well, iso datetime format, but I'm working with date objects not datetime objects). So I've come to conclusion that I just have to change the / to - in ["%Y/%m/%d"] and everything should work. And it actually did work :D

This is how the new updated form class looks like:

class AnalysisInformationForm(forms.Form):
    start = forms.DateField(help_text="Enter a date between 2019-03-01 and now.",
                            input_formats=["%Y-%m-%d"],
                            widget=DatePicker(
                                options={
                                    'minDate': '2019-03-01',
                                    'maxDate': datetime.datetime.utcnow().date().strftime("%Y-%m-%d")
                                })
                            )
    end = forms.DateField(help_text="Enter a date between the chosen start date and now",
                          input_formats=["%Y-%m-%d"],
                          widget=DatePicker(
                              options={
                                  'minDate': '2019-03-01',
                                  'maxDate': datetime.datetime.utcnow().date().strftime("%Y-%m-%d")
                              })
                          )

Thanks to everyone who tried to help, you helped me find the right solution.

Upvotes: 1

rahul.m
rahul.m

Reputation: 5854

for django 2.1 try this

def clean(self):
    cleaned_data = super().clean()
    start = cleaned_data.get("start")
    end = cleaned_data.get("end")

hope it helps

Upvotes: 0

Pavel Kovtun
Pavel Kovtun

Reputation: 367

Try to find what passed to the clean() method before the super() is called. Perhaps it comes from client in non-iso format and did not pass the default validation from superclass

Upvotes: 0

Zeinab Mohammed
Zeinab Mohammed

Reputation: 63

may be you need to make " post " in html all uppercase

 <form method="POST" novalidate>

Upvotes: 0

Related Questions