Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django - Reverse Engineering the Admin site's "Add Foreign Key" button

TL;DR (Short synopsis):

I have recreated the admin "Add" button in my own project. However, when I hit "save" on the parent form, it is not recognizing the new select element.

Whole Story:

I have that functionality working in my own project... almost. I need help figuring out the last step. As it is now, I have a "+" button, I click it, a popup shows up, I add a new object, hit save, popup closes and that new item is now in my select box and selected - just like the admin page. However, when I hit save on this parent form, I get the error that I've selected an item not in the list. Of course, since the page has reloaded, my new item is part of the list and I just hit save again and it works. Of course, I need it to save on the first time!

The basic setup is my Parent model is called System and the foreign key model is called Zone. The Zone model lists how many zones a system has (1 zone,2 zones,10 zones, etc...)

OK, some code:

The "Add" link in the template of the parent form:

<a href="/systems/zones/new/?popup=1" id="add_id_numZones" onclick="return showAddPopup(this);">Add</a>

In my New_Zone view, after saving the new zone, I check if the popup GET variable is 1, if so, return a javascript function. Here's the view:

        ...
        if form.is_valid():
            f = form.save(commit=False)
            pk_value = f.numOfZones
            form.save()
            obj = Zone_Info.objects.get(numOfZones=pk_value)
            if isPopup == "1":
                return HttpResponse('<script>opener.closeAddPopup(window, "%s", "%s");</script>' % (escape(pk_value), escape(obj)))
        ...

And here is my Javascript (largely copied from the admin javascript:

function html_unescape(text) {
// Unescape a string that was escaped using django.utils.html.escape.
    text = text.replace(/&lt;/g, '<');
    text = text.replace(/&gt;/g, '>');
    text = text.replace(/&quot;/g, '"');
    text = text.replace(/&#39;/g, "'");
    text = text.replace(/&amp;/g, '&');
    return text;
}

function windowname_to_id(text) {
    text = text.replace(/__dot__/g, '.');
    text = text.replace(/__dash__/g, '-');
    return text;
}


function showAddPopup(triggeringLink, pWin) {
    var name = triggeringLink.id.replace(/^add_/, '');
    href = triggeringLink.href;
    var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
    win.focus();
    return false;
}

function closeAddPopup(win, newID, newRepr) {
    newID = html_unescape(newID);
    newRepr = html_unescape(newRepr);
    var name = windowname_to_id(win.name);
    var elem = document.getElementById(name);
    if (elem) {
        if (elem.nodeName == 'SELECT') {
            var o = new Option(newRepr, newID);
            elem.options[elem.options.length] = o;
            o.selected = true;
        } else if (elem.nodeName == 'INPUT') {
            if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) {
                elem.value += ',' + newID;
            } else {
                elem.value = newID;
            }
        }
    } else {
        var toId = name + "_to";
        elem = document.getElementById(toId);
        var o = new Option(newRepr, newID);
        SelectBox.add_to_cache(toId, o);
        SelectBox.redisplay(toId);
    }

    win.close();
}

I took a look at this question and it seems that I am doing precisely the same thing.

Any ideas on how to get that last step of getting the form to recognize the new select element?

Thanks!

like image 867
Garfonzo Avatar asked Oct 16 '11 05:10

Garfonzo


1 Answers

I Figured it Out

The problem was what I was passing to my closeAddPopup javascript function. Essentially, I was passing garbage values.

Here's what I originally had in my New_Zone view (which didn't work):

    ...
    if form.is_valid():
        f = form.save(commit=False)
        pk_value = f.numOfZones
        form.save()
        obj = Zone_Info.objects.get(numOfZones=pk_value)
        if isPopup == "1":
            return HttpResponse('<script>opener.closeAddPopup(window, "%s", "%s");</script>' % (escape(pk_value), escape(obj)))
    ...

It's a pretty stupid mistake on my part (clearly it's late). I was assigning f to the field numOfZones which I thought was the pk and sending that to the script.

Now, the working view looks like this:

       if form.is_valid():
           obj = form.save()
           pk_value = obj.pk
           if "_popup" in request.REQUEST:
               return HttpResponse('<script>opener.closeAddPopup(window, "%s", "%s");</script>' % (escape(pk_value), escape(obj)))

Anyway... thanks to... well, Stackoverflow. I don't think I would have solved the problem without posting the question and rereading my code on stackoverflow.

like image 77
Garfonzo Avatar answered Nov 06 '22 00:11

Garfonzo