Article View: pl.comp.lang.python
Article #15878Kiedy pajtoniarze nie maja o czym gadac ..
From: Przemek Wiśniewski
Date: Sat, 24 Jun 2006 02:41
Date: Sat, 24 Jun 2006 02:41
174 lines
9866 bytes
9866 bytes
.. to gadaja o uebowych frejuerkach albo o swoich pierwszych razach.
Naszla mnie chec zdradzenia pewnego pomyslu. Otoz ja czesto uzywam
Pythona w.. projektach PHP. Nie ma jednak znaczenia przy czym go uzywam,
ale jak go uzywam. Otoz jest cos takiego jak domain specific language,
ja to sobie tlumacze jako jezyk (badz narzedzie) pozawalajace rozwiazac
pewien specyficzny problem stosujac wysoka abstrakcje. Moze wyszlo maslo
maslane ale mam nadzieje, ze rozumiecie.
Problemem niech bedzie jakas aplikacja skladajaca sie z modelu danych
(baza danych), widokow formularzy, walidatora, kontrolera, itp.. Zamiast
dlubac taka aplikacje recznie w PHP mozna skonstruowac jezyk dzieki
ktoremu bedziemy mogli operowac takimi pojeciami jak wlasnie formularz,
model danych. Napisanie takiej aplikacji w duzej mierze sprowadzi sie do
modelowania programu w naszym jezyku wysokiej abstrakcji i wygenerowaniu
z tego kodu w docelowym, bardziej przyziemnym jezyku.
Gdzie tu python a gdzie php? Python swietnie nadaje sie do stworzenia
takiego pseudo jezyka. Program Scons jest na to swietnym przykladem. Z
kolei PHP to toporne gowno, ktore swietnie sie nadaje do bycia efektem
generacji kodu przez pythona, bron Boze do recznego pisania na wieksza
skale.
Zaczynamy od uscislenia pojec, np: w bazie danych mamy tabelki, ale
poslugiwanie sie relacyjnym modelem danych jest czasami niewygodne.
Lepiej miec obiektowy model danych, wiec wyobrazamy sobie pythonowa
klase Model, ktora ma pewne metody i atrybuty i ktorej mozemy uzyc w
taki sposob:
article = Model(table='ARTICLE')
article.Id(name='article_id', column='ARTICLE_ID')
article.Property(name='title', column='TITLE',
type=StringType(length=100))
article.Property(name='body', column='BODY',
type=StringType(length=1000))
To jest wygodny (przynajmniej dla mnie) sposob opisu takiego modelu
danych. Co mozna z tym dalej zrobic? Mozna z tego wygenerowac pewien kod
PHP, ktorego recznie nie chcielibysmy pisac. Mozna tez wygenerowac
schemat bazy.
Poki co nasz artykul sklada sie z dwoch prostych pol i identyfikatora.
Troche malo i chyba nie warte zachodu dla takiej pierdulki angazowac w
to pythona. Ale mozna pojsc krok do przodu:
author = Model(table='AUTHOR')
author.Id(...)
author.Property(name='first_name', ...)
author.Property(name='last_name', ...)
article.ManyToOne(name='author', column='AUTHOR_ID', model=author)
I tu zaczyna sie ciekawe. Mamy juz prosta referencje - wiele artykulow
do jednego autora. Mozemy sie pokusic o bardziej zlozone referencje -
wedlugo podrecznika do Hibernate. Ja w takim modelu dodatkowo oznaczam
sobie pola, ktore wskazuja na date i uzytkownika, ktory: (1) obiekt
utworzyl, (2) obiekt zmodyfikowal. Te pola moj program wypelnia dla mnie
automatycznie w odpowiednich momentach.
Nie jest teraz wazne co dokladnie z takiego modelu wygenerowac, to sie
okaze kiedy znane beda wszystkie szczegoly.
Idziemy dalej i stwierdzamy, ze uzytkownik naszego programu to jelen i
napewno bedzie popelnial bledy przy wypelnianiu formularzy. Potrzebna
jest walidacja. Przyjmijmy, ze w formularz jest widokiem modelu -
zawiera pola do edycji wlasciwosci tego modelu. Kazde takie pole bedzie
mialo pewne reguly walidacji.
articleRules = Rules(model=article)
articleRules['title'] = MaxLengthRule(100, error_msg='error.max-length')
articleRules['body'] = MaxLengthRule(1000, error_msg='error.max-length')
articleRules['date'] = MaskRule('####-##-##', error_msg='error.date')
Wiec mamy proste reguly walidacji. Oddzielilem je od definicji modelu
zeby nie bylo balaganu. To co otrzymalismy to zbior regul dla
konkretnego modelu. Z tego mozna wygenerowac calkiem sprytny walidator
formularzy dzialajacy na dwoch warstwach: w przegladarce jako javascript
- sprawdza uzytkownika na biezaco wpisuje i nie pozwoli wyslac smieci
oraz na serwerze jako php - ostatecznie sprawdzi to co uzytkownik wyslal.
Program generujacy moze przed generowaniem kodu sprawdzic czy
zdefiniowalismy reguly dla wszystkich pol, czy reguly maksymalnej
dlugosci nie przekracaja faktycznej dlugosci pola itp. Dobrze jest
zaimplementowac takie sprawdzanie zeby latwiej bylo utrzymac porzadek.
W tych regulach walidacji pojawil sie tez komunikat bledu, parameter
error_msg to identyfikator jakiegos komunikatu. Nasz program generujacy
moze sprawdzic czy w plikach z wersjami jezykowymi istnieje taki
komunikat. Pedanci mogliby chciec wiedziec jakie komunikaty sa
nieuzywane, to tez da sie latwo sprawdzic.
Dobra. Jak mamy walidacje to mozemy zajac sie formularzami.
editArticleForm = Form(parameters={'article': article,
'authors': ComboBoxModel(author)})
editArticleForm.add(TextBox(value='article/title',
label='article.title.label'))
editArticleForm.add(TextBox(value='article/body',
label='article.body.label'))
editArticleForm.add(ComboBox(value='article/author',
model='authors',
label='article.author.label'))
btnBox = editArticleForm.add(ButtonBox())
btnBox.add(SubmitButton(action='save-article.php', label='button.save'))
btnBox.add(LinkButton(href='/strona-glowna.php', label='button.cancel'))
Formularz przyjmuje od kontrolera dwa parametry - artykul i dane dla
pola wyboru autora (ComboBox).
Formularz zawiera jakies pola edycji - pola tekstowe, pola wyboru. Dla
tak prostych rzeczy taka zabawa moze faktycznie nie miec sensu, ale..
dla bardziej skomplikowanych pol owszem. Wyobrazmy sobie pole wyboru
daty - pole tekstowe i obok ikonka do popupa, latwo zdefiniowac w
pythonie klase, ktora podczas generowania formularza wygeneruje takie
pole. Moze cos bardziej skomplikowanego - pole wyboru obrazka - mamy
miniaturke obrazka i obok przycisk, po kliknieci otwiera sie popup z
lista obrazkow do wyboru, po wybraniu miniaturka sie aktualizuje - to
tez latwo zrobic. A teraz cos bardzo skomplikowanego - widok listy.
Nasz widok listy czyli ListView korzysta z modelu (ListModel). ListModel
ma metody zwracajace ilosc wszystkich elementow, ilosc elementow
pokazywanych na stronie no i metode zwracajaca te elementy. ListView ma
kolumny, kazda taka kolumna ma swoja nazwe oraz kontrolke
(pole/field/widget) odpowiadajaca za zawartosc komorki w rzedzie. Majac
liste elementow najpierw wyrenderujemy naglowek listy, a potem w rzedach
wyrenderujemy komorki za pomoca tych kontrolek. Dzieki temu mozemy miec
liste z checkboxami, z polami edycji itp. Jak z takiej definicji listy
wygenrowac konkretny kod zostawiam zainteresowanym :) Nie jest to wcale
trudne jak sie dobrze przemysli, ja nie opisuje gotowego roziazania.
ListView powinno tez zawierac pager czyli przyciski do scrollowania.
Na koniec jeszcze przyciski. (1) Przyciski siedza w ButtonBoxie, ktory
moze miec inny uklad niz reszta formularza, wiec wcale nie jest
powiedziane, ze definiujac widok formularza w ten sposob nie mamy wplywu
na jego koncowy wyglad. (2) Przyciski wywoluja jakies akcji, opcjonalnie
wywoluja walidacje, wszystko zalezy od przycisku.
I tak dalej. Wiecej mi sie pisac nie chce. Dodam tylko, ze to co
przedstawilem nie jest tak ekscytujace jak to, ze za pomoca takiego
specyficznego jezyka mozna latwo zbudowac aplikacje z tak modnych
ostatnio pojec jak na przyklad porlety (te z jboss portala), czy tez
workflowy (czyli przerozne procesy - activity based workflow jak
OpenFlow lub zwykly entity based workflow czyli dokument ze statusami).
Uwazam, ze jak ktos siada do problemu to pierwsze co powinien zrobic to
wlasnie ustalic sobie zbior takich pojec jak wyzej przedstawilem. Majac
dobre definicje mozna sie pokusic o stworzenie sobie jakiegos mniej lub
bardziej zaawansowanego narzedzia do generowania kodu.
Python oczywiscie ma swoje ograniczenia skladniowe i nie mozna od niego
wymagac Bog wie czego. Majac taki kod zapisany w pythonie nie da sie
przy zwracaniu bledu okreslic dokladnej linii w pliku.
Mozna sie pokusic o uzycie XMLa zamiast kodu w pytohnie. Wtedy daloby
sie zglaszac problemy z dokladna lokalizacja, ale zrobienie takiego
generatora jest trudniejsze. Pojawiaja sie wtedy tez inne problemy -
przypuscmy, ze z definicji modeli danych generujemy schemat bazy. Po
zmianie nazwy jakiegos pola chcemy zeby program zmienil rowniez nazwe w
bazie. Stosujac zwykle pliki XMLowe nie da sie tego sensownie zrobic.
Tutaj fajnie byloby miec GUI - graficzny program odnotowalby zmiane
nazwy i przy pierwszej okazji zmienilby tez w bazie danych.
Kolejny problem z tym wszystkim jest taki, ze nie da sie zastosowac tej
techniki kiedy na poczatku nie zna sie dokladnie problemu, bo kiedy sie
go poznaje w trakcie pracy to modyfikacja jezyka moze byc bardzo bolesna.
Mam nadzieje, ze to co napisalem jest w miare spojne i zrozumiale.
Pozdrawiam ze wspanialego miasta Krakowa,
Przemek Wisniewski
Technical Details
Message-ID:
<e7i8om$8gu$1@inews.gazeta.pl>Path:
mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!news.gazeta.pl!not-for-mailShow Full Headers
Complete Article Headers
Path: mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!news.gazeta.pl!not-for-mail From: Przemek Wiśniewski <przemek.wisniewski@gazeta.pl> Newsgroups: pl.comp.lang.python Subject: Kiedy pajtoniarze nie maja o czym gadac .. Date: Sat, 24 Jun 2006 04:41:28 +0200 Organization: "Portal Gazeta.pl -> http://www.gazeta.pl" Lines: 174 Message-ID: <e7i8om$8gu$1@inews.gazeta.pl> NNTP-Posting-Host: chello087206234164.chello.pl Mime-Version: 1.0 X-Trace: inews.gazeta.pl 1151116887 8734 87.206.234.164 (24 Jun 2006 02:41:27 GMT) X-Complaints-To: usenet@agora.pl NNTP-Posting-Date: Sat, 24 Jun 2006 02:41:27 +0000 (UTC) X-User: przemek.wisniewski User-Agent: Thunderbird 1.5.0.4 (Windows/20060516) Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit