Download - Google Application Engine Templates for HTML University of …csev/courses/appengine/handouts/... · 2008. 11. 8. · Google Application Engine Templates for HTML University of Michigan

Transcript
Page 1: Google Application Engine Templates for HTML University of …csev/courses/appengine/handouts/... · 2008. 11. 8. · Google Application Engine Templates for HTML University of Michigan

GoogleApplicationEngineTemplatesforHTML

UniversityofMichigan–InformaticsCharlesSeverance

WhileitispossibletogeneratealloftheHMTLofyourapplicationfromwithinstringsinthePython,thisisgenerallyapoorwaytoauthorHTML.Inparticular,itmeansthateverytimeyouwanttochangeabitofthegeneratedHTMLyouneedtodigthroughtheprogram,findthestringsandthenchangetheHTMLcode: formstring = """<form method="post" action="/" enctype="multipart/form-data"> Zap Data: <input type="text" name="zap"><br> Zot Data: <input type="text" name="zot"><br> File Data: <input type="file" name="filedat"><br> <input type="submit"> </form>""" Atthesametime,ourwebapplicationneedstohavesomepartsofthewebpagesbegenerateddynamicallyaspartofthecodeofthewebapplication–eitherbasedontheuser’sinputorbasedonsomeinformationretrievedfromthedatabase.Thecompromiseforthisistointroducethenotionofa“template”.AtemplateisafilethatcontainsmostlyHTML–buttherearespeciallymarkedareasofthetemplatethatisreplacedbydatahandedtothetemplatefromthePythoncodewhenthetemplateistoberendered.Therearemanydifferenttemplatinglanguagesandsyntaxes.ThedefaulttemplatesyntaxusedbytheGoogleApplicationEngineisfromtheDjangoproject.TemplateSyntaxThetemplatesyntaxinGoogleApplicationaugmentstheHTMLusingcurlybracestoidentifytemplatedirectives.Thetemplateforourdumperprogram:

<form method="post" action="/" enctype="multipart/form-data"> Zap Data: <input type="text" name="zap"><br> Zot Data: <input type="text" name="zot"><br> File Data: <input type="file" name="filedat"><br> <input type="submit"> </form> <pre> Request Data: {{ dat }} </pre>

Page 2: Google Application Engine Templates for HTML University of …csev/courses/appengine/handouts/... · 2008. 11. 8. · Google Application Engine Templates for HTML University of Michigan

TheareainthetemplatethatwillbereplacedwithdatafromPythonistheareaindoublecurlybraces.Inbetweenthedoublecurlybraces”dat”isakeythatisusedtodeterminewhichpieceofdatafromthePythoncodetoputintothetemplatetoreplacethe{{dat}}inthetemplate.Byconvention,weputthetemplatesintoadirectorynamed“templates”.ThiswaywecaneasilykeepHTMLtemplatesseparatefromthePythoncode.

Namingthefolder“templates”isnotarule–itisaconvention.Itisagoodconventionbecauseitmeansthatotherdeveloperswillimmediatelyknowwheretofindthetemplates.UsingtheTemplatesfromPythonTodisplaythetemplateinPythonweaddcodeto“render”thetemplateandthenprinttheoutputoftherenderprocesstotheHTTPresponse.Hereissomesimplecodewhichaccomplishesrenderstheindex.htmfile: import os from google.appengine.ext.webapp import template temp = os.path.join(os.path.dirname(__file__), 'templates/index.htm') outstr = template.render(temp, {'dat': ‘Hello There’}) self.response.out.write(outstr)

Thefirstlineisabitcomplexlooking–itcallsthePythonoperatingsystemlibrary(os)tolookupthelocationoftheindex.htmfileinthefoldernamedtemplates.Therealworkisdoneinthetemplate.render()line.Thistakestwoparameters–thefirstisthelocationofthetemplatefileandthesecondisaPythondictionaryobjectwhichcontainsthestringstobeplacedinthetemplatewherethe{{dat}}entriesarefound.Theresultsofthesubstitutionofthevariablesintothetemplatearereturnedasastringinthevariableoutstr.Thenthestringthat(outstr)isreturnedfromtherenderoperationisthenwrittenoutastheHTTPresponse.Thetextreturnedfromtherenderprocesswilllookasfollows:

<form method="post" action="/"

Page 3: Google Application Engine Templates for HTML University of …csev/courses/appengine/handouts/... · 2008. 11. 8. · Google Application Engine Templates for HTML University of Michigan

enctype="multipart/form-data"> Zap Data: <input type="text" name="zap"><br> Zot Data: <input type="text" name="zot"><br> File Data: <input type="file" name="filedat"><br> <input type="submit"> </form> <pre> Request Data: Hello There </pre>

ThedatafromthePythondictionaryisnowsubstitutedintothetemplateasitisrendered.Thefollowingisadiagramoftheprocess.

Theprocessissimple–renderenginelooksforthe“hotspots”inthetemplateandwhenitfindsaspotwhereasubstitutionisrequired,therendererlooksinthePythondictionarytofindthereplacementtext.Thetemplatelanguageisactuallyquitesophisticated–wewilllookatthecapabilitiesofthetemplatelanguageinmoredetaillater.OverallFlowTogobacktoourexampleandputitallincontext,wecanseehowourdumperprogramhasevolved: def dumper(self): prestr = ‘ ‘ for key in self.request.params.keys(): value = self.request.get(key) if len(value) < 100: prestr = prestr + key+':'+value+'\n' else: prestr = prestr + key+':'+str(len(value))+' (bytes long)\n'

Page 4: Google Application Engine Templates for HTML University of …csev/courses/appengine/handouts/... · 2008. 11. 8. · Google Application Engine Templates for HTML University of Michigan

temp = os.path.join(os.path.dirname(__file__), 'templates/index.htm’) outstr = template.render(temp, {'dat': prestr}) self.response.out.write(outstr)

Thefirsthalfofthedumper()methodloopsthroughtherequestparametersandinsteadofwritingtheparameterinformationtotheresponse,theinformationisappendedintothepredatstringincludinglabelinginformation,theparametersthemselvesandnewlines(“\n”).Thenthepredatstringisthenpassedintothetemplate.render()asinadictionaryunderthekey“dat”.TherenderenginethenmergesthedatafromthePythoncodewiththeHTMLtemplateandreturnstheresultingHTMLthatiswrittenoutastheresponse.AbstractionandSeparationofConcerns–“ModelViewController”Forcomplexwebprogramsitisveryimportanttobeorganized–andforeachareaoffunctionalitytohaveitsplace.Followingcommonlyunderstoodpatternshelpsuskeeptrackofthebitsoftheprogramasitbecomesincreasinglycomplex.Oneofthemostcommonprogrammingpatternsinweb‐basedapplicationsiscalled“Model‐View‐Controller”orMVCforshort.ManywebframeworkssuchasRubyonRailsandSpringMVCfollowtheModel‐View‐Controllerpattern.TheMVCpatternbreaksthecodeinawebapplicationintothreebasicareas:

• Controller‐Thecodethatdoesthethinkinganddecisionmaking

• View‐TheHTML,CSS,etc.whichmakesupthelookandfeeloftheapplication

• Model‐Thepersistentdatathatwekeepinthedatastore

InaGoogleApplicationEngineprogram,theindex.pyisanexampleofControllercodeandtheHTMLinthetemplatesisanexampleofaView.WewillencountertheModelinalaterchapterandthenwewillrevisittheMVCpatternandexploreitinmoredetail.MultipleTemplatesRealapplicationshavemanydifferentviews(screens)sonowwewillexplorehowtosupportmultipletemplatesinourapplication.Wewillbuildasimpleapplicationwhichhasthreescreensandnavigationbetweenthescreens.

Page 5: Google Application Engine Templates for HTML University of …csev/courses/appengine/handouts/... · 2008. 11. 8. · Google Application Engine Templates for HTML University of Michigan
Page 6: Google Application Engine Templates for HTML University of …csev/courses/appengine/handouts/... · 2008. 11. 8. · Google Application Engine Templates for HTML University of Michigan

ThisapplicationusesCSStomakethenavigationappearatthetopofthescreen.Lookingattheapplicationlayout,weseeonetemplateforeachHTMLpage.

Thetemplatesfoldercontainsthetemplatesthatareusedtocreateoutputdynamically(rendered)inthePythoncode(index.py)–thestaticfoldercontainsfilesthatdonotchange–theymaybeCSS,Javascript,orimagefiles.StaticFilesWeindicatethatthestaticfolderholdsthese“un‐changing”filesbyaddinganentrytotheapp.yamlfile:

application: ae-05-templates version: 1 runtime: python api_version: 1 handlers: - url: /static static_dir: static - url: /.* script: index.py

WeaddanewhandlerentrywiththespecialindicatortomapURLswhichstartwith/statictothespecialstaticfolder.Andweindicatethatthematerialinthestaticfolderisnon‐dynamic.TheorderoftheURLentriesinthehandlersectionisimportant–itfirstcheckstoseeifanincomingURLstartswith“/static”–ifthereisamatch–thecontentis

Page 7: Google Application Engine Templates for HTML University of …csev/courses/appengine/handouts/... · 2008. 11. 8. · Google Application Engine Templates for HTML University of Michigan

servedfromthestaticfolder.Ifthereisnomatch,the“catch‐all”URL(/.*)routesallURLstotheindex.pyscript.Theconventionistonamethefolder“static”.Youtechnicallycouldnamethefolderandpath(/static)anythingyoulike.Thestatic_dirdirective(likethescriptdirective)isadirectiveandcannotbechanged.Butthesimplethingistoalwaysfollowthepatternandnamethefolderandpath“static”.Theadvantageofplacingfilesinastaticfolderisthatitdoesnotuseyourprogram(index.py)forservingthesefiles.SinceyoumaybepayingfortheCPUusageoftheGoogleservers–avoidingCPUusageonservingstaticcontentisagoodidea.Thestaticcontentstillcountsagainstyourdatatransferred–itjustdoesnotincurCPUcosts.Evenmoreimportantly,whenyouindicatethatfilesarestatic,itallowsGoogletodistributethesefilestomanydifferentserversgeographicallyandleavethefilesthere.ThismeansthatretrievingthesefilesfromdifferentcontinentsmaybeusingGoogleserversclosesttotheuserofyourapplication.Thismeansthatyourapplicationcanscaletofarmoreusersefficiently.ReferencingStaticFilesOnceyouputafileinthestaticfolder,yousimplyreferencethosefileswithabsolutepathswhichstartwith/staticasfollows:<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>App Engine - HTML</title> <link href="/static/glike.css" rel="stylesheet" type="text/css" /> </head> <body> <div id="header">

WhentheAppEngineseestheURL,itroutesittooneofitsmanydistributedcopiesofthefileandservesupthecontent.GeneralizingTemplateLookupwithMultipleTemplatesIntheaboveexample(ae‐04‐template)therewasonlyonetemplatesowehard‐codedthenameofthetemplatewhenwewantedtodotherenderoperation.Sometimesyouareinahandlerthatknowstheexactnameofthetemplatesoitcanfollowthatpattern.Othertimes,youwanttohaveabunchoftemplatesandservethemupwithpathssuchas:

http://localhost:8080/index.htm http://localhost:8080/topics.htm http://localhost:8080/sites.htm

Page 8: Google Application Engine Templates for HTML University of …csev/courses/appengine/handouts/... · 2008. 11. 8. · Google Application Engine Templates for HTML University of Michigan

Andwedonotwanttohavetowritespecialcodetolookupeachtemplateseparately.Wecancreategeneralpurposecodetolookupatemplatebasedontheincomingpathoftherequest–andifwefindamatchingtemplate,weusethattemplateandotherwiseweusetheindex.htmtemplate.Hereisthecontrollercodetoaccomplishthis:class MainHandler(webapp.RequestHandler): def get(self): path = self.request.path try: temp = os.path.join(os.path.dirname(__file__), 'templates' + path) outstr = template.render(temp, { }) self.response.out.write(outstr) except: temp = os.path.join(os.path.dirname(__file__), 'templates/index.htm') outstr = template.render(temp, { }) self.response.out.write(outstr)

Wefirstpullinthepathfromself.request.pathandappendthepathtotemplates–wetrytoperformarenderusingthisfile–andifitfails,wegototheexcept:processingandrenderusingtheindex.htmtemplate.Theself.request.pathvariableshowsthepathwithintheapplicationthatisbeingrequested.Inthelogoutputbelow,youcanseeeachpathbeingrequestedusingaGETrequest.

Youcanseethebrowserrequestingapathlike“/sites.htm”whichrendersfromthetemplateandthenwhenthebrowserseesthereferencetothe“/static/glike.css”the

Page 9: Google Application Engine Templates for HTML University of …csev/courses/appengine/handouts/... · 2008. 11. 8. · Google Application Engine Templates for HTML University of Michigan

browserthendoesanotherGETrequesttoretrievetheCSSwhichisstoredinthestaticfolder.Theself.request.pathstartswithaslash(/)sowhenitisappendedto“templates”thepathwehandtotherenderenginelooksliketemplates/sites.htm Whichisexactlywherewehavestoredourtemplate.ExtendingBaseTemplatesWehaveonlybeguntoscratchthesurfaceofthecapabilitiesofthetemplatinglanguage.Oncewehavesuccessfullyseparatedourviewsfromthecontroller,wecanstartlookingatwaystomanageourviewsmoreeffectively.IfyoulookattheexampleHTMLfilesusedastemplatesinthisapplication,youwillfindthatthefilesarenearlyidenticalexceptforafewsmalldifferencesbetweeneachfile.Mostofthecontentofthefileisidenticalandcopiedbetweenfiles.<head> <title>App Engine - HTML</title> <link href="/static/glike.css" rel="stylesheet" type="text/css" /> </head> <body> <div id="header"> <h1><a href="index.htm" class="selected"> App Engine</a></h1> <ul class="toolbar"> <li><a href="sites.htm">Sites</a></li> <li><a href="topics.htm" class="selected">Topics</a></li> </ul> </div> <div id="bodycontent"> <h1>Application Engine: Topics</h1> <ul> <li>Python Basics</li> <li>Python Functions</li> <li>Python Python Objects</li> <li>Hello World</li> <li>The WebApp Framework</li> <li>Using Templates</li> </ul> </div> </body> </html>

Theonlythingsthatchangebetweenthefilesiswhichlinkisselected(i.e.class=”selected”)andtheinformationinthe“bodycontent”div.Allthematerialinthe<head>areaandnearlyallmaterialintheheaderdivareidenticalbetweenfiles.

Page 10: Google Application Engine Templates for HTML University of …csev/courses/appengine/handouts/... · 2008. 11. 8. · Google Application Engine Templates for HTML University of Michigan

Wecauseasignificantmaintenanceproblemwhenwerepeatthistextinmany(perhapshundreds)filesinourapplication.Whenwewanttomakeachangetothiscommoncontent–wehavetoeditallthefilesandmakethechange–thisbecomestediousanderrorprone.Italsomeansthatwehavetotesteachscreenseparatelytomakesureitisupdatedandworkingproperly.Tosolvethis,wecreateaspecialtemplatethatcontainsthecommonmaterialforeachpage.Thenthepagefilesonlyincludethematerialthatisdifferent.Hereisasamplepagefilethatismakinguseofthebasetemplate.Hereisthenewindex.htmtemplatefile:

{% extends "_base.htm" %} {% block bodycontent %} <h1>Application Engine: About</h1> <p> Welcome to the site dedicated to learning the Google Application Engine. We hope you find www.appenginelearn.com useful. </p> {% endblock %}

Thetemplatinglanguageusescurlybracestoindicateourcommandstotherenderengine.Thefirstlineofsaysthispagestartswiththetextcontainedinthefile“_base.htm”.Wearestartingwith_base.htmandextendingit.Thesecondlinesays,“whenyoufindanareamarkedasthe“bodycontentblock”inthe_base.htmfile–replacethatblockwiththetextinbetweentheblockandendblocktemplatecommands.The_base.htmfileisplacedinthetemplatedirectoryalongwithalloftherestofthetemplatefiles:

Thecontentsofthe_base.htmfilearethecommontextwewanttoputintoeachpageplusanindicationofwherethebodycontentistobeplaced:<head> <title>App Engine - HTML</title>

Page 11: Google Application Engine Templates for HTML University of …csev/courses/appengine/handouts/... · 2008. 11. 8. · Google Application Engine Templates for HTML University of Michigan

<link href="/static/glike.css" rel="stylesheet" type="text/css" /> </head> <body> <div id="header"> <h1><a href="index.htm" class="selected"> App Engine</a></h1> <ul class="toolbar"> <li><a href="sites.htm">Sites</a></li> <li><a href="topics.htm" >Topics</a></li> </ul> </div> <div id="bodycontent"> {% block bodycontent %} Replace this {% endblock %} </div> </body> </html>

Weincludeatemplateenginedirectiveinthebasetemplatetoindicatethebeginningandendoftheblockthatwillbereplacedbyeachtemplatethatuses(extends)_base.htm.Thetext“Replacethis”willnotappearintheresultingHTMLaftertherenderhasbeencompleted.

WedonotneedtomakeanychangetotheControllercode–theuseofabasetemplateissomethingthatiscompletelyhandledinthetemplate.render()call.ConditionalHTMLusingifequalOutapplicationhasseveralpages–andwhilewehavemovedmostoftherepeatedtextintoabasefile,thereisoneareainthe_base.htmthatneedstochangebetweenfiles.Ifwelookatthepages,weseethataswemovebetweenpages,wewanttohavethenavigationlinkscoloreddifferentlytoindicatewhichpagewearecurrentlylookingat.

Page 12: Google Application Engine Templates for HTML University of …csev/courses/appengine/handouts/... · 2008. 11. 8. · Google Application Engine Templates for HTML University of Michigan

WemakethischangebyusingtheselectedclassinthegeneratedHTML.Forexampleonthetopics.htmfileweneedthe“Topics”linktobeindicatedas“selected”: <ul class="toolbar"> <li><a href="sites.htm">Sites</a></li> <li><a href="topics.htm" class="selected">Topics</a></li> </ul>

Weneedtogeneratethistextdifferentlyoneachpageandthegeneratedtextdependsonthefilewearerendering.Thepathindicateswhichpageweareon–sowhenweareontheTopicspage,thepathis“/topics.htm”.Wemakeasmallchangetothecontrollertopassinthecurrentpathintotherenderprocessasfollows:def get(self): path = self.request.path try: temp = os.path.join(os.path.dirname(__file__), 'templates' + path) outstr = template.render(temp, { 'path': path }) self.response.out.write(outstr) except: temp = os.path.join(os.path.dirname(__file__), 'templates/index.htm') outstr = template.render(temp, { 'path': path }) self.response.out.write(outstr)

Withthischange,thetemplatehasaccesstothecurrentpathfortherequest.Wethenmakethefollowingchangetothetemplate: <ul class="toolbar"> <li><a href="sites.htm" {% ifequal path '/sites.htm' %} class="selected" {% endifequal %} >Sites</a></li> <li><a href="topics.htm" {% ifequal path '/topics.htm' %} class="selected"

Page 13: Google Application Engine Templates for HTML University of …csev/courses/appengine/handouts/... · 2008. 11. 8. · Google Application Engine Templates for HTML University of Michigan

{% endifequal %} >Topics</a></li> </ul>

Thisinitiallylooksabitcomplicated.Atahighlevel,allitisdoingisaddingthetextclass=”selected”totheanchor(</a>)tagwhenthecurrentpathmatches“/topic.htm”or“/sites.htm”respectively.Wearetakingadvantageofthefactthatwhitespaceandend‐linesdonotmatterinHTML.Thegeneratedcodewilllookoneofthefollowingtwoways: <li><a href="topics.htm" class="selected" >Topics</a></li>

or <li><a href="topics.htm" >Topics</a></li>

Whileitlooksalittlechoppy–itisvalidHTMLandourclass=”selected”appearswhenappropriate.Lookingatthecodeinthetemplate,wecanexaminetheifequaltemplatedirective: {% ifequal path '/topics.htm' %} class="selected" {% endifequal %} Theifequaldirectivecomparesthecontentsofthepathvariablewiththestring“/topics.htm”andconditionallyincludestheclass=”select”inthegeneratedoutput.ThecombinationofthetwoifequaldirectivesmeansthatthelinksgiveustheproperlygeneratednavigationHTMLbasedonwhichpageisbeinggenerated.Thisisquitenicebecausenowtheentirenavigationcanbeincludedinthe_base.htmfile,makingthepagetemplatesverycleanandsimple:

{% extends "_base.htm" %} {% block bodycontent %} <h1>Application Engine: About</h1> <p> Welcome to the site dedicated to learning the Google Application Engine. We hope you find www.appenginelearn.com useful. </p> {% endblock %}

Page 14: Google Application Engine Templates for HTML University of …csev/courses/appengine/handouts/... · 2008. 11. 8. · Google Application Engine Templates for HTML University of Michigan

Thisapproachmakesitverysimpletoaddanewpageormakeachangeacrossallpages.Ingeneralwhenwecanavoidrepeatingthesamecodeoverandover,ourcodeiseasiertomaintainandmodify.MoreonTemplatesThisonlyscratchedthesurfaceofthetemplatedirectives.TheGoogleTemplateEnginecomesfromtheDjangoproject(www.django.org).Youcanreadmoreaboutthetemplatelanguagefeaturesat:http://docs.djangoproject.com/en/dev/ref/templates/builtins/?from=olddocs

ThismaterialsisCopyrightCreativeCommonsAttribution2.5–[email protected]‐chuck.com