Kouzla v Django administraci
Přemýšleli jste někdy nad tím, jak je v Django adminu zrealizováno přidávání nových prvků do ForeignKey a M2M políček?
Mám tím na mysli ty situace, kdy na stránce máte například <select>ítko a vedle něj zelené plus — kliknutím na plus se otevře popup okno, do kterého přidáte nový prvek a po uložení se nová hodnota jakoby zázrakem objeví v původním okně:

Jak je to možné? Důmyslnou kombinací Javascriptového kódu a odezvy ze serveru. Pojďme se na to podívat podrobněji.
Představte si formulář s ForeignKey políčkem:
<select id="id_country" name="country">
<option selected="selected" value="">---------</option>
<option value="1">Česká Republika</option>
</select>
<a onclick="return showAddAnotherPopup(this);"
id="add_id_country" class="add-another"
href="/admin/addresses/country/add/">
<img width="10" height="10" alt="Přidat další"
src="/adminmedia/img/admin/icon_addlink.gif"/>
</a>
Za <select>ítkem se nachází odkaz, jehož:
- atribut
idje “add_id_country” (tj. název je odvozen odidprvku<select>— pouze se k němu přidal prefix “add_”) - atribut
onclickobsahuje volání Javascriptového kódushowAddAnotherPopup
Po kliknutí na obrázek s pluskem se začínají dít zajímavé věci:
- JS kód
showAddAnotherPopupotevře popup okno, jehož název odvodí zidodkazu — v tomto případě bude mít názevid_country(prefix “add_” se dá pryč) - do popup okna se načte administrační stránka pro přidání nového prvku (viz atribut
hrefz odkazu výše); JS kód ale k adrese dynamicky přidá GET parametr_popup=1(v našem případě bude mít URL tvar “/admin/addresses/country/add/?_popup=1”), na což Django (resp. contrib aplikace admin) zareaguje jednodušší stránkou — vykreslí se pouze formulář, bez “grafické” hlavičku, menu, drobečkové navigace. - po vyplnění formuláře a kliknutí na submit tlačítko “Uložit” dojde ke speciální obsluze na straně serveru (mrkněte do souboru
django/contrib/admin/options.py, metodaresponse_add). Během zpracovávání dat z formuláře se hlídá, jestli náhodou v POST datech není přítomno políčko “_popup”. Pokud je detekováno (což v našem případě je), odezva ze serveru není redirect, nebo klasická HTML stránka, ale následující Javascriptový (!) kód:
<script type="text/javascript">
opener.dismissAddAnotherPopup(window, "3", "jj");
</script>
opener je vlastnost Javascriptového objektu Window, která obsahuje referenci na okno, ze kterého byl popup vyvolán. Kód opener.dismissAddAnotherPopup(window, "3", "Polsko"); tedy nad původním oknem zavolá JS funkci dismissAddAnotherPopup. Jako parametr ji předá aktuální popup okno, a nově přidanou hodnotu (její id a repr hodnotu).
Funkce dismissAddAnotherPopup si z parametru window zjistí jméno popup okna (“id_country”), pak nalezne v původním okně HTML prvek shodného jména (selektítko s id="id_country") a dynamicky do něj přidá novou položku <option value="3">Polsko</option>.
Geniální!
Studium uvedého kódu pro mně znamenalo objevení zcela nových souvislostí, a to:
- popup okno otevřené Javascriptem má přístup k mateřskému oknu, které jej vyvolalo prostřednictvím vlastnosti
opener - odpověď ze serveru může být i taková zvěřina, jako je samostatný JS kód
- “propojení” HTML prvku ze starého okna a novým oknem prostřednictvím jeho jména — tj. název popup okna je odvozen od
idHTML prvku, do kterého se má nová hodnota přidat