Reputation:
I have a form that creates a lesson with a date and time. I currently have validators to ensure that past dates can't be used, which are working perfectly. However, I am having trouble visualising how a validator that makes sure the time entered isn't past 11:59 pm would work. I inlucded a snippet of what I am trying to achieve (I know it doesn't work the way it is layed out, it is just there to provide context). I would appreaciate any help with this.
forms.py
def validate_date1(value):
if value < timezone.now():
raise ValidationError('Date cannot be in the past')
def validate_date2(value):
if value < timezone.now():
raise ValidationError('Date cannot be in the past')
def present_date1(value):
if value > '11:59 pm':
raise ValidationError('Time cannot be past 11:59 pm')
def present_date2(value):
if value > '11:59 pm':
raise ValidationError('Time cannot be past 11:59 pm')
class LessonForm(forms.ModelForm):
lesson_instrument = forms.ChoiceField(choices=instrument_list, widget=forms.Select(attrs={'class' : 'form-control', 'required' : 'True'}))
lesson_datetime_start = forms.DateTimeField(input_formats=['%Y-%m-%d %I:%M %p'], widget=forms.DateTimeInput(attrs={'class': 'form-control', 'placeholder':'YYYY-MM-DD Hour:Minute am/pm'}), validators=[validate_date1, present_date1])
lesson_datetime_end = forms.DateTimeField(input_formats=['%Y-%m-%d %I:%M %p'], required=False, widget=forms.DateTimeInput(attrs={'class': 'form-control', 'placeholder':'YYYY-MM-DD Hour:Minute am/pm'}), validators=[validate_date2, present_date2])
lesson_weekly = forms.BooleanField(required=False)
Upvotes: 1
Views: 150
Reputation: 20692
You want to validate lesson_datetime_start
and lesson_datetime_end
together, not separately. Just checking that the time isn't greater than 11:59pm doesn't cut it, since that would make 2019-05-04 11:00pm - 2019-05-05 12:00am invalid even though it's a correct one hour interval starting at 11pm.
To do that, add a clean()
method to your form:
def clean(self):
cleaned_data = super().clean()
if self.cleaned_data.get('lesson_datetime_start') \
and self.cleaned_data.get('lesson_datetime_end') \
and self.cleaned_data['lesson_datetime_start'] >= self.cleaned_data['lesson_datetime_end']:
raise ValidationError({'lesson_datetime_end': "End time must be later than start time."})
return cleaned_data
In the same way, you could add a validator that the duration of the lesson isn't greater than a certain expected time interval (e.g. cannot be longer than 4 hours), by subtracting the two datetime
fields and comparing them to datetime.timedelta(hours=x)
.
You can also do it in your model, so assuming you have a Lesson
model with fields lesson_start
and lesson_end
:
def clean(self):
if self.lesson_start and self.lesson_end and self.lesson_start >= self.lesson_end:
raise ValidationError({'lesson_end': "End time must be later than start time."})
Upvotes: 0
Reputation: 26281
So, the input date cannot be in the past and it cannot be after 23:59
, so basically it needs to be within the rest of the present day.
How about:
import pytz
def date_is_not_past(dt):
if dt < datetime.now(pytz.UTC):
raise ValidationError('Date cannot be in the past')
def date_is_today(dt):
if dt.date() != datetime.now(pytz.UTC).date():
raise ValidationError('Date needs to be today')
Upvotes: 1
Reputation: 169051
The validators for a DateTimeField
will get a datetime.datetime
object, not a string.
Here we extract the time component out of the datetime and compare it to our constant last possible time.
import datetime
LAST_POSSIBLE_TIME = datetime.time(23, 59)
def validate_time(value):
if value.time() > LAST_POSSIBLE_TIME:
raise ValidationError('Time cannot be past 11:59 pm')
Upvotes: 2