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ě:

Přidání nové hodnoty do FK políčka

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 id je “add_id_country” (tj. název je odvozen od id prvku <select> — pouze se k němu přidal prefix “add_”)
  • atribut onclick obsahuje volání Javascriptového kódu showAddAnotherPopup

Po kliknutí na obrázek s pluskem se začínají dít zajímavé věci:

  • JS kód showAddAnotherPopup otevře popup okno, jehož název odvodí z id odkazu — v tomto případě bude mít název id_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 href z 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, metoda response_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 id HTML prvku, do kterého se má nová hodnota přidat
  1. msgre posted this
Blog comments powered by Disqus