rwx
rwx

Reputation: 716

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>

Upvotes: 1

Views: 3458

Answers (1)

e4c5
e4c5

Reputation: 53734

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.

Upvotes: 3

Related Questions