Search code examples
djangodjango-viewsdjango-formsdjango-templateshttp-post

Why does form.is_valid return false. Please advice


I have a project where one books a room online then is directed to the checkout page where you input a phone which is required in the backend. Code is as below:

forms.py

from django import forms

class AvailabilityForm(forms.Form):

    check_in = forms.DateTimeField(input_formats=['%Y-%m-%dT%H:%M'], required=True)
    check_out = forms.DateTimeField(input_formats=['%Y-%m-%dT%H:%M'], required=True)


class PhoneNoForm(forms.Form):

    phone_no = forms.IntegerField(required=True)

views.py

class RoomDetailView(View):

    def get(self, request, *args, **kwargs):
        category = self.kwargs.get('category', None)

        got_category = get_room_category(category)

        print(category)
        print(got_category)
    
        form = AvailabilityForm()

        if got_category is not None:
        
            context = {
                'category':got_category,
                'form':form
            }

            return render(request, 'detail.html', context)
        else:
            return HttpResponse("Category Nyet!")

    def post(self, request, *args, **kwargs):
        category = self.kwargs.get('category', None)
        form = AvailabilityForm(request.POST)

        if form.is_valid():
            data = form.cleaned_data
    
            available_rooms = get_available_rooms(category, data['check_in'], data['check_out'] )

            if available_rooms is not None:

                room = available_rooms[0]

                context = {
                    'room':room
                }
        
                return render(request, 'booking/checkout.html', context)
            else:
                return HttpResponse("We're out of those rooms" )
        else:
            return HttpResponse('Form invlid')

class CheckoutView(TemplateView):
    template_name = 'booking/checkout.html'

    def get_phone(request):
        if request.POST:
            phone_no = request.POST.get('PhoneNo', None)
            print(phone_no)

            cl = MpesaClient()
            # Use a Safaricom phone number that you have access to, for you to be able to view the prompt
            phone_number = phone_no
            amount = 1
            account_reference = 'reference'
            transaction_desc = 'Description' 
            callback_url = 'https://darajambili.herokuapp.com/express-payment'

          # callback_url = request.build_absolute_uri(reverse('booking:mpesa_stk_push_callback'))
            response = cl.stk_push(phone_number, amount, account_reference, transaction_desc, callback_url)

            return HttpResponse(response.text)
        
        else:
            return HttpResponse('This is else')
            

checkout.html

    <div>
      <form action="" method="POST">
        {% csrf_token %}
        <label for="Input No.">Input Phone No. :</label>
        <input type="number" name="id_PhoneNo" id="PhoneNo">
        <input type="submit" value="Proceed">
      </form>
    </div>

detail.html

<form id="booking-form" action="" method="POST">

  {% csrf_token %}

    <div class="input-div">
      <label for="id_check_in">Check In : </label>
      <input type="datetime-local" id="id_check_in" name="check_in">
    </div>

    <div class="input-div">
      <label for="id_check_out">Check Out : </label>
      <input type="datetime-local" id="id_check_out" name="check_out">
    </div>

    <div class="input-div">
      <button type="submit">Book the Room</button>
    </div>

  </form>

form.is_valid returns False. I have tried using GET method but i need the phone number for more processing hence my insistence on using POST. If possible, help me fast. Where is the problem? Sorry if it's too much code.


Solution

  • You need to return the errors to the HTML pages to see why the error is occurring. So, if the form is not valid, send that to HTML page by:

    def post(self, request, *args, **kwargs):
        category = self.kwargs.get('category', None)
        form = AvailabilityForm(request.POST)
    
        if form.is_valid():
            ...
        else:
            category = self.kwargs.get('category', None)
            got_category = get_room_category(category)
    
            context = {
                'category':got_category,
                'form':form
            }
    
            return render(request, 'detail.html', context)
    

    And render the errors like this(as per documentation):

    {% for field in form %}
        <div class="fieldWrapper">
            {{ field.errors }}
            {{ field.label_tag }} {{ field }}
            {% if field.help_text %}
            <p class="help">{{ field.help_text|safe }}</p>
            {% endif %}
        </div>
    {% endfor %}
    

    FYI, rather than directly subclassing the View, you should use FormView. Here is an example code on how to use:

    class RoomDetailView(FormView):
        form_class = AvailabilityForm
    
        def get_context_data(self, *args, **kwargs):
            context = super().get_context_data(*args, **kwargs)
            category = self.kwargs.get('category', None)
            got_category = get_room_category(category)
            if got_category is not None:
                context.update({
                    'category':got_category,
                })
                return context
            else:
                raise Http404("Category does not exist")
    
         def form_valid(self, form):
            data = form.cleaned_data
            available_rooms = get_available_rooms(category, data['check_in'], data['check_out'] )
            if available_rooms is not None:  # this part should be in a separate view
               room = available_rooms[0]
               context = {
                   'room':room
               }
               return render(request, 'booking/checkout.html', context)
            else:
               return HttpResponse("We're out of those rooms" )