74

I've checked out tons of tutorials for django AJAX forms, but each one of them tells you one way of doing it, none of them is simple and I'm a bit confused since I've never worked with AJAX.

I have a model called "note", a modelform for it, and inside the template I need that everytime a note element sends the stop() signal (from jQuery Sortables) django updates the object.

My current code:

views.py

def save_note(request, space_name):

    """
    Saves the note content and position within the table.
    """
    place = get_object_or_404(Space, url=space_name)
    note_form = NoteForm(request.POST or None)

    if request.method == "POST" and request.is_ajax:
        msg = "The operation has been received correctly."          
        print request.POST

    else:
        msg = "GET petitions are not allowed for this view."

    return HttpResponse(msg)

JavaScript:

function saveNote(noteObj) {
    /*
        saveNote(noteObj) - Saves the notes making an AJAX call to django. This
        function is meant to be used with a Sortable 'stop' event.
        Arguments: noteObj, note object.
    */
    var noteID = noteObj.attr('id');

    $.post("../save_note/", {
        noteid: noteID,
        phase: "Example phase",
        parent: $('#' + noteID).parent('td').attr('id'),
        title: $('#' + noteID + ' textarea').val(),
        message: "Blablbla",
    });
}

The current code gets the data from the template and prints it in the terminal. I don't know how I can manipulate this data. I've seen some people manages the data through jqueryforms to send the data to django.

How can I access the data sent by AJAX and update the note object?

razieh babaee
  • 298
  • 5
  • 19
CastleDweller
  • 8,204
  • 13
  • 49
  • 69

6 Answers6

129

Since you are using jQuery why not use the following:

<script language="JavaScript">
    $(document).ready(function() {
        $('#YOUR_FORM').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..
                    $('#DIV_CONTAINING_FORM').html(response); // update the DIV 
                }
            });
            return false;
        });
    });
</script>

EDIT

As pointed out in the comments sometimes the above won't work. So try the following:

<script type="text/javascript">
    var frm = $('#FORM-ID');
    frm.submit(function () {
        $.ajax({
            type: frm.attr('method'),
            url: frm.attr('action'),
            data: frm.serialize(),
            success: function (data) {
                $("#SOME-DIV").html(data);
            },
            error: function(data) {
                $("#MESSAGE-DIV").html("Something went wrong!");
            }
        });
        return false;
    });
</script>
doğukan
  • 23,073
  • 13
  • 57
  • 69
Robert Johnstone
  • 5,431
  • 12
  • 58
  • 88
  • Thanks, I will probably use this solution. The others also work, but I think this will be cleaner (withour using jQuery forms plugin) – CastleDweller Sep 08 '11 at 11:03
  • $(this).serialize() can't catch `submit` input. For instance if we have two submit buttons in a form! Can you tell me how to handle it? – Surya Jul 07 '13 at 20:34
  • @Sevenearths,while saving a form data in a method using ajax post,should i use return render or HttpResponse,need clarification – user2086641 Aug 08 '13 at 18:15
  • 2
    first at the top of your `views.py` use: `from django.utils import simplejson`. Then do something like `returnedJSON['message_type'] = 'success' returnedJSON['message'] = 'The something saved successfully' return HttpResponse(simplejson.dumps(returnedJSON), mimetype="application/json")`. Something like that should work – Robert Johnstone Aug 12 '13 at 08:24
  • what if I don't want to frm.serialize? i want to get individuals frm.values of every field? – Muhammad Taqi Feb 03 '15 at 07:29
  • Then access the input element directly using jquery `$('#something').val()` – Robert Johnstone Feb 05 '15 at 18:24
  • May I ask a question? What is the purpose of "return false;" in the script? – Huy Than Jun 23 '16 at 16:49
  • Sure. It's so the standard form submission does not complete. If you took it out the form would be submitted twice. Once as a normal form submission (page sent to server, server process form, response sent to browser, etc...). Second as an ajax request – Robert Johnstone Jun 24 '16 at 15:49
11

You can access the data on the POST request using the name of the variable, in your case:

request.POST["noteid"]
request.POST["phase"]
request.POST["parent"]
... etc

The request.POST object is inmutable. You should assign the value to a variable, and then manipulate it.

I would advise you to use this JQuery plugin, so you can write normal HTML forms and then get them "upgraded" to AJAX. Having $.post everywhere in you code is kind of tedious.

Also, use the Network view on Firebug(for Firefox) or the Developer Tools for Google Chrome so you can view what's being sent by you AJAX calls.

Gerardo Curiel
  • 161
  • 1
  • 6
  • 1
    Fair warning for accessing data directly from the POST object for those coming here for answers in the future. Ensure you access data after validation using the `cleaned_data` attribute [2.2 docs](https://docs.djangoproject.com/en/2.2/topics/forms/#building-a-form-in-django). Wasn't sure on the op's version of Django but it seems consistent as far back as 1.4.6 or more. – Clay Apr 15 '19 at 23:10
5

Something to look out for is when returning the form as an html snipped to a modal.

Views.py

@require_http_methods(["POST"])
def login(request):
form = BasicLogInForm(request.POST)
    if form.is_valid():
        print "ITS VALID GO SOMEWHERE"
        pass

    return render(request, 'assess-beta/login-beta.html', {'loginform':form})

Simple view to return the html snipped

Form html Snipped

<form class="login-form" action="/login_ajx" method="Post"> 
  <div class="modal-header">
    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
    <h4 class="modal-title" id="header">Authenticate</h4>
  </div>
  <div class="modal-body">
        {%if form.non_field_errors %}<div class="alert alert-danger">{{ form.non_field_errors }}</div>{%endif%}
        <div class="fieldWrapper form-group  has-feedback">
            <label class="control-label" for="id_email">Email</label>
            <input class="form-control" id="{{ form.email.id_for_label }}" type="text" name="{{ form.email.html_name }}" value="{%if form.email.value %}{{ form.email.value }}{%endif%}">
            {%if form.email.errors %}<div class="alert alert-danger">{{ form.email.errors }}</div>{%endif%}
        </div>
        <div class="fieldWrapper form-group  has-feedback">
            <label class="control-label" for="id_password">Password</label>
            <input class="form-control" id="{{ form.password.id_for_label }}" type="password" name="{{ form.password.html_name}}" value="{%if form.password.value %}{{ form.password.value }}{%endif%}">
            {%if form.password.errors %}<div class="alert alert-danger">{{ form.password.errors }}</div>{%endif%}
        </div>
  </div>
  <div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<input type="submit" value="Sign in" class="btn btn-primary pull-right"/>
</div>
</form>

Page containing the modal

<div class="modal fade" id="LoginModal" tabindex="-1" role="dialog">{% include "assess-beta/login-beta.html" %}</div>

Use the include tag to load the snipped on page load so it is available when you open the modal.

Modal.js

$(document).on('submit', '.login-form', function(){
$.ajax({ 
    type: $(this).attr('method'), 
    url: this.action, 
    data: $(this).serialize(),
    context: this,
    success: function(data, status) {
        $('#LoginModal').html(data);
    }
    });
    return false;
});

Using the .on() in this case work like .live() the key being binding the submit event not to the button but to the document.

JUhrig
  • 61
  • 1
  • 2
  • Your solution worked for me, the only thing I need to change was `data: {}` to `data: $(this).serialize()`. – Jarrod Sep 20 '17 at 03:16
3

As the other answers do work, I prefer to use the jQuery Form Plugin. It fully supports what you want and more. The post view is handled as usual in the Django part, just returning the HTML that is being replaced.

Armando Pérez Marqués
  • 5,661
  • 4
  • 28
  • 45
2

On the server side, your django code can process the AJAX post the same way it processes other form submissions. For example,

views.py

def save_note(request, space_name):

    """
    Saves the note content and position within the table.
    """
    place = get_object_or_404(Space, url=space_name)
    note_form = NoteForm(request.POST or None)

    if request.method == "POST" and request.is_ajax():        
        print request.POST
        if note_form.is_valid():
            note_form.save()
            msg="AJAX submission saved"
        else:
            msg="AJAX post invalid"
    else:
        msg = "GET petitions are not allowed for this view."

    return HttpResponse(msg)

I've assumed your NoteForm is a ModelForm -- which it should be -- so it has a save method. Note that in addition to adding the save() command, I changed your request.is_ajax to request.is_ajax(), which is what you want (if you use request.is_ajax your code will just check whether the request has a method called is_ajax, which obviously it does).

user931920
  • 275
  • 1
  • 3
1

Most of examples of using AJAX POST with Django forms, including the official example:

https://docs.djangoproject.com/en/1.9/topics/class-based-views/generic-editing/#ajax-example

are fine when ModelForm.clean() did not produce any errors (form_valid). However, they do not perform hard part: translating ModelForm errors via AJAX response to Javascript / DOM client-side.

My pluggable application uses AJAX response routing with client-side viewmodels to automatically display class-based view AJAX post ModelForm validation errors similar to how they would be displayed in traditional HTTP POST:

https://django-jinja-knockout.readthedocs.org/en/latest/forms.html#ajax-forms-processing https://django-jinja-knockout.readthedocs.org/en/latest/viewmodels.html

Both Jinja2 and Django Template Engine are supported.

Dmitriy Sintsov
  • 3,821
  • 32
  • 20