I have a view where I validate date and time on form submit making sure the date + time are not past. Date and Time are two separate fields. It works, but I know its wrong way of doing it and the date+time should be validated in Django Forms.
This is in my view.py
(Probably not done the right way but it works)
my_date = request.session['reservationdate'] #in "mm/dd/yyyy" format
my_time = request.session['reservationtime'] #in "hh:mm" format
my_date_time = (my_date + ' ' + my_time + ':00') #convert to "mm/dd/yyyy hh:mm:ss"
my_date_time = datetime.strptime(my_date_time, '%m/%d/%Y %H:%M:%S') #convert to valid datetime
if datetime.now() <= my_date_time:
#do this
else:
...
now my goal is to have something like the above in Django forms:
class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['my_date'].required = True
self.fields['my_time'].required = True
...
def clean_my_date(self):
my_date = self.cleaned_data['my_date']
my_time = self.cleaned_data['my_time']
my_date_time = (my_date + ' ' + my_time + ':00')
my_date_time = datetime.strptime(my_date_time, '%m/%d/%Y %H:%M:%S')
if datetime.now() <= my_date_time:
raise forms.ValidationError(u'Wrong Date!')
return my_date
class Meta:
model = MyModel
fields = ['my_date', 'my_time', ...]
Edit:
working code:
def clean_my_time(self):
my_date = self.cleaned_data['my_date']
my_time = self.cleaned_data['my_time']
my_date_time = ('%s %s' % (my_date, my_time))
my_date_time = datetime.strptime(my_date_time, '%Y-%m-%d %H:%M:%S')
if datetime.now() >= my_date_time:
raise forms.ValidationError(u'Wrong Date or Time! "%s"' % my_date_time)
return my_time
Thanks to all for the help especially xyres for his work and for being patient with me!
I'll try to answer one last time. Instead of doing def clean_my_date(...)
, do this:
def clean_my_time(self):
# rest of your code remains same
If the above solution doesn't work, try this answer.
Update
Since the above code worked, I feel I should try and explain why and how.
Let's look at the order of fields in your form
fields = ['my_date', 'my_time', ...]
As, you can see my_time
field comes after my_date
field. So, when your code was like
def clean_my_date(self)
the clean()
method of your form gets called and it returns a dictionary object called cleaned_data
. This cleaned_data
dict has all the keys i.e. fields of your form upto my_date
field. Any field that is after my_date
won't be in cleaned_data
. As, my_time
field is after my_date
field, it was not in cleaned_data
. That is why you got a KeyError
.
After changing your code to
def clean_my_time(self)
the clean()
method returned cleaned_data
with all the fields upto my_time
. As my_date
comes before my_time
, it is, therefore, present in cleaned_data
. Hence, no error.
So it depends on the order of your form fields. If you want to validate two fields together, do it in the clean_my_field(self)
method of the field that comes later in order. The answer posted by Jérôme Thiard is also a good alternative.
You are trying to validate over multiple fields. This point is well covered by the django documentation, see https://docs.djangoproject.com/en/1.7/ref/forms/validation/#cleaning-and-validating-fields-that-depend-on-each-other.
The job has to be done in the clean
method.
Assuming django 1.7, your code could look like
class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['my_date'].required = True
self.fields['my_time'].required = True
...
def clean(self):
cleaned_data = super(MyForm, self).clean()
# here all fields have been validated individually,
# and so cleaned_data is fully populated
my_date = cleaned_data.get('my_date')
my_time = cleaned_data.get('my_time')
if my_date and my_time:
my_date_time = (my_date + ' ' + my_time + ':00')
my_date_time = datetime.strptime(my_date_time, '%m/%d/%Y %H:%M:%S')
if datetime.now() <= my_date_time:
msg = u"Wrong Date time !"
self.add_error('my_date', msg)
self.add_error('my_time', msg)
return cleaned_data
class Meta:
model = MyModel
fields = ['my_date', 'my_time', ...]