Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Admin - RelatedObjectLookups - How Does it refresh and set the select on the parent window?

I want one of my forms to work just like the admin page does so I figured I'd look in the code and see how it works.

Specifically I want the user to be able to click a "+" icon next to a select list and be taken to the admin page's popup form to add a new item.

When they enter a new item there, I want that new item to appear in the select box, and be selected (Just like how this feature works on the admin pages).

I copied the admin js libraries into my own template, and I made my link call the same JS function and the popup windows does open correctly, but after I save a new object the popup window goes blank instead of closing, and nothing happens on the parent page.

Here's what I put in my page:

...
<td>
    <div class="fieldWrapper">
        <select name="form-0-plasmid" id="id_form-0-plasmid">
        ...
        </select>
        <a href="/admin/VirusTracker/plasmid/add/" class="add-another" id="add_id_plasmid" onclick="return showAddAnotherPopup(this);"> <img src="/media/admin/img/admin/icon_addlink.gif" width="10" height="10" alt="Add Another"/></a>
    </div>
</td>
...

I tried stepping through the javascript on the admin form to see how it's working, but I'm not seeing anything that would close the window or populate the parent window's select.

Thanks in advance for any help.

Update 3

I'm getting this javascript error when dismissAddAnotherPopup is run

"SelectBox is not defined"

Which is pointing to this line in dismissAddAnotherPopup

SelectBox.add_to_cache(toId, o);

I thought I knew Javascript, but I don't see where that variable is supposed to come from :-(

Update 2

Everything seems to be firing properly. After I click save on the popup window I get a blank page. This is the source of that page:

<script type="text/javascript">opener.dismissAddAnotherPopup(window, "9", "CMV_flex_myr_GENE1_._._WPRE_BGH");</script>

So it would seem that this javascript isn't being executed or is failing.

Update

Here is the relevant code that Daniel mentioned. So the only problem is that this code either isn't firing, or is firing incorrectly.

django/contrib/admin/options.py:

...
        if request.POST.has_key("_popup"):
            return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>' % \
                # escape() calls force_unicode.
                (escape(pk_value), escapejs(obj)))
...

/media/admin/js/admin/RelatedObjectLookups.js:

function dismissAddAnotherPopup(win, newId, newRepr) {
    // newId and newRepr are expected to have previously been escaped by
    // django.utils.html.escape.
    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();
}
like image 972
Greg Avatar asked Jan 20 '23 22:01

Greg


2 Answers

Ok, the javascript simply uses the id attribute of the launching element to identify the select field to update. (after removing 'add_' from the beginig).

So I simply changed the link's id attribute to match the select element's id in my template:

<a href="/admin/VirusTracker/plasmid/add/" class="add-another" id="add_id_{{field.html_name}}" onclick="return showAddAnotherPopup(this);"> <img src="/media/admin/img/admin/icon_addlink.gif" width="10" height="10" alt="Add Another"/></a>

Wow I wish this had been documented somewhere! I lost a few hours on this.

(See my updates to the question for more technical details on how it all works.)

like image 173
Greg Avatar answered Jan 31 '23 00:01

Greg


The trick - and it's a bit of a hack, actually - is what happens when you click save on the popup in the admin.

If you look at the code of response_add in django.contrib.options.ModelAdmin, you'll see that when you save an item in the popup, the admin returns an HttpResponse consisting solely of a piece of Javascript. This JS calls the dismissAddAnotherPopup function in the parent window, which closes the popup and sets the form value appropriately.

It's fairly simple to copy this functionality into your own app.

Edited after updates If admin javascript doesn't work, it's usually because it has a dependency on the jsi18n code - which you include via a URL (not a static path):

<script type="text/javascript" src="/admin/jsi18n/"></script>
like image 32
Daniel Roseman Avatar answered Jan 30 '23 23:01

Daniel Roseman