Podívejte se na všechny díly seriálu nebo na zdrojáky příkladu.
Před lety jsem v Pylons 1 napsal malou aplikaci, která byla vpodstatě CRUD s nějakou přidanou hodnotou. A protože nejlépe se něco naučíme tréningem, rozhodl jsem se napsat podobnou aplikaci s novými technologiemi, konkrétně frameworkem Pyramid. Ve své aplikaci jsem si sám napsal vrstvu, která zobrazovala a editovala ORM objekty. Byla jednoduchá, a umožňovala deklarativně posat, jak se má zobrazit objekt v tabulce, v detailním pohledu, jak se má editovat, a jak zobrazit případnou hierarchii objektů. Nepřipomíná vám to něco?
Kde už jsme takový seznam vlastností viděli? Aha, formalchemy. Což nám spolu s SQLalchemy dává název tohoto článku. Pokud byste chtěli navrhnout, ať použiju formalchemy_pyramid, tak vězte, že o něm vím, ale chtěl jsem zůstat na trochu nižší úrovni. Jdeme na to.
Základ je jednoduchý. Máme ORM třídu, nebo objekt. Pak máme formalchemy.Grid a formalchemy.FieldSet. formalchemy.Grid dostane pole objektů, a z nich umí vyrenredovat tabulku. formalchemy.FieldSet dostane jeden objekt a vyrenderuje formulář, pro editaci. Všechno najdete na https://github.com/petrblahos/faapp-sample. Dnes začneme step01. Nejprve, jak to rozchodit. Nejlépe bude, když si uděláte virtualenv. Unixáci jsou kompetentní, takže to ukážu jen pro Windows:
virtualenv FAAPP cd FAAPP scripts\activate git clone https://github.com/petrblahos/faapp-sample cd faapp-sample\step01 python setup.py develop rem ted si dejte kafe ..\..\scripts\initialize_faapp_db development.ini pserve --reload development.iniPokud všechno vyšlo, tak stačí nasměrovat Váš prohlížeč na localhost:6543
Dost váhám ohledně toho, jak moc jít do hloubky. Předně, pochybuju, že znáte Pyramid. Na to si ale projděte pár tutorialů, a potom Narrative Documentation. Co se týče SQLAlchemy, tak tam jsem ochoten napsat ještě míň. Začnu psát, a když se někdo ozve v komentářích, tak můžu příště něco doplnit. Jak se orientovat v této aplikaci? Nejprve model. V model/__init__.py je jenom konfigurace toho, abychom měli v request.db naši databázovou Session. Zajímá nás model/meta.py, kdežto si nadefinujeme ORM modely. Jak vidíte, mám tak 2 jednoduché tabulky s one-to-many relationship mezi nimi. Tady je snad jen dobré uvědomit si, že tyto „modely“ nejsou „bindnuty“ k žádné databázi. Dotazy spouštíme v rámci Session. Hmm, asi bych neměl psát bindnuty, ale boundnuty, že? No a v této fázi chci: Napíšu definici tabulky do model/meta.py, a to mi stačí pro vytvoření základního interface pro prohlížení, zadávání a editaci dat.
Jak nejlépe napsat česky routes? Jejich definice je v __init__.py. Máme 4:
Tady chceme vypsat všechny ORM modely, abychom pak s nimi mohli dělat něco dalšího. Použijeme jednoduchou introspekci – vybereme z modulu model/meta.py všechny potomky Base.
@view_config(route_name='top', renderer='/top.mako') def top(request): models = [] for (name, ent) in meta.__dict__.iteritems(): if name.startswith("_"): continue if "Base"==name: continue try: if issubclass(ent, meta.Base): models.append(name) except: pass return { 'models': models, }
List čeká jméno modelu jako parametr. Uděláme z něho třídu, a předhodíme do Grid, což je něco z formalchemy, co umí vyrenderovat tabulku. Všimněte si, že Grid chci readonly. Můžete si zkusit, co udělá, když nebude readonly, ale předem upozorňuju, že Vaše změny nemá kdo uložit.
@view_config(route_name="list", renderer="/list.mako") def list(request): model = meta.__dict__.get(request.matchdict["model"]) grid = Grid(model, request.db.query(model).all(), ) grid.configure(readonly=True) return { "q": request.db.query(model).all(), "grid": grid, }
Proč dělám ten dotaz ještě jednou? Mrkněte na šablonu templates/list.mako:
... <ul> % for i in q: <li> <a href="${ request.route_url("edit", model=request.matchdict["model"], id=i.id) }"> ${ i.id }</a> ${ i } % endfor </ul> <a href="${ request.route_url("new", model=request.matchdict["model"]) }"> ${ _("New") }</a> <hr> <table> ${ grid.render() |n} </table>
Ano. Kromě toho, že dělám grid.render() si ještě vypíšu jednotlivé záznamy ručně, protože zatím neumím grid donutit, aby mi udělala nějaké odkazy na editaci. Takže když chci editovat, použiju zatím ty ručně vytvořené odkazy.
V definici rout edit a new vidíte, že se liší jenom tím, že new nemá „ocas“ s id. Handler těch rout necháme dohromady. Pokud v request.matchdict najdeme to id, tak je to edit, jinak je to new. V každém případě najdeme model, pokud máme id, tak nejdeme i databázový záznam a uděláme s ním formalchemy.FieldSet. Ten si pak necháme v šabloně vykreslit, tentokrát už ne read-only.
@view_config(route_name="edit", request_method="GET", renderer="/edit.mako") @view_config(route_name="new", request_method="GET", renderer="/edit.mako") def edit(request): model = meta.__dict__.get(request.matchdict["model"]) if "id" in request.matchdict: obj = request.db.query(model).filter(model.id==request.matchdict["id"]).first() fs = FieldSet(obj) else: fs = FieldSet(model, session=request.db) return { 'fs': fs }
Jak se teda dělá ten save? Visí na úplně stejné routě – edit nebo new. Pyramid umožňuje při konfiguraci handleru nastavit nějaké predikáty, takže ten náš předchozí edit se vyvolá, když přijde GET. Když přijde POST, tak se vyvolá tento save:
@view_config(route_name="edit", request_method="POST", renderer="/edit.mako") @view_config(route_name="new", request_method="POST", renderer="/edit.mako") def saveedit(request): model = meta.__dict__.get(request.matchdict["model"]) if "id" in request.matchdict: obj = request.db.query(model).filter(model.id==request.matchdict["id"]).first() fs = FieldSet(obj, request=request) else: fs = FieldSet(model, session=request.db, request=request) if fs.validate(): request.db.add(fs.model) fs.sync() else: return { "fs": fs } return HTTPFound(location=request.route_url("list", model=request.matchdict["model"]))Vidíte, že jsem do něj přidal validaci, a jestliže prošla, tak prostě uložím a přesměruju na list. Commit se provede automaticky při skončení handleru díky pyramid_tm – Pyramid Transaction Manager.
Příště budeme trošku zjednodušovat, a začneme upravovat obsah toho, co formalchemy vyrenderuje. Slovo závěrem: Pokud snad někoho to, co píšu zajímá, a chtěli byste něco víc rozvést, tak napište do komentáře.
Jmenuju se Petr Blahoš. Programuju něco přes 20 let. Tady se snažím psát hlavně o Pythonu, webovém frameworku Pyramid, a občas i o něčem úplně jiném.
Přečteno 19 226×
Přečteno 11 853×
Přečteno 9 346×
Přečteno 8 804×
Přečteno 8 594×