I have a good bit of experience in non-Django web development, but I am struggling to find a good practice for handling what to do when the user enters invalid data in their form and I want to re-display the form with their submitted data and the form errors displayed as well. I have a simple form with three fields and this is how I finally made it work.
def get(self, request) :
# Check if we have been redirected...
redirect_html = request.session.pop('form_error_html', False)
if redirect_html : return HttpResponse(redirect_html)
old_data = {'title': 'SakaiCar', 'mileage' : 42,
'purchase_date': '2018-08-14' }
form = BasicForm(initial=old_data)
ctx = {'form' : form}
return render(request, 'form.html', ctx)
def post(self, request) :
form = BasicForm(request.POST)
if not form.is_valid() :
ctx = {'form' : form}
html = render_to_string('form.html', ctx, request=request)
request.session['form_error_html'] = html
return redirect(request.path)
# Do something with the valid data..
return redirect('/')
My template is really basic (I like this simplicity):
<p>
<form action="" method="post">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type="submit" value="Submit">
</form>
</p>
Now this approach kind of weirds me out because I am sending the entire rendered HTML page through the session from the post()
to the get()
. But I can't send the form
with the errors in place through the session back to the the get()
(that would be prettier) because it won't serialize - you get "Object of type 'BasicForm' is not JSON serializable".
I have done this a different way where I extract the errors form the form
object into a list
and then pass my own list of errors from the post()
to the redirected get()
and then alter form.html
to display errors.
{% if errors %}
{% for error in errors %}
<p style="color:red">Error in {{ error.label }}: {{ error.message }}</p>
{% endfor %}
{% endif %}
I have not included all the Python code to make this work - but you get the idea. This feels more elegant because I am not putting a blob of HTML into the session, but then the errors display in a way other than the normal Django forms way. And if I were using crispy forms - then all that crispy UI goodness would not come into play.
I even thought about pulling out the errors in the post()
code and passing them to the get()
through the session and then inserting them into the form
object in the get()
prior to render()
- that would feel more elegant too. If I get bored I might try to dig through the forms structure and implement this as well.
I just cannot believe that with Django having so mush awesome built-in magic - that I can't just say something like return form.post_redirect_get()
in the not form.is_valid
code.
I want something that is a replicable pattern that is easily explained and used the Django UI elements as much as possible.