🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: pl.comp.lang.python
Article #15878

Kiedy pajtoniarze nie maja o czym gadac ..

#15878
From: Przemek Wiśniewski
Date: Sat, 24 Jun 2006 02:41
174 lines
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-mail

Show 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