Scukařina, žádná dřina – díl druhý: Django je naše máma

V druhém díle Scukopříběhu se zaměříme na praktické momenty vývoje v Djangu. Představíme si sehranou trojici z vývojové laboratoře, zajedeme s databází k moři a nakonec lusknutím prstu připravíme vašemu klientovi velké administrační překvapení. Vše v článku, stačí jen kliknout a číst.

Seriál: Scukařina, žádná dřina (5 dílů)

  1. Scukařina, žádná dřina – díl první: agilně! 18.8.2010
  2. Scukařina, žádná dřina – díl druhý: Django je naše máma 25.8.2010
  3. Scukařina, žádná dřina – díl třetí: Djangová je náš táta 1.9.2010
  4. Scukařina, žádná dřina – díl čtvrtý: jiná práce s CSS 8.9.2010
  5. Scukařina, žádná dřina – appendix 15.9.2010

Poznámka: Jde do tuhého. Obecnost jsme nechali v prvním díle, odteď musíte vědět, co to Django je, nebo alespoň zahrát melodii ve stupnici 1 2 b3 #4 5 b6 7. Stejně jako minule doporučuji seriál na Zdrojáku, přeložený tutoriál nebo oficiální anglickou dokumentaci.

Zdlouhavé konfigurace? Nikoliv

Django obsahuje tři naprosto zásadní pomůcky: vývojový server, podporu SQLite a interaktivní shell.

Vývojový server vás odstiňuje od nutnosti konfigurovat lokální webový server (třeba Apache). Jednoduše vlezete do adresáře s projektem, napíšete ./manage.py runserver a do prohlížeče vložíte adresu  http://127.0.0.1:8000/

SQLite je jednou z Djangem podporovaných databází. Milé na tomto capartovi je to, že se nemusí skoro nic nastavovat. Stačí do souboru settings.py uvést dvě konstanty (DATABASES ENGINE a NAME ) a databáze jede.

Pokud používáte Python, určitě jste si už potykali s jeho shellem. Django disponuje podobnou konzolí, která oproti staršímu bráchovi získává možnost manipulovat s objekty Djanga a vašeho projektu (např. provádět ORM dotazy).

Všechny zmíněné celky jdou ruku v ruce s rychlým a efektivním vývojem:

  • Před napsáním kódu nejdříve podráždím aplikaci přes konzoli ./manage.py
    shell
    , a hledám optimální formu algoritmu.
  • Při raném návrhu modelů zase často měním databázová pole. Protože v této fázi zpravidla nemám v DB žádná důležitá data, smažu soubor s SQLite databází a zavolám ./manage.py syncdb (přece se nebudu zdržovat SQL  ALTER y).
  • A vývojový server? Ten doceníte při ladění chyb. Stačí do kódu vložit pár print ů nebo rovnou import pdb; pdb.set_trace() a hned v konzoli uvidíte, co se děje (indián to sice umí taky, ale složitěji).

Poznámka: Pokud budete do svého projektu později nasazovat „dospělejší“ databázi a chcete zůstat nervově stabilní, moc to s SQLite nepřehánějte. Mezi jednotlivými DB totiž existují drobné rozdíly, které by se mohly po nasazení na produkčním serveru nepříjemně projevit.

Cesta na jih

Migrace — Achillova pata Djanga. Na rozdíl od Railsů Django neobsahuje nativní podporu pro databázové migrace. Pokud tedy v průběhu vývoje změníte existující model, musíte si jej sami na databázové úrovni upravit. Naštěstí zde ale máme aplikaci South, která nám s touto bolístkou pomůže.

Vytvoření migrace

Předpokládejme, že už nějakou dobu vyvíjíte aplikaci customers.Customer, v databázi máte uložena důležitá data a přijde požadavek na změnu modelu. Vlezte do systémové konzole a zavolejte:

./manage.py convert_to_south customers 

Tímto způsobem dáte „Jižanovi“ najevo, že má vaši aplikaci customers sledovat a zapamatovat si její aktuální stav. Současně vznikne uvnitř customers podadresář migrations, do kterého si bude South ukládat migrační skripty.

Zpět k aplikaci — proveďte plánovanou úpravu modelu a opět z konzole zavolejte:

./manage.py schemamigration customers --auto 

customers je jméno migrované aplikace a parametr --auto způsobí, že South provede automatickou detekci změn a vygeneruje kód migrace. Schválně se teď podívejte do adresáře customers/migrations/, kde objevíte nový soubor a uvnitř v metodě forward kód provádějící změnu.

Aplikování migrace

Všechny dosud provedené operace ale s modelem, resp. tabulkou v databázi, reálně nic neprovedly. Aplikování migrací musíte spustit explicitně:

./manage.py migrate 

Teprve teď budou databázové tabulky „lícovat“ s vašimi Django modely.

Migrace dat

Pokud svým modelem měníte databázové schéma (např. nahrazujete pole name za fname a lname) a zároveň dochází ke změně uložených dat (hodnotu uvnitř name rozbijete podle mezery, a uložíte do fname a lname), musíte si si tento úkol rozdělit do několika samostatných migrací. Například v první přidáte do modelu pole fname a lname, v druhé překonvertujete data a ve třetí smažete pole name. Mixovat migrace schémat a dat není dobrý nápad.

Vytvořit migraci pro pozměněný model už umíme. Co ale s tou konverzí? Toto:

./manage.py datamigration customers split_name 

Parametrem datamigration vytvoříte migrační skript, který zdánlivě nic nedělá (metoda forward je prázdná). V tomto případě musíte sami doplnit kód, který se o konverzi dat postará:

def forwards(self, orm):
    for c in orm.Customer.objects.all():
        c.fname, c.lname = c.name.split(' ')
        c.save()

Všimli jste si toho „orm“? Pokud na úrovni migrací přistupujete k modelům, nedělejte to obvyklou formou přes importy (např. from
myproject.customers.models import Customer
), ale přes argument orm. Jde o to, že s pomocí „orm“ vytáhnete požadovaný (pseudo)model v takovém stavu, v jakém byl v době psaní skriptu.

Navážeme na dříve zmiňovaný příklad. V modelu Customer máme pole name, které chceme rozdělit do dvou samostatných polí. Veškerá již uložená data v DB chcete uchovat a zkonvertovat do nové podoby. Zapomenete ale na „orm pravidlo“ (importujete model Customer přímo). Na vašem počítači vše funguje jak má, ./manage.py migrate proběhne bez obtíží.

Po dokončení všech úprav se vrhnete na server: zaktualizujete zdrojové kódy, spustíte migrační skripty nad starou databází a… vyskakuje traceback s jakousi chybou. Haló, haló, co se stalo?

Vývoj na vašem počítači je kontinuální. Každou změnu v modelu promítáte do migrací a ty hned aplikujete na databázi. Modely a tabulky jsou vždy synchronní. Na serveru ale dojde ke skokové změně zdrojových kódů (např. spojením větve):

Datová migrace na serveru natáhne přes import nejaktuálnější podobu modelu, ta ale neodpovídá stavu databáze.
(kliknutím zobrazíte originální obrázek)

Využitím konstrukcí „orm.Model“ se podobným nepříjemnostem vyhneme.

Správcem za minutu

Pokud se to k vám ještě nedoneslo, tak administrační rozhraní Djanga je koží! O co jde?

Během desítek vteřin budete schopni rozjet komfortní webovou administraci pro kterýkoliv z modelů svého projektu. Schválně se podívejte do oficiální dokumentace django.contrib.admin  — pouze svůj model zaregistrujete a pak s pomocí atributů třídy ModelAdmin ladíte jeho konkrétní podobu (vyhledávání, filtry, rozvržení prvků formuláře, …). Mňam!

Je přirozené, že časem z „atributové“ košilky vyrostete a budete chtít víc. Třeba:

  • zobrazovat vypočítané hodnoty (tj. nejen to, co máte ve sloupcích DB tabulek)
  • manipulovat pouze s objekty, na které má přihlášený uživatel právo
  • vybavit editační formulář dalšími funkčními prvky…
  • …nebo do něj dynamicky generovat data či rovnou formulářová pole

V tom případě se vám otevře další vesmír, protože django.contrib.admin vývojáři nabízí slušnou baterii „háčků“ (metod, které můžete přetížit vlastním kódem). Aplikace django.contrib­.admin je právem označována za kyľafičr frameworku Django.

Praktická ochutnávka

Z počátku jsme na Scuku pro editaci podniku používali následující formulář:

Administrace podniku poskládaná z obvyklých atributů aplikace  django.contrib.admin

Po vpuštění betatesterů nám ale začaly prudce přibývat recenze a hlášení o chybách. Pohodlnost správy se pomaličku vytrácela, protože jsme se museli neustále proklikávat jednotlivými obrazovkami administrace. Jeden večer jsem se proto spustil a zaflirtoval s Djangem:

Upravený administrační formulář nyní obsahuje informace i o provázaných modelech

Administrátor po rozkliknutí záznamu hned vidí seznam recenzí, hlášení o chybách a informaci o průměrném hodnocení podniku (která se počítá speciálním algoritmem).

Realizace

A cože se onu noc dělo? Asi vás zklamu, moc daleko jsme se nedostali. Stačilo totiž přetížit jednu metodu a vytvořit šablonu:

Soubor shops/admin.py:

from django.contrib import admin
class ShopAdmin(admin.ModelAdmin):
    # ...
    # (obvykla konfigurace admin tridy)
    # ...
    def change_view(self, request, object_id, extra_context=None):
        # do kontextu sablony propasirujeme promennou "problems"
        # se seznamem dosud nevyresenych hlaseni o chybach
        shop = get_object_or_404(Shop, id=object_id)
        extra_context = {
            'problems': ShopProblem.objects.filter(shop=shop, resolved=False)
        }
        return super(ShopAdmin, self).change_view(request, object_id, extra_context)

Soubor templates/admin/shops/shop/change_form.html:

{% extends "admin/change_form.html" %}
{% load i18n %}
{# CSS trida, ktera zpusobi rozdeleni layoutu na 2 sloupce #}
{% block coltype %}colMS{% endblock %}
{% block content %}
    {{ block.super }} {# puvodni obsah -- formik na leve strane #}
    <div id="content-related">
        <div class="module" id="recent-actions-module">
            {% comment %}
                HTML kod generujici obsah v pravem sloupci, napr.:
            {% endcomment %}
            <ul>
            {% for obj in problems %}
                <li>
                    <a href="/admin/shops/shopproblem/{{ obj.id }}/">
                        {{ obj.created|date }} od {{ obj.user }}
                    </a>
                </li>
            {% endfor %}
            </ul>
        </div>
    </div>
{% endblock %}

No není to paráda?

Úskalí

Však to znáte, v každém vztahu to občas zajiskří a na povrch vyplavou skutečnosti, o kterých jste zatím neměli ani páru.

V případě Django administrace jsem kdysi narazil na neefektivitu v případě ukládání modelu, který měl na stránce hodně inline prvků. Po kliknutí na „Save“ se vygenerovalo 300+ SQL dotazů. Jindy mě zase zamrzelo, že celý django.contrib.admin je příliš pevně propojen s relačními databázemi a v případě použití NoSQL můžete na celý administrátorský komfort zapomenout.

Chce to ale víc času. Pokud se budete stýkat častěji a pochopíte limity toho druhého, budete schopni nepříjemným překvapením předcházet. Je to vztah jako každý jiný.

Co nás čeká příště?

Hrdinný Scuk.cz se pochlubí účinným preparátem na hubení bezobratlých, poodkryje nám jednu ze zavrhovaných praktik (pst!) a dokáže, že i bez orientačního smyslu najde na mapě, co potřebujete. Zůstaňte věrni, snad to dobře dopadne.

Komentáře: 14

Přehled komentářů

Voy Pěkné, ale o čem to je?
EskiMag Re: Pěkné, ale o čem to je?
Martin Malý Re: Pěkné, ale o čem to je?
msgre Re: Pěkné, ale o čem to je?
architekt_ Re: Pěkné, ale o čem to je?
Pitrsonek Tip na knížku o pythonu a Django
Mintaka Re: Tip na knížku o pythonu a Django
msgre Re: Tip na knížku o pythonu a Django
architekt_ Re: Tip na knížku o pythonu a Django
Martin Malý Re: Tip na knížku o pythonu a Django
Pitrsonek Paráda díky
Martin Malý Re: Paráda díky
Pitrsonek Re: Paráda díky
Bruce Re: Paráda díky
Zdroj: https://www.zdrojak.cz/?p=3307