rwx rwx - 7 months ago 172
Javascript Question

Display validation errors of a Django Form after submitting via jQuery `.ajax`

General



I have a form inside a modal.
The modals content (the form) is loaded by jQuery .load through
$('.modal-content').load('{% url 'edito' q.id %}');
. Django returns by this URL through render_to_string the HTML for the
.modal-content
.

Problem



If I submit the form I want to rerender the form in the
.modal-content
if there occured validation errors, otherwise close the modal.

I get it only to work either with closing the modal independent if there are validation errors or not or I get redirected to the forms
action={% url 'edito' q.id %}
without stylings because django only returns the content for
.modal-content
.

Code



edito.html (modals content, that goes in
.modal-content
)



<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">Edit</h4>
</div>
<div class="modal-body">
<form id="myForm" method="post" class="form" role="form" action="{% url 'edito' q_id %}">{% csrf_token %}
<fieldset>
{% for field in form %}
{% if field.errors %}
<div class="form-group has-error">
<label for="{{ field.id_for_label }}" class="control-label">{{ field.label }}</label>
<input class="form-control" id="{{ field.id_for_label }}" name="{{ field.html_name }}" placeholder="{{ field.field.widget.attrs.placeholder }}" type="text" />
<span class="help-block">{% for error in field.errors %}{{ error }}<br />{% endfor %}</span>
</div>
{% else %}
<div class="form-group">
<label for="{{ field.id_for_label }}" class="control-label">{{ field.label }}</label>
<input class="form-control" id="{{ field.id_for_label }}" name="{{ field.html_name }}" placeholder="{{ field.field.widget.attrs.placeholder }}" type="text" value="{{ field.value }}" />
</div>
{% endif %}
{% endfor %}
</fieldset>
<div class="form-actions">
<input id="sendMe" type="submit" class="btn btn-primary" value="Edit" />
<!--<a class="btn btn-warning" href="../..">Cancel</a>-->
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>

<script type="text/javascript">
$('#myForm').submit(function() { // catch the form's submit event
$.ajax({ // create an AJAX call...
data: $(this).serialize(), // get the form data
type: $(this).attr('method'), // GET or POST
url: $(this).attr('action'), // the file to call
success: function(response) { // on success..
$('.modal-content').html(response); // should check if with validation errors
$('#myModal').modal("hide");
},
});
return false;
});
</script>


This was the one where the modal is closed anyway. If I use the following script instead I'll be redirected to the styleless HTML site returned by
render_to_string
.

Alternative script part



<script type="text/javascript">
$('#myForm').live('submit', function() {
$.ajax({
type: $(this).attr('method'),
url: this.action,
data: $(this).serialize(),
context: this,
success: function(data, status) {
$('#myModalContent').html(data);
}
});
return false;
});
</script>


views.py



def edito(request, **kwargs):
if request.method == 'POST':
instance = Question.objects.get(id=kwargs.get("question_id"))
form = QuestionCreationForm(request.POST, instance=instance)
if form.is_valid():
form.save()
return HttpResponse(
render_to_string(
"edito.html",
{'q_id': kwargs.get("question_id"), },
request=request,
)
)
else:
return HttpResponse(
render_to_string(
"edito.html",
{
'q_id': kwargs.get("question_id"),
'form': form,
},
request=request,
)
)
else:
q = Question.objects.get(id=kwargs.get("question_id"))
prefilled_data = {
...
}
form = QuestionCreationForm(initial=prefilled_data)
return HttpResponse(
render_to_string(
"edito.html",
{
'form': form,
'q_id': kwargs.get("question_id")
},
request=request,
)
)


main.html (where the modal is in)



<div class="modal fade" id="myModal" role="dialog">
<div class="modal-dialog">
<div id="myModalContent" class="modal-content">
</div>
</div>
</div>

<a href="{% url 'edito' q.id %}" onclick="$('.modal-content').load('{% url 'edito' q.id %}');" data-toggle="modal" data-target="#myModal"><span class="glyphicon glyphicon-edit"></span></a>

Answer

Form.errors.as_json Is ideally suited for this.

Returns the errors serialized as JSON.

Following is an example from the page linked to above after a call to the as_json() method.

{"sender": [{"message": "Enter a valid email address.",
 "code": "invalid"}], "subject": [{"message": "This field is
> required.", "code": "required"}]}

When the form is submitted and is invalid, your django form handler should send this JSON response. Then your javascript can iterate through the list and mark the appropriate form fields.

if the form submit is a success your django code should return something similar to {'status': 'ok'} and then your javascript can close the modal.