Unable to raise ValidationError in Django form

513 Views Asked by At

I have made a form in which the user is supposed to enter date and time. I want to make sure that the time is in the correct format so I wrote convert_to_time function so that it can raise a ValidationError when the format of time is wrong but it is not working the way I want. I want to display the error in the form itself. I seem that the Exception is not working. I mean the control is never going inside the Exception part.

Here is my forms.py file.


"""Function to convert string to time."""
def convert_to_time(date_time):
    format = '%H:%M:%S'
    try:
        datetime.datetime.strptime(date_time, format).time()
    except Exception:
        #print("qkftio")   This statement does not get executed even when the time format is wrong
        raise ValidationError(
                "Wrong time format entered."
            )

"""class used for booking a time slot."""
class BookingForm(forms.ModelForm):
    class Meta:
        model = Booking
        fields = ['check_in_date', 'check_in_time', 'check_out_time',
                    'person', 'no_of_rooms']

    """Function to ensure that booking is done for future."""
    def clean(self):
        cleaned_data = super().clean()
        normal_book_date = cleaned_data.get("check_in_date")
        normal_check_in = cleaned_data.get("check_in_time")
        convert_to_time(str(normal_check_in))

Here is my models.py file.

"""class used when a user books a room slot."""
class Booking(models.Model):
    check_in_date = models.DateField()
    check_in_time = models.TimeField()
    check_out_time = models.TimeField()
    PERSON = (
        (1, '1'),
        (2, '2'),
        (3, '3'),
        (4, '4'),
    )
    person = models.PositiveSmallIntegerField(choices=PERSON, default=1)
    no_of_rooms = models.PositiveSmallIntegerField(
        validators=[MaxValueValidator(1000), MinValueValidator(1)], default=1
        )

Can someone help?

Edit: Here is my views.py file.

def booking(request):
    if request.method == 'POST':
        form = BookingForm(request.POST)
        if form.is_valid():
            request.session['normal_book_date'] = request.POST['check_in_date']
            request.session['normal_check_in'] = request.POST['check_in_time']
            request.session['normal_check_out'] = request.POST['check_out_time']
            request.session['normal_person'] = int(request.POST['person'])
            request.session['normal_no_of_rooms_required'] = int(
                request.POST['no_of_rooms']
                )
            response = search_availability(request.session['normal_book_date'],
                                           request.session['normal_check_in'],
                                           request.session['normal_check_out'],
                                           request.session['normal_person'],
                                           request.session['normal_no_of_rooms_required'])
            if response:
                context = {
                    'categories': response,
                    'username': request.user.username
                    }
                return render(request, 'categories.html', context)
            return HttpResponse("Not Available")
        else:
            context = {
                'form': form,
                'username': request.user.username
                }
            return render(request, 'book.html', context)
    context = {
        'form': BookingForm(),
        'username': request.user.username
        }
    return render(request, 'book.html', context)

This is the link to my github repo https://github.com/AnshulGupta22/room_slot_booking

Edit 2: This is the screenshot of the output that I am gettingScreen shot of output

I want the error to be displayed in the form itself.

1

There are 1 best solutions below

11
Nealium On

Normally you raise validation errors inside an is_valid

I also changed the error type, to it attaches the error to the field itself and the message will show when you render the view with the failed form

"""class used for booking a time slot."""
class BookingForm(forms.ModelForm):
    class Meta:
        model = Booking
        fields = ['check_in_date', 'check_in_time', 'check_out_time',
                    'person', 'no_of_rooms']

    def is_valid(self):
        valid = super(BookingForm, self).is_valid()
        # ^ Boolean value

        format = '%H:%M:%S'
        try:
            # Note: `self.cleaned_date.get()`
            datetime.datetime.strptime(str(self.cleaned_data.get('check_in_time')), format).time()
        except Exception:

            self.add_error('check_in_time', 'Wrong time format entered.')
            valid = False

        return valid

Edit

I cloned your project and changed the form to this and I got the error messages to render in the template

Just make sure all your validation is inside is_valid() and it should always work.
And for add_error just put the field that the msg should show up for.

hotel\forms.py

class BookingForm(forms.ModelForm):
    class Meta:
        model = Booking
        fields = ['check_in_date', 'check_in_time', 'check_out_time',
                    'person', 'no_of_rooms']

    def is_valid(self):
        valid = super(BookingForm, self).is_valid()

        # Always Check that it's NOT None before using it
        #   if self.cleaned_data.get('field'):
        #       # do custom validation
        #   else:
        #       # already failed validation

        # Validate both Check-in & Check-out
        for i in ['check_in_time', 'check_out_time']:
            if self.cleaned_data.get(i):
                if str(self.cleaned_data.get(i)).count(':') != 3:
                    self.add_error(i, 'Wrong time format entered.')
                    valid = False

        # Both values are Valid, now make sure check-out is after check-in
        if valid and self.cleaned_data.get('check_in_time') and self.cleaned_data.get('check_out_time'):
            if self.cleaned_data.get('check_out_time') < self.cleaned_data.get('check_in_time'):
                self.add_error('check_out_time', 'You can\'t check-out before check-in.')


        if self.cleaned_data.get('check_in_date'):
            now = timezone.now()
            if self.cleaned_data.get('check_in_date') < now.date():
                self.add_error('check_in_date', 'You can only book for future.')
                valid = False

            if valid:
                # Now validate Date & Time
                if (self.cleaned_data.get('check_in_date') == now.date() and
                    self.cleaned_data.get('check_in_time') < now.time()):
                    self.add_error('check_in_date', 'You can only book for future.')
                    self.add_error('check_in_time', 'You can only book for future.')
                    valid = False

        return valid

Also you should add a requirements.txt file to your repo with pip freeze > requirements.txt This was mine after running your project:

asgiref==3.5.2
Django==4.1.2
djangorestframework==3.14.0
pytz==2022.5
sqlparse==0.4.3
tzdata==2022.5