0

Im trying to do what is suggested here: How to reopen a Django form in a jQuery dialog when the form validation fails in the backend?

But I don't have enough points to add a comment there..

In my base html page i have a link which opens a dialog with a Django-form. I use the jquery load() to fill the Dialog with this child-html-template. In this child template i have a submit button. I'm trying to bind this button to an ajax function that will:

  1. Post the form to the right URL
  2. Fetch the response from Django view (the form as HTML to be able to show valdidation errors)
  3. Replace the content in the dialog box with the data i get back from the submit-POST.

Is this possbible? Been working on this for days now and i just cant make it happen. Can somone post an example with code to end my suffering.. It's the ajax that is my biggest problem.

Where should i put the script? In the base or the child template? Do you have any alternative solutions?

Thank you!

Community
  • 1
  • 1
user3199840
  • 525
  • 2
  • 13
  • 35

1 Answers1

1

I did this not long ago in. I found it easier to send the errors in json, and then handle them client-side and attach them to the relevent fields. Like so:

  1. Use ajax to load the form from a view into the jQuery dialog box
  2. When the user sends the data send the information to same view
  3. If validation fails, send errors as a json array. Use js on client-side to attach them to the relevant fields
  4. If succeeds send a positive response of some kind

Check out this excellent example for reference

edit

Here's a working example. Not sure I'm using the best methods to do this, but I think it's pretty understandable. Also, I'm not accounting for the possibility of non-ajax form submit (it's easy enough to do, some logical conditioning using form.is_ajax() and see example linked above for further reference).

So first the views (ContactForm is the same as the one linked):

import json
from django.http import HttpResponse
from django.shortcuts import render_to_response

def home(request):
    return render_to_response('index.html') #nothing special here


from django.views.decorators.csrf import csrf_exempt
from cStringIO import StringIO 

@csrf_exempt #you should use csrf, I'm just skipping this for the example
def contact(request):
    if request.method == 'POST':
        form = ContactForm(request.POST)
        response = {}

        if form.is_valid(): 
            response["status"] = "OK"
            # save the data, or do whatever.

        else:
            response["status"] = "bad"
            response.update(form.errors)

        # now just to serialize and respond
        s = StringIO()
        json.dump(response, s)
        s.seek(0)
        return HttpResponse(s.read())

    else:
        form = ContactForm() # An unbound form

    return render_to_response('contact.html', {
        'form': form,
    })

As you can see, if there's nothing here you get an html with the form, if not, you get a json response with an object called response which contains 'status' and might also contain errors. I'm using StringIO with json.dump(data, file) as it has always proved the least buggy and most fluent way I ever used to serialize to json (seriously. You won't believe how easily it can break).

Now let's go over the client side:

base.html:

<html>
    <head>
         <link rel="stylesheet" href="http://code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css">
          <script src="http://code.jquery.com/jquery-1.9.1.js"></script>
          <script src="http://code.jquery.com/ui/1.10.4/jquery-ui.js"></script>
          <link rel="stylesheet" href="http://getbootstrap.com/2.3.2/assets/css/bootstrap.css">
    </head>
    <body>

    {% block content %}
    {% endblock %}

    </body>
</html>

contact.html:

{% extends 'base.html' %}
{% block content %}
    <form method="post" id='myform'>
    {{ form.as_p }}
    </form>
{% endblock %}

and finally, the main.html with the relevant js. This is where the magic happens:

{% extends 'base.html' %}
{% block content %}

        <button class='btn'>Contact!</button>
        <div id="dialog-modal">
        </div>

    <script>
    $(function() {
        $('button').on('click', function() { 

            // first things firts, fetch the form into the dialog
            $('#dialog-modal').load('contact/ #myform');

            // initiate dialog
            $('#dialog-modal').dialog({
                  height: 450,
                  modal: true,

                  // I'm using the built-in buttons, but you can use your own
                  buttons: {
                        Send: function() { 
                            var dialog = $(this), 
                                form = $('#myform'),
                                data = form.serialize();

                        $('.off').remove(); // this is to avoid duplicates

                        // run ajax post call
                        $.ajax({ 
                            url: 'contact/',
                            data: data,
                            type: 'post',

                            // if successful print response
                            success: function(response) {
                                res = $.parseJSON(response);

                                // if form was successful:
                                if (res['status'] == 'OK') {
                                    // form was successful
                                    alert('Thank you! Form has been submitted'); // say thank you
                                    dialog.dialog('close'); // close dialog
                                }

                                // if not... 
                                else if (res['status'] == 'bad') {
                                    delete res['status'] // we don't need this anymore...
                                    var errors = res; // just for simplicity

                                    $.each(errors, function(key, value) {
                                        // i.e. key='subject' and value is the error message itself

                                        var err = $('<span></span>', {
                                                        'class': 'off',
                                                        'text': value
                                                }),
                                            br = $('<br></br>', {
                                                'class': 'off',
                                            }),
                                            input = $('#id_'+key).parent(); //find the parent div for the relevant input by key

                                        // add a small break
                                        br.appendTo(input);

                                        // add the error
                                        err.appendTo(input);

                                        // add some styling
                                        err.css('color', 'red').css('font-size', '10px');

                                    });
                                }
                            }

                          });

                        }
                      }
                });


        });
    });
    </script>
{% endblock %}

Hope that's not too much. Here's an image how it looks after attempting to send:

enter image description here

Pick it up from here. There's a lot of room to play around and extend this. Good luck!

yuvi
  • 18,155
  • 8
  • 56
  • 93
  • Thank you! :) I will look into that right away! I've been stuck on this dialog problems for a few depressing days now.. – user3199840 Jan 23 '14 at 18:48
  • Yes I am! Im loading a dialog with a html template (django form) from a link in my navbar on my base page. This works fine. But when it comes to submitting and getting the response in the same dialog that is the problem. Beacuse i don't want to reload the base or redirect. I have a map with markers in the background and i want it to stay there while the activity goes on only in the dialog. – user3199840 Jan 23 '14 at 20:02
  • I understand what you're trying to achieve. It'll take a little while, but not much – yuvi Jan 23 '14 at 20:03
  • Thar she blows. Try looking at my answer and the link I gave you and figure out how to do it with your own project – yuvi Jan 23 '14 at 21:01
  • Damn Yuvi! Beautiful! Great stuff! I'll dive in to it right away! :) – user3199840 Jan 23 '14 at 21:04
  • If you found the answer helpful, consider marking it as correct so others facing similar problems will know to use it as well – yuvi Jan 23 '14 at 23:31
  • Yes of course! I had to take a break from the programming so I'm not sure if it's the right solution just yet. And I'm a beginner so it takes some time to go through and understand it all. And while I'm doing that I'd like to have people post alternative solutions also. Like the html rendering thing a was starting with for example. But I'll mark it when I'm done testing out this! Thanks again for taking the time posting it! – user3199840 Jan 24 '14 at 00:13
  • YUVI YOU ARE A F-IN HERO!!! This was exactly the function i was looking for!! I have not fully understand how it all works yet but it's such a great starting point to build on! That really made my day, no that made my whole week! THANK YOU SO MUCH! Hope in humanity restored. – user3199840 Jan 24 '14 at 12:21
  • Sure thing man, happy to have helped out. It's really not that complicated once you learn some jQuery (which I strongly recommend) – yuvi Jan 24 '14 at 16:33
  • Yes that was my problem. My lack of skills in jquery, json and ajax but Im taking some courses on Codeacademy on that. I think a lot of why i couldnt get it the first time was i did'nt use csrf_exempt. (That has to be a later issue.. Hehe) – user3199840 Jan 24 '14 at 17:37
  • Just a quick follow up question. I could easy reuse this function for diffrent forms right? Like login, registration, contact etc? If i change the variables and point the to different views. – user3199840 Jan 24 '14 at 17:43
  • Yes, it's pretty generic, but don't use it blindly. You're goal should be to be able to reconstruct something like this from scratch. I recommend envato's 30 days to learn jQuery, it did wonders for me – yuvi Jan 24 '14 at 19:33
  • OK. Thanks I'll def check that! Right now Im learning Django and following different youtube-tutorials while Im building a project. So this was an important piece to be able to continue into more Django Auth. But the plan is learn it all! – user3199840 Jan 24 '14 at 21:17
  • Hate to bother you again Yuvi but i was just wondering. Is there some work around here if one of the form field is an image field? Can't post images with ajax but i still want the this function validationg the other fields. And i have added some other functions. Really don't want to reconstruct it all again.. – user3199840 Jan 31 '14 at 16:08
  • The only difference would be with sending it not with the validation which would stay pretty much the same. However you should probably open up a new question and ask for help instead of asking here (since it's kind of a different question) – yuvi Jan 31 '14 at 21:16
  • OK im desperate. Getting nowhere.. If you want to just give me a little hint in the right direction I would be very thankful and stop asking you again yuvi! Not getting any response on my 3 other posts at all and my trial and error fails. http://stackoverflow.com/questions/21503802/submit-form-django-form-with-imagefield-via-jquery-ajax-post – user3199840 Feb 03 '14 at 19:19
  • Yeah I saw you asked for help on this like 3 or 4 times. Hopefully I'll find time somewhere tomorrow to have a look – yuvi Feb 03 '14 at 19:39