2

I am building a Django app that has a main dashboard with a bunch of on/off toggles. My goal would be to allow the user to toggle their settings on or off and have their changes automatically saved to the database.

So I followed this tutorial to leverage Ajax form submissions in my Django app so that the user would not have to reload the page. The problem that I am having (I think) is that the checkbox values are being POSTed to my Django views as 'on' whereas they should be POSTed as 'True'.

I think that this is the cause of the error, because I see this in my error logs:

Exception Type: IntegrityError at /update_usersettings/
Exception Value: dataplan_usersettings.normalize_person_name_proper may not be NULL

...

POST:
id_normalize_person_name_proper = u'on'
id_normalize_company_name_proper = u'on'

My existing JavaScript and Django views.py are here: https://gist.github.com/joefusaro/25b6221536291c1ba0d1

Update: I've added the relevant Django template and forms code here. Not that I am using widget_tweaks for form rendering. The form renders like so:

<form action="/update_usersettings/" method="POST" id="usersettings_form">
<input checked="checked" class="bootstrap-switch-default" id="id_normalize_person_name_proper" name="bootstrap-switch" type="checkbox" />
<input checked="checked" class="bootstrap-switch-default" id="id_normalize_company_name_proper" name="bootstrap-switch" type="checkbox" />
<input type="submit" value="Post">

FINAL UPDATE

Thanks to Animesh for a great starting point. Here is the final Ajax code required. Special thanks to Victor K. for his help figuring this out!

Here is the final Ajax code:

$(function() {


// Submit post on submit
$('#usersettings_form').on('submit', function(event){
    event.preventDefault();
    console.log("Form submitted...")  // sanity check
    save_usersettings();
});

$('input[type="checkbox"]').on('switchChange.bootstrapSwitch', function(event, state) {
    $('#usersettings_form').submit();
});

// AJAX for posting.
function get_post_data() {
    var data = {};

    $('input:checkbox').each(function () {
        var $this = $(this);
        var id = $this.attr('id');
        data[id.replace('id_', '')] = $this.is(':checked');
    });

    return data;
}

function save_usersettings() {
    console.log("Saving user settings...") // sanity check
    $.ajax({
        url : "update_usersettings/", // the endpoint
        type : "POST", // http method
        data : get_post_data(),
        // handle a successful response
        success : function(json) {
            console.log(json); // log the returned json to the console
            // $("#talk").prepend("<li><strong>"+json.text+"</strong> - <em> "+json.author+"</em> - <span> "+json.created+
            //     "</span> - <a id='delete-post-"+json.postpk+"'>delete me</a></li>");
            console.log("Successfully saved user settings."); // another sanity check
        },
        // handle a non-successful response
        error : function(xhr,errmsg,err) {
            // $('#results').html("<div class='alert-box alert radius' data-alert>Oops! We have encountered an error: "+errmsg+
            //     " <a href='#' class='close'>&times;</a></div>"); // add the error to the dom
            console.log(xhr.status + ": " + xhr.responseText); // provide a bit more info about the error to the console
        }
    });
};

And here is the final views.py:

class UpdateUserSettings(LoginRequiredMixin, UpdateView):
    model = UserSettings
    form = UserSettingsForm

    def post(self, request, *args, **kwargs):
      if request.method=='POST':
        response_data = {}

        form = UserSettingsForm({})

        us = UserSettings.objects.get(user=1)

        VALUE_MAP = {
            'false': False,
            'true': True
        }

        for name, field  in form.fields.items():
            if isinstance(field, BooleanField):
                if request.POST.get(name):
                    if request.POST[name] in VALUE_MAP.keys():
                        setattr(
                            us,
                            name,
                            VALUE_MAP[request.POST[name]]
                        )

        us.save()

        response_data['result'] = 'Update successful!'

        return HttpResponse(
            json.dumps(response_data),
            content_type="application/json"
        )
      else:
        return HttpResponse(
            json.dumps({"nothing to see": "this isn't happening"}),
            content_type="application/json"
        )
Community
  • 1
  • 1
Joe Fusaro
  • 847
  • 2
  • 11
  • 23

1 Answers1

2

You can handle this inside the view.

CHECKBOX_MAPPING = {'on':True,
                    'off':False,}
class UpdateUserSettings(LoginRequiredMixin, View):
    model = UserSettings

    def post(self,request,*args,**kwargs):
        normalize_person_name_proper = CHECKBOX_MAPPING.get(request.POST.get('normalize_person_name_proper'))

You can do this for all the fields you are supposed to receive in checkboxes from the user.

Also, one thing to note here is that you need not use request.method=='POST'check inside the post method of a class based generic view. The method will be called only when the request is POST.What you are looking for is if request.is_ajax():

Hope this helps.

Animesh Sharma
  • 3,258
  • 1
  • 17
  • 33
  • Hi, thanks for jumping in and trying to help! I implemented your suggestion but I am still receiving the same error, "dataplan_usersettings.normalize_person_name_proper may not be NULL" – Joe Fusaro Mar 03 '15 at 04:37
  • Can you please check up what values you get in the POST dict. I guess you are getting an empty value from the frontend in 'normalize_person_name_proper'. – Animesh Sharma Mar 03 '15 at 05:01
  • Here is some add'l output from the JS console: Exception Type: IntegrityError at /update_usersettings/ Exception Value: dataplan_usersettings.normalize_person_name_proper may not be NULL Request information: GET: No GET data POST: id_normalize_person_name_proper = u'on' id_normalize_company_name_proper = u'on' – Joe Fusaro Mar 03 '15 at 05:05
  • So it looks like 'on' is being POSTed...unless I am not understanding your question... – Joe Fusaro Mar 03 '15 at 05:06
  • Seems like the problem is with key of the POST dict you are trying to access. You are looking for `request.POST.get('normalize_person_name_proper')` whereas it should `request.POST.get('id_normalize_person_name_proper')` – Animesh Sharma Mar 03 '15 at 05:10
  • Ah, yes. Thank you. So this variable oversight (on my part) combined with your original comment look like it *should* work....The only problem now is this: when toggling from unchecked to checked ('off' to 'on') the changes are successfully saved to the database....but for some reason when toggling from checked to unchecked, the change does not save to the database. I can only imagine that this is because 'off' is not matching up with the CHECKBOX_MAPPING dict...any ideas on how to overcome this final roadblock? – Joe Fusaro Mar 03 '15 at 05:34
  • A little research has shown that checkboxes do not transmit an 'off' value: http://stackoverflow.com/questions/4260918/why-do-html-checkboxes-only-transmit-an-on-value – Joe Fusaro Mar 03 '15 at 05:36
  • Then it will just be: `normalize_person_name_proper = CHECKBOX_MAPPING.get(request.POST.get('id_normalize_person_name_proper'),False)` – Animesh Sharma Mar 03 '15 at 05:39
  • That doesn't seem to work either...still only able to toggle from off to on – Joe Fusaro Mar 03 '15 at 05:47
  • Could you share your models file which contains UserSettings model? – Animesh Sharma Mar 03 '15 at 05:53
  • Hi - thanks again for your help here. I have updated the Gist with my models.py - https://gist.github.com/joefusaro/25b6221536291c1ba0d1#file-models-py – Joe Fusaro Mar 03 '15 at 14:35