Django JavaScript Integration - AJAX and jQuery

Django JavaScript Integration - AJAX and jQuery

ID:33912288

大小:6.84 MB

页数:324页

时间:2019-03-01

上传者:U-14522
Django JavaScript Integration - AJAX and jQuery_第1页
Django JavaScript Integration - AJAX and jQuery_第2页
Django JavaScript Integration - AJAX and jQuery_第3页
Django JavaScript Integration - AJAX and jQuery_第4页
Django JavaScript Integration - AJAX and jQuery_第5页
资源描述:

《Django JavaScript Integration - AJAX and jQuery》由会员上传分享,免费在线阅读,更多相关内容在学术论文-天天文库

DjangoJavaScriptIntegration:AJAXandjQueryDevelopAJAXapplicationsusingDjangoandjQueryJonathanHaywardBIRMINGHAM-MUMBAI DjangoJavaScriptIntegration:AJAXandjQueryCopyright©2010PacktPublishingAllrightsreserved.Nopartofthisbookmaybereproduced,storedinaretrievalsystem,ortransmittedinanyformorbyanymeans,withoutthepriorwrittenpermissionofthepublisher,exceptinthecaseofbriefquotationsembeddedincriticalarticlesorreviews.Everyefforthasbeenmadeinthepreparationofthisbooktoensuretheaccuracyoftheinformationpresented.However,theinformationcontainedinthisbookissoldwithoutwarranty,eitherexpressorimplied.Neithertheauthor,norPacktPublishing,anditsdealersanddistributorswillbeheldliableforanydamagescausedorallegedtobecauseddirectlyorindirectlybythisbook.PacktPublishinghasendeavoredtoprovidetrademarkinformationaboutallofthecompaniesandproductsmentionedinthisbookbytheappropriateuseofcapitals.However,PacktPublishingcannotguaranteetheaccuracyofthisinformation.Firstpublished:January2011ProductionReference:1291210PublishedbyPacktPublishingLtd.32LincolnRoadOltonBirmingham,B276PA,UK.ISBN978-1-849510-34-9www.packtpub.comCoverImagebyVinayakChittar(vinayak.chittar@gmail.com) CreditsAuthorEditorialTeamLeaderJonathanHaywardAksharaAwareReviewersProjectTeamLeaderJakeKronikaAshwinShettyMichaelSzulProjectCoordinatorAcquisitionEditorJoelGoveyaStevenWildingProofreaderDevelopmentEditorSandraHopperMaitreyaBhakalProductionCoordinatorTechnicalEditorsAparnaBhagatVanjeetD'souzaConradSardinhaCoverWorkAparnaBhagatIndexersHemanginiBariMonicaAjmeraMehta ForewordInthisbook,wewillbeexploringDjangoJavaScriptintegrationandbuildanAjaxapplicationusingjQuery.WewillbuildaWeb2.0intranetemployeedirectory,andwewillaimforasolutionthatisPythonicinmorewaysthanone.Webdevelopmentthatismore"Pythonic"thanjustPythonThisbookisintendedtobeabookabouthowtodowebdevelopmentinthespiritofPython++.Theterm"Python++"asweuseithereisnottheusual"Pythonisgreat,"evenifthatmayalsobeouropinion,butamoredirectanalogytoC++.WhentheeffortwasmadetoimprovetheClanguage,thebasickindofimprovementattemptedwastoexpandandbroadenthecorelanguage.ItisnoaccidentthatStroustrup'sTheC++ProgrammingLanguageisfarlongerthanKerniganandRitchie'sTheCProgrammingLanguage.Thelatterisasmallbookdescribingasmallcorelanguage,whiletheformerisalargebookmadelargebythelargecorelanguageitdescribes.TheanalogyintendedbyPython++issomewhatloosehere,andspecificallydoesnotincludealarge,orevenasmall,expansionofthecorelanguage.Itispossibletotinkerwiththecorelanguage—easy_extendletsyouextendPythontoincludeado-whileloop(wherethetestconditionisfirstevaluatedattheend,notthebeginning)—oraddprimitivesyntaxsoyoucandothingslikeifremote_ipin10.0.0.0:,butthisisalmostbesidethepoint.TherealpossibilitiesforexpandingPythondonotneedtoradicallyexpandthecorelanguage,orchangethecorelanguageatall.Django(http://www.djangoproject.com/)isnotageneralpurposeenhancementtoPython:ifyouareautomatingsystemadministrationtasks,forinstance,youveryprobablyhavenoreasontouseDjango.Butforalimitedproblemdomain,namelycertainkindsofwebdevelopment,DjangoismorePythonicthanPython.Python'scgimoduleisgoodforsomekindsofsmallandsimpletasks,butifyouaregoingtomakeaserious,large-scalewebapplicationwithmanystandardfunctions,usingPythoncorelanguage+Pythonstandardlibrary+DjangoisafundamentallymorePythonicapproachthanjustPythoncorelanguage+Pythonstandardlibraryalone. OnStackOverflow,someoneaskedthequestion,"HaveyouconsideredusingDjangoandfoundgoodreasonsnotto?"Therewerevariousanswers,buttheanswerwiththemost"up"votesbyfarsaid,"Iamanhonestguy,andtheclientwantedtochargebythehour.TherewasnowayDjangowasgoingtomakemeenoughmoney."DjangoitselfisnotthelimittoPython++.Pinax(http://pinaxproject.com/)isbuiltontopofDjangoandoffersa"moreDjangothanDjango"platformtobuildasocialnetwork.Satchmo(http://satchmoproject.com/)isalso"moreDjangothanDjango"foranothernarrowerfocus:e-commercewebshops.AndthereareotherplatformsbuiltonDjango;itistimewellspenttosearchthePythonPackageIndex(http://pypi.python.org/pypi)forDjangotoseewhatisalreadyavailable.Inthistextwewilloftenuse"Django"asashorthandforeitherbasicDjangooranyofthemanygoodtoolsbuiltontopofDjango.Dependingonwhatyouaretryingtodo,itmaybethatthebulkofthePythonworkinDjangoisresolvedsurprisinglyquickly:youcanbuildandbrandaPinaxsocialnetworkbydoinglittlemorethanoverridingtheCSSandaddingimages.ThisbookwilladdressthePythonsideandtrytogiveasolidbasisforprogrammingPythonforDjango,workingwiththetemplates,andsoon,butthatproblemcanoftenbesolvedsocleanlythatmostoftheworkthatremainsisstylingandAjax.DjangoanditstemplatingengineBeforefurtherexploringtechnicaldetails,itwouldbeworthtakingalookattheopinionsandphilosophybehindtheDjangotemplatinglanguage,becauseanunderstandableapproachof,"Oh,it'sageneralpurposeprogramminglanguageveryslightlyadaptedfortemplating,"isarecipeforneedlessfrustrationand"isarecipeforneedlessfrustrationandpain.TheDjangodevelopersthemselvesacknowledgethattheiropinionsinthetemplatinglanguageareonejustopinioninanareawheredifferentpeoplehavedifferentopinions,andyouarewelcometodisagreewiththemifyouwant.Ifyoudon'tlikethetemplatingsystemthatDjangocomeswith,Djangoisdesignedtoletyouuseanother.Butitisworthunderstandingwhatexactlythephilosophybehindthetemplatinglanguageis;evenifthisisnottheonlyphilosophyonecoulduse,itiscarefullythoughtout. TheDjangotemplatinglanguageisintendedtofostertheseparationofpresentationandlogic.Initsdesigndecisions,bothlargeandsmall,theDjango'stemplatingengineisoptimizedprimarilyfordesignerstousefordesigning,ratherthanprogrammerstouseforprogramming,anditslimitationsarealmostascarefullychosenasthefeaturesitprovides.UnlikeASP,JSP,andPHP,itisnotaprogramminglanguageinterspersedwithHTML.Itprovidesenoughpowerforpresentation,isintendednottoprovideenoughpowertodoseriousprogrammingworkwhereitdoesn'tbelong(intheDjangoopinion),andissimpleenoughthatsomenon-programmerscanpickitupinaday.Foraprogrammer,thedifficultyoflearningthetemplatingbasicsiscomparabletothedifficultyofsimpleHTMLorSQL:itissimple,andagoodbiteasiertolearnthanwrappingyourarmsaroundaregularprogramminglanguage.ItislikelythatthereareanumberofDjangoprogrammersouttherewhostartedbyasking,"Whydoesn'tthetemplatinglanguagejustletyoumixPythonandHTML?"andafterplayingwithit,foundthemselvessaying,"Thisisn'twhatIwouldhavecomeupwithmyself,butIreally,reallylikeit."Additionalbenefitsincludeitbeingfast(mostoftheworkisdonebyasingleregularexpressioncall,andthefounderstalkaboutdisablingcachingbecauseitcouldn'tkeepupwiththetemplaterenderingengine'sspeed),secure(itisdesignedsothatitcanbeusedbyuntrusteddesignerswithoutallowingamaliciousdesignertoexecutearbitrarycode),andversatileenoughtogeneratewhatevertextformatyouwant:plaintext,HTML,XML,XHTML,JSON,JavaScript,CSV,ReStructuredText,andsoon.WewillbeusingittogeneratewebpagesandJSON,butDjango'stemplatinglanguageisageneral-purposetexttemplatingsolution.Whatwewilldointhisbook—buildingaWeb2.0intranetemployeephotodirectoryManybookswillteachyouanewtechnologybywalkingthroughasampleproject.Thisbookisnodifferent,butthesampleprojectisnotatoy:itisawalkthroughmakingareal,liveWeb2.0intranetemployeephotodirectorythatyoucancustomizetoyourorganization'sneeds.Thisissomethingthatisbothuseful,andwillgiveusatourofthepotentialfordevelopingAjaxapplicationsusingDjangoontheserversideandjQueryontheclientside.-JonathanHayward AbouttheAuthorJonathanHaywardasachildranked7thinanationwidemathcontest,andlaterprogrammedavideogameonhiscalculator.Heholdsmaster'sdegreesinbridgingmathematicsandcomputerscience(UIUC),andphilosophyandtheology(Cambridge).JonathanhaslivedintheU.S.,Malaysia,France,andEngland,andhasstudiedwelloveradozendialectsandlanguages.Hewearsthehatsofauthor,philosopher,theologian,artist,poet,wayfarer,philologist,inventor,andaskilledwebdeveloperwhoholdsadeepinterestinthehumansideofcomputing.Hehasawebsiteshowcasinghisworksathttp://JonathansCorner.comandcanbereachedviae-mailatjonathan.hayward@pobox.com.Iwouldliketothankmyparents,JohnandLinda,wholovelearningandtaughtmefaith,mybrothers,Matthew,KirkandJoe,myparish,St.InnocentofMoscow,forawealthofsupport.IwouldalsoliketothanktheeditorialteamatPackt:StevenWilding,whohelpedmecomeupwiththebookideainthefirstplace,VedPrakashJha,whohelpedseeittocompletion,andJoelGoyeva,whohelpedmewithinnumerablelogisticsalongtheway.And,ofcourse,thereviewersJakeKronikaandMichaelSzul,whoofferedaninvaluablesharpening.TheDjangolist,django-users@googlegroups.com,isworthitsweightingold.IwouldliketothankDanielRoseman,AlexRobbins,DanHarris,KarenTracey,OlegLokalma,MarkLinsey,JeffGreen,ElijahRutschman,BrianNeal,EuanGoddard,SævarÖfjörð,"Ringemup",BenAtkin,TomEvans,SamLai,andPrestonHolmes.Authorshavetoleavesomebodyoutwhodeservestobementioned;that'sjustpartoftheterritory.ButIwouldliketothankonepersoninparticular:thereader.You'rereallythereasonthebookishere,andyou'vechosentoinvestsomemoneyinabookandsometimeinfascinatingtechnologiesandletmehelpyoualongtheway.Thankyousomuch. AbouttheReviewersJakeKronika,awebdesigneranddeveloperwithoverfifteenyearsofexperience,bringstothisbookastrongbackgroundinfrontenddevelopmentwithJavaScriptandAJAX,aswellasexposuretotheDjangoframework.HavingearnedaBachelorsofSciencedegreeinComputerSciencefromIllinoisWesleyanUniversityin2005,withaminorinBusinessAdministration,JakewentontobecomeSeniorUserInterface(UI)SpecialistforImaginaryLandscape,LLC,asmallwebdevelopmentfirminRavenswood,onthenorthsideofChicago.Inthisrole,thefoundationsofhisstrengthsinCascadingStyleSheets(CSS)andJavaScript(JS)werebuilt,aswellasextensiveuseofPythonandtheDjangoFramework.Fromthere,JakewentontoworkfortheSun-TimesNewsGroup,owneroftheChicagoSun-TimesandnumeroussuburbannewspapersinChicagoland.ItwasinthisrolethathewasinitiallyexposedandrapidlybecameanexpertwiththejQueryframeworkforJS.FollowinganintermediatepositionasTechnologyConsultantwithObjectiveArts,Inc,JakehasworkedasUIPrototyperforJPMorganChasesinceFebruary2010.Since1999,hehasalsooperatedGridlineDesign&Development,asoleproprietorshipforwebdesign,development,andadministration.Iwouldliketothankmywife,Veronica,forherongoingsupport.SheandmytwinchildrenMykaelaandKadenprovideallthejoyIcouldwantinthisworld. MichaelSzulhasdesignedanddevelopedsoftwareapplicationsforFortune500companies,includingAIGandPraxair,since1998.Later,heservedasaseniorsoftwareengineerforthetechnologydivisionofPerformanceMediaGroup,contributingtotheirfastgrowthandsuccess,includingplacementontheInc.5000.Szul'sexpertiseinsocialsoftwaredevelopmentledtoalateralmovewithinthecompanytobecomethedirectorofdevelopmentfortheirtravelsocialnetwork.HeevenbuiltsuccessfulsocialsoftwareforcompaniessuchasAppleVacationsandCondeNaste'sGourmetMagazine.AsapartneratBarbellaDigital,Inc.,hecurrentlydesignsanddevelopsenterprise-levelworkflowsystemsandmobileapplicationsforeducationalinstitutions. www.PacktPub.comSupportfiles,eBooks,discountoffersandmoreYoumightwanttovisitwww.PacktPub.comforsupportfilesanddownloadsrelatedtoyourbook.DidyouknowthatPacktofferseBookversionsofeverybookpublished,withPDFandePubfilesavailable?YoucanupgradetotheeBookversionatwww.PacktPub.comandasaprintbookcustomer,youareentitledtoadiscountontheeBookcopy.Getintouchwithusatservice@packtpub.comformoredetails.Atwww.PacktPub.com,youcanalsoreadacollectionoffreetechnicalarticles,signupforarangeoffreenewslettersandreceiveexclusivediscountsandoffersonPacktbooksandeBooks.http://PacktLib.PacktPub.comDoyouneedinstantsolutionstoyourITquestions?PacktLibisPackt'sonlinedigitalbooklibrary.Here,youcanaccess,readandsearchacrossPackt'sentirelibraryofbooks.WhySubscribe?•FullysearchableacrosseverybookpublishedbyPackt•Copyandpaste,printandbookmarkcontent•OndemandandaccessibleviawebbrowserFreeAccessforPacktaccountholdersIfyouhaveanaccountwithPacktatwww.PacktPub.com,youcanusethistoaccessPacktLibtodayandviewnineentirelyfreebooks.Simplyuseyourlogincredentialsforimmediateaccess. TableofContentsPreface1Chapter1:jQueryandAjaxIntegrationinDjango7AjaxandtheXMLHttpRequestobject8Humanspeech:Anoverlaidfunction8Ajax:Anotheroverlaidfunction8ThetechnologiesAjaxisoverlaidon9JavaScript9XMLHttpRequest14Methods14Properties15HTML/XHTML17XML18JSON18CSS19TheDOM19iframesandotherAjaxvariations20JavaScript/AjaxLibraries21Server-sidetechnologies21AlookatDjango21Djangotemplatingkickstart22AmorecompleteglimpseatDjangotemplating23SettingJavaScriptandotherstaticcontentinplace32Summary33Chapter2:jQuery—theMostCommonJavaScriptFramework35jQueryandbasicAjax36jQueryAjaxfacilities39$.ajax()39context40data42 TableofContentsdataFilter43dataType43error(XMLHttpRequest,textStatus,errorThrown)44success(data,textStatus,XMLHttpRequest)44type44url44$.aj0axSetup()45Sampleinvocation45$.get()and$.post()45.load()46jQueryasavirtualhigher-levellanguage48Theselectors48Aclosure-basedexampletomeasureclockskew52Casestudy:Amorein-depthapplication56Chapter3:ValidatingFormInputontheServerSide56Chapter4:Server-sideDatabaseSearchwithAjax56Chapter5:SigningUpandLoggingintoaWebsiteUsingAjax57Chapter6:jQueryIn-placeEditingUsingAjax57Chapter7:UsingjQueryUIAutocompleteinDjangoTemplates57Chapter8:DjangoModelForm:aCSSMakeover57Chapter9:DatabaseandSearchHandling57Chapter10:TinkeringAround:Bugfixes,FriendlierPasswordInput,andaDirectoryThatTellsLocalTime58Chapter11:UsabilityforHackers58Appendix:DebuggingHardJavaScriptBugs58Summary58Chapter3:ValidatingFormInputontheServerSide61Thestandardlecture:low-levelvalidation62Matchingregularexpressions62Youcannotguaranteeabsolutelyvaliddata63Validatingcandetect(some)maliciousinput63TheDjangowayofvalidation64Djangogivesyousomethingsforfree64ThestepsinDjango'svalidation65Amoresensibleandcruelty-freeapproachtovalidation66Thingsgetmurkier67Thezero-one-infinityrule:acardinalruleofthumbinusability68AnimprovementonDjango'sadvertisedapproach68Avalidationexample:GPScoordinates70Avoidingerrormessagesthatpointfingersandsay,"You'rewrong!"71[ii] TableofContentsValidationasdemandingthatassumptionsbemet72Old-school:conformtoourU.S.-basedassumptions!72Addingthewrongkindofband-aid74Makingassumptionsanddemandingthatusersconform76Atleastnamesaresimple,right?76EveninASCII,thingskeepgettingmurkier77Bettervalidationmaybelessvalidation78Caveat:Englishissomethingofalinguafranca79Wedon'thavetonegotiatewithpistols80Doingourbesttosolvethewrongproblem:astory81Itreallydoesapplytovalidation82FacebookandLinkedInknowsomethingbetter83Summary83Chapter4:Server-sideDatabaseSearchwithAjax85Searchingontheclientsideandserverside86HandlingdatabasesthroughDjangomodels86Modelsforanintranetemployeephotodirectory87Searchingourdatabase95AtourofDjangopersistencefacilities100Summary103Chapter5:SigningUpandLoggingintoaWebsiteUsingAjax105admin.py:administrativefunctionscalledonce107functions.py:project-specificfunctions,includingour@ajax_login_requireddecorator107views.py:functionsthatrenderwebpages108style.css:basicstylingforusability113search.html:atemplateforclient-sideAjax114TheDjangoadmininterface122Summary124Chapter6:jQueryIn-placeEditingUsingAjax125Includingaplugin127Howtomakepagesmoreresponsive127Atemplatehandlingtheclient-siderequirements128Thebulkoftheprofile132Whitespaceanddelivery133Page-specificJavaScript136Supportontheserverside137Summary139[iii] TableofContentsChapter7:UsingjQueryUIAutocompleteinDjangoTemplates141Addingautocomplete:firstattempt142Progressiveenhancement,abestpractice142Areal-worldworkaround146"Interest-basednegotiation":apowertoolforproblemsolvingwhenplanAdoesn'twork146Afirstworkaround148BoilerplatecodefromjQueryUIdocumentation154TurningonAjaxbehavior(ortryingto)156Codeontheserverside156Refiningoursolutionfurther159Summary163Chapter8:DjangoModelForm:aCSSMakeover165"Hello,world!"inModelForm165Expandingandcustomizingtheexample168CustomizingModelFormpages'appearance170GoingunderModelForm'shood182Anexcellent"stupid"question:where'sthee-mailslot?184Summary187Chapter9:DatabaseandSearchHandling189MovingforwardtoanAHAHsolution189DjangotemplatesforsimpleAHAH192Templatingforalistofsearchresults192Templateforanindividualprofile195Viewsontheserverside202Tellingiftheuserisloggedin202Aviewtosupportdeletion202TheAHAHviewtoloadprofiles203HelperfunctionsfortheAHAHviewforsearching204Anupdatedmodel206AnAHAHserver-sidesearchfunction207Handlingtheclient-side:Atemplateforthemainpage209CSSforstylingthedirectory232Ourupdatedurlpatterns241Summary241Chapter10:TinkeringAround:Bugfixes,FriendlierPasswordInput,andaDirectoryThatTellsLocalTime243Minortweaksandbugfixes243Settingadefaultnameof"(Insertnamehere)"244EliminatingBorgbehavior244[iv] TableofContentsConfusingjQuery'sload()withhtml()245Preventingdisplayofdeletedinstances246Addingafavicon.ico249Handlingpasswordinputinaslightlydifferentway250Adirectorythatincludeslocaltimekeeping252Summary260Chapter11:UsabilityforHackers261Usabilitybeginswithanthropology…andDjangohackershaveagoodstartonanthropology262Anthropologicalusabilitytechniques263Anintroductoryexample:cardsorting263Focusgroups:cargocultresearchforusability265Anthropologicalobservation:thebedrockofusability265Morethanonewaytoseethesamesituation266Applyingthisfoundationtousability268It'sjustlike(hard)debugging271Lessonsfromotherareas272Livecross-culturalencounters272History273Oldbooksandliterature274Thelastotherarea:whateveryouhave277Understandingtheuser278Alessonfromoptimization278What'swrongwithscratchinganitch,oryouarenotyouruser279Worstpracticesfromthejargonfile279Pythonandusability280It'snotallaboutthecomputer!280Whattodointheconcrete282Furtherreading283Summary284Appendix:DebuggingHardJavaScriptBugs285"JustfiddlingwithFirebug"isconsideredharmful285Cargocultdebuggingatyourfingertips285Thescientificmethodofdebugging286Exhaustingyourselfbybarkingupthewrongtree287Thehumbledebugger289Thevalueoftakingabreak289Twomajorbenefitstoaskingforhelp290[v] TableofContentsFirebugandChromedevelopertools290Thebasicsacrossbrowsers290ZeroinginonChrome293Summary298Index299[vi] PrefaceYouwanttocreateanAJAXapplication.WhywouldyouuseDjango?WhywouldyouusejQuery?Whywouldyouusebothtogether?EnterDjangoJavaScriptIntegration:AJAXandjQuery—yourcomprehensiveanswertoallthesequestionsandtheonlyextensive,practical,andhands-onguidetodevelopinganyAJAXapplicationwithDjangoandjQuery.GonearethedayswhenyouusedtolamentoverthelackofofficialdocumentationonAJAXwithDjango.ThisbookwillteachyouexactlywhyDjangoiscalledThewebframeworkforperfectionistswithdeadlines,howjQuery—the"writelessdomore"JavaScriptlibrary—ispracticallyavirtualhigher-levellanguage,andwhytheybothdeservetobeintegratedwithAJAX.Thishands-on-guideshowsyouhowtoputDjangoandjQuerytogetherintheprocessofcreatinganAJAXapplication.Inthisbook,theyarebroughttogetherinareal-worldscenario,withattentiontousability,tobuildanddevelopanAJAXapplication.ThefirsttwochaptersprovideashortandnecessaryintroductiontotheworldofDjango,jQuery,andAJAX;theremainingchaptersarebasedonacasestudythatwillmakeyourealizetheimmensepotentialandbenefitsofintegratingDjangoandjQuerywithyourAJAXapplication.Bythetimeyouaredonewiththisbook,you'llbedevelopingyourAJAXapplicationswithDjangoandjQueryinlesstimethanyoucansay"integrate".YouwillcoverthebasicsofAJAX;usejQuery,themostcommonJavaScriptlibrary,ontheclientside,andlearnformvalidationwithaneyetowardsusability,buildthingswithDjangoontheserverside,handleloginandauthenticationviaDjango-basedAJAX,andthendipintotherichjQuerypluginecosystemtobuildin-placeeditingintoyourpages. PrefaceYouwilladdauto-completefunctionalitycourtesyofjQueryUI,easilybuildformswithDjangoModelForm,andthenlookataclient-sidesearchimplementationthatcanlookthingsupwithoutnetworkaccessafterinitialdownload.Youwilllearntoimplementasimple,expandableundosystem,andoffermorefull-bloodedaccountmanagement,tinker,fixsomebugs,offeramoreusablewaytohandlepasswordinput,addlocaltimesupportforpeoplewhoarenotinyourtimezone,lookatusability,andfinallytakealookatdebugging.Afterworkingthroughthisbook,youwillhavebothanAJAXapplication:aWeb2.0employeeintranetphotodirectory,andwithitadeepunderstandingthatyoucanusetocustomize,extend,andfurtherdevelopitinyourorganization.WhatthisbookcoversThisbookcoversDjangoJavaScriptintegrationandbuildinganAjaxapplicationwithDjangoontheserversideandjQueryontheclientside.Itprovidesfirstanoverview,thenafirstAjaxapplication,andintroducesjQuery;discussesformvalidation,server-sidedatabasesearch;Ajaxloginfacilities;jQueryin-placeeditingandautocomplete,DjangoModelform,andhowtogiveauto-generatedformsatransformationalCSSmakeover.Italsodiscussesclient-sidefunctionality,customization,andfurtherdevelopmentwithtinkeringandaddedfeatures,beforeagrandfinaleexploringusability,andanappendixondebugginghardJavaScriptbugs.Chapter1,jQueryandAjaxIntegrationinDjangolaysasolidfoundationandintroducesyoutotheworkingpiecesofDjangoAjaxtobeexploredintherestofthebook.Chapter2,jQuery—theMostCommonJavaScriptFrameworkexploresthe"higher-level"wayofdoingthingsinjQuery.YouwilllearnhowjQueryisnotPythonanddoesnotlooklikePython,buthowthereissomething"Pythonicinspirit"abouthowitworks.Chapter3,ValidatingFormInputontheServerSidewillteachyouhowtosendanAjaxrequesttotheserverviajQuery,andvalidateitontheserversidebasedontheprinciplethatallinputisguiltyuntilproveninnocentofbeingmalicious,malformed,incomplete,orotherwiseinvalid.Chapter4,Server-sideDatabaseSearchwithAjaxlooksbothatthemeritsofhandlingsearchingandotherbackendfunctionswiththefullpowerofabackendenvironment,andexploreswhy,ontheclientside,youshouldworkhardtobeaslazyaspossibleindoingnetwork-relatedwork.[2] PrefaceChapter5,Signing-upandLoggingintoaWebsiteUsingAjaxintroducesDjangoauthenticationfacilitiesandaccountmanagementandincludesbothserver-sideandclient-sidecode.Chapter6,jQueryIn-placeEditingUsingAjaxgoesfromabasicfoundationtoacontinuingpracticalapplication.ItwillshowawaytousejQuerytomakeanin-placereplacementofatablethatallowsin-placeediting,whichcommunicateswiththeserverinthebackground,addingpersistencetochanges.Chapter7,UsingjQueryUIAutocompleteinDjangoTemplatestellsyouwhatyouneedontheclientsideandserversidetogetautocompleteworkingwithjQueryUI.Italsoincludescreativeproblemsolvingwhensomethinggoeswrong.ThischapterwilltellyouwhyitisnotuncommonforprogrammerstowritepluginstheirfirstdaydoingjQuery.Chapter8,DjangoModelForm:aCSSMakeoverexploresDjangoModelFormandhowtouseit.Chapter9,DatabaseandSearchHandlingcoversallthebasesforasimple,AHAHsolution.Inadditiontoshowing"lazy"bestpractices,italsoshowcasesaJavaScriptin-memorydatabase,withanapplicationdesigned,atthedeveloper'spreference,toeitheralwaysperformlazyhandlingofsearchandotherrequests,orstartloadinganin-memorydatabaseandfallingbacktolazyhandlinguntilthein-memorydatabaseisavailable.Chapter10,TinkeringAround:Bugfixes,FriendlierPasswordInput,andaDirectoryThatTellsLocalTimecoverssometinkeringandtweaks,andbugfixesalongthewayChapter11,UsabilityforHackersstepsbackfromyourapplicationandtakesalookatusabilityandthebedrockcompetencieshackerscanleveragetodousability.Appendix,DebuggingHardJavaScriptBugslooksatthestateofmindthatisneededtodebugdifficultbugs.WhatyouneedforthisbookThisbookassumesabroadtechnicalmaturityandanabilitytolearnandintegratedifferentskills.IthelpstobeaPythonistawiththeusualstrengthsthatcomewithPython,andknowledgeoftheWeb.Abasicunderstandingof,ortheabilitytolearn,DjangoandJavaScriptwillbehelpful.Ifyou'reagoodgeneralistprogrammerwhowantstolearnDjangoJavaScriptintegration,thisbookisforyou.[3] PrefaceWhothisbookisforThisbookisforpeoplelookingtointegrateAJAX/JavaScriptfunctionalityintotheirwebapplications.ItisforDjangouserswhoarelookingtoeasilyintegrateAJAXfeaturesintotheirapplications.Conversely,itwillalsobeapricelesscompanionforusersfamiliarwithDjangoandjQuerywhoarelookingtointegratethemintheirAJAXapplications.AworkingknowledgeofDjangoandbasicfamiliaritywithAJAXandjQueryareassumed.ConventionsInthisbook,youwillfindanumberofstylesoftextthatdistinguishbetweendifferentkindsofinformation.Herearesomeexamplesofthesestyles,andanexplanationoftheirmeaning.Codewordsintextareshownasfollows:"YoucanoverridetheemptystringbysettingTEMPLATE_STRING_IF_INVALIDinyoursettings.pyfile".Ablockofcodeissetasfollows:functionouter(){result=0;for(i=0;i<100;++i){result+=inner(i);}returnresult}Whenwewishtodrawyourattentiontoaparticularpartofacodeblock,therelevantlinesoritemsaresetinbold:defajax_profile(request,id):entity=directory.models.Entity.objects.filter(id=int(id))[0]ifentity.is_invisible:returnHttpResponse(u'

People,etc.

')Newtermsandimportantwordsareshowninbold.Wordsthatyouseeonthescreen,inmenusordialogboxesforexample,appearinthetextlikethis:"WecanWecanthenclickonEntity(orLocations),andaddanentity".Warningsorimportantnotesappearinaboxlikethis.[4] PrefaceReaderfeedbackFeedbackfromourreadersisalwayswelcome.Letusknowwhatyouthinkaboutthisbook—whatyoulikedormayhavedisliked.Readerfeedbackisimportantforustodeveloptitlesthatyoureallygetthemostoutof.Tosendusgeneralfeedback,simplysendane-mailtofeedback@packtpub.com,andmentionthebooktitleviathesubjectofyourmessage.Ifthereisabookthatyouneedandwouldliketoseeuspublish,pleasesendusanoteintheSUGGESTATITLEformonwww.packtpub.comore-mailsuggest@packtpub.com.Ifthereisatopicthatyouhaveexpertiseinandyouareinterestedineitherwritingorcontributingtoabook,seeourauthorguideonwww.packtpub.com/authors.CustomersupportNowthatyouaretheproudownerofaPacktbook,wehaveanumberofthingstohelpyoutogetthemostfromyourpurchase.DownloadingtheexamplecodeforthebookYoucandownloadtheexamplecodefilesforallPacktbooksyouhavepurchasedfromyouraccountathttp://www.PacktPub.com.Ifyoupurchasedthisbookelsewhere,youcanvisithttp://www.PacktPub.com/supportandregistertohavethefilese-maileddirectlytoyou.ErrataAlthoughwehavetakeneverycaretoensuretheaccuracyofourcontent,mistakesdohappen.Ifyoufindamistakeinoneofourbooks—maybeamistakeinthetextorthecode—wewouldbegratefulifyouwouldreportthistous.Bydoingso,youcansaveotherreadersfromfrustrationandhelpusimprovesubsequentversionsofthisbook.Ifyoufindanyerrata,pleasereportthembyvisitinghttp://www.packtpub.com/support,selectingyourbook,clickingontheerratasubmissionformlink,andenteringthedetailsofyourerrata.Onceyourerrataareverified,yoursubmissionwillbeacceptedandtheerratawillbeuploadedonourwebsite,oraddedtoanylistofexistingerrata,undertheErratasectionofthattitle.Anyexistingerratacanbeviewedbyselectingyourtitlefromhttp://www.packtpub.com/support.[5] PrefacePiracyPiracyofcopyrightmaterialontheInternetisanongoingproblemacrossallmedia.AtPackt,wetaketheprotectionofourcopyrightandlicensesveryseriously.Ifyoucomeacrossanyillegalcopiesofourworks,inanyform,ontheInternet,pleaseprovideuswiththelocationaddressorwebsitenameimmediatelysothatwecanpursuearemedy.Pleasecontactusatcopyright@packtpub.comwithalinktothesuspectedpiratedmaterial.Weappreciateyourhelpinprotectingourauthors,andourabilitytobringyouvaluablecontent.QuestionsYoucancontactusatquestions@packtpub.comifyouarehavingaproblemwithanyaspectofthebook,andwewilldoourbesttoaddressit.[6] jQueryandAjaxIntegrationinDjangoWewillbeworkingwiththeleadingPythonwebframework,Django,ontheserverside,andjQuery-poweredAjaxontheclientside.Duringthecourseofthisbook,wewillcoverthebasictechnologiesandthenseethemcometogetherinanemployeeintranetphotodirectorythatsharessomeWeb2.0strengths.ThereismorethanonegoodJavaScriptlibrary;wewillbeworkingwithjQuery,whichhasreachedacceptanceasastandardlightweightJavaScriptlibrary.ItmightbesuggestedthatPythonistasmayfindmuchtolikeinjQuery:jQuery,likePython,wascarefullydesignedtoenablethedevelopertogetpowerfulresultseasily.Inthischapter,wewill:•DiscussAjaxasnotasingletechnologybutatechniquewhichisoverlaidonothertechnologies•CoverthebasictechnologiesusedinAjaxJavaScript•Cover"Hello,world!"inaDjangokickstart•IntroducetheDjangotemplatingengine•CoverhowtoserveupstaticcontentinDjangoOverall,whatwewillbedoingislayingasolidfoundationandintroducingtheworkingpiecesofDjangoAjaxtobeexploredinthisbook. jQueryandAjaxIntegrationinDjangoAjaxandtheXMLHttpRequestobjectAjaxisnotatechnologylikeJavaScriptorCSS,butismorelikeanoverlaidfunction.So,whatexactlyisthat?Humanspeech:AnoverlaidfunctionHumanspeechisanoverlaidfunction.Whatismeantbythisisreflectedintheanswertoaquestion:"Whatpartofthehumanbodyhasthebasicjobofspeech?"Thetongue,foroneanswer,isusedinspeech,butitalsotastesfoodandhelpsusswallow.Thelungsanddiaphragm,foranotheranswer,performtheessentialtaskofbreathing.Thebraincannotbeoverlooked,butitalsodoesagreatmanyotherjobs.Allofthesepartsofthebodydosomethingmoreessentialthanspeechand,forthatmatter,allofthesecanbefoundamonganimalsthatcannottalk.Speechissomethingthatisoverlaidoverorgansthatarethereinthefirstplacebecauseofsomethingotherthanspeech.SomethingsimilartothisistrueforAjax,whichisnotatechnologyinitself,butsomethingoverlaidontopofothertechnologies.Ajax,somepeoplesay,standsforAsynchronousJavaScriptandXML,butthatwasaretroactiveexpansion.JavaScriptwasintroducedalmostadecadebeforepeoplebeganseriouslytalkingaboutAjax.NotonlyisittechnicallypossibletouseAjaxwithoutJavaScript(onecansubstituteVBScriptattheexpenseofbrowsercompatibility),buttherearequiteafewsubstantialreasonstouseJavaScriptObjectNotation(JSON)inlieuofheavy-on-the-wireeXtensibleMarkupLanguage(XML).PerformingtheoverlaidfunctionofAjaxwithJSONreplacingXMLisjustaseligibletobeconsideredfull-fledgedAjaxasasolutionincorporatingXML.Ajax:AnotheroverlaidfunctionWhatexactlyisthisoverlaidfunction?Ajaxisawayofusingclient-sidetechnologiestotalkwithaserverandperformpartialpageupdates.Updatesmaybetoallorpartofthepage,orsimplytodatahandledbehindthescenes.Itisanalternativetotheolderparadigmofhavingawholepagereplacedbyanewpageloadedwhensomeoneclicksonalinkorsubmitsaform.Partialpageupdates,inAjax,areassociatedwithWeb2.0,whilewholepageupdatesareassociatedwithWeb1.0;itisimportanttonotethat"Web2.0"and"Ajax"arenotinterchangeable.Web2.0includesmoredecentralizedcontrolandcontributionsbesidesAjax,andforsomeobjectivesitmaymakeperfectsensetodevelopane-commercesitethatusesAjaxbutdoesnotopenthedoortothesamekindofcommunitycontributionsasWeb2.0.[8] Chapter1SomeofthekeyfeaturescommoninWeb2.0include:•PartialpageupdateswithJavaScriptcommunicatingwithaserverandrenderingtoapage•Anemphasisonuser-centereddesign•Enablingcommunityparticipationtoupdatethewebsite•EnablinginformationsharingascoretowhatthiscommunicationallowsTheconceptof"partialpageupdates"maynotsoundverybig,butpartofitssignificancemaybeseeninanunintendedeffect.Theoriginalexpectationofpartialpageupdateswasthatitwouldenablewebapplicationsthatweremoreresponsive.Theexpectationwasthatifsubmittingaformwouldonlychangeasmallareaofapage,usingAjaxtojustloadthechangewouldbefasterthanreloadingtheentirepageforeveryminorchange.Thatmuchwastrue,butonceprogrammersbeganexploring,whattheyusedAjaxforwasnotsimplyminorpageupdates,butmakingclient-sideapplicationsthattookonchallengesmorelikethoseonewouldexpectadesktopprogramtodo,andthemoreinterestingAjaxapplicationsusuallybecameslower.Again,thiswasnotbecauseyoucouldnotfetchpartofthepageandupdateitfaster,butbecauseprogrammersweretryingtodothingsontheclientsidethatsimplywerenotpossibleundertheolderwayofdoingthings,andwerepushingtheenvelopeontheconceptofawebapplicationandwhatwebapplicationscando.ThetechnologiesAjaxisoverlaidonNowletuslookatsomeofthetechnologieswhereAjaxmaybesaidtobeoverlaid.JavaScriptJavaScriptdeservesprideofplace,andwhileitispossibletouseVBScriptforInternetExplorerasmuchmorethanaproofofconcept,fornowifyouaredoingAjax,itwillalmostcertainlybeAjaxrunningJavaScriptasitsengine.YourapplicationwillhaveJavaScriptworkingwithXMLHttpRequest,JavaScriptworkingwithHTML,XHTML,orHTML5;JavaScriptworkingwiththeDOM,JavaScriptworkingwithCSS,JavaScriptworkingwithXMLorJSON,andperhapsJavaScriptworkingwithotherthings.[9] jQueryandAjaxIntegrationinDjangoWhileaddressingagroupofDjangodevelopersorPythonistas,itwouldseemappropriatetoopenwith,"Ishareyourenthusiasm."Ontheotherhand,whileaddressingagroupofJavaScriptprogrammers,inafewwaysitismoreappropriatetosay,"Ifeelyourpain."JavaScriptisalanguagethathasbeendiscoveredasagem,butitswartswereenoughforittobelargelyunappreciatedforalongtime."AjaxisthegatewaydrugtoJavaScript,"asithasbeensaid—however,JavaScriptneedsagatewaydrugbeforepeoplegethookedonit.JavaScriptisanexcellentlanguageandaterriblelanguagerolledintoone.BeforediscussingsomeofthestrengthsofJavaScript—andthelanguagedoeshavesometrulydeepstrengths—Iwouldliketosay"Ifeelyourpain"anddiscusstwoquitedistincttypesofpainintheJavaScriptlanguage.ThefirstsourceofpainissomeofthelanguagedecisionsinJavaScript:•TheWikipediaarticlesaysitwasdesignedtoresembleJavabutbeeasierfornon-programmers,adecisionreminiscentofSQLandCOBOL.•TheJavaprogrammerwhofindstheC-familyidiomoffor(i=0;i<100;++i)availablewillbeastonishedtofindthatthefunctionsareclobberingeachother'sassignmentstoiuntiltheyareexplicitlydeclaredlocaltothefunctionbydeclaringthevariableswithvar.Thereismorepainwherethatcamefrom.Thefollowingtwofunctionswillnotperformthenaivelyexpectedmathematicalcalculationcorrectly;theassignmentstoiandtheresultwillclobbereachother:functionouter(){result=0;for(i=0;i<100;++i){result+=inner(i);}returnresult}functioninner(limit){result=0;for(i=0;iTheWorldWideWebproject

WorldWideWeb

TheWorldWideWeb(W3)isawide-areahypermediainformationretrievalinitiativeaimingtogiveuniversalaccesstoalargeuniverseofdocuments.

EverythingthereisonlineaboutW3islinkeddirectlyorindirectlytothisdocument,includinganexecutivesummaryoftheproject,Mailinglists,Policy,November'sW3news,FrequentlyAskedQuestions.

…Atthetimeofthiswriting,HTML5istakingshapebutisnot"outinthewild",andsotherearenoreportsofhowtheshoefeelsafterthepublichaswornitforawhile.Codeinthisbook,wherepossible,willbewritteninXHTML1.0Strict.Dependingonyoursituation,thismayormaynotbetherightdecisionforyou;ifyouareworkingwithanexistingproject,therightHTML/XHTMLisoftentheonethatmaintainsconsistencywithintheproject.[17] jQueryandAjaxIntegrationinDjangoXMLeXtensibleMarkupLanguage(XML)istiedtoanattempttocleanupearlyHTML.Atleastinearliestforms,HTMLwasablacksheepamongspecificmarkuplanguagesderivedfromthegeneralizedandquiteheavyStandardGeneralizedMarkupLanguage(SGML).Forgivingwebbrowsersmeant,inpart,thatearlywebhobbyistscouldwriteterriblemarkupanditwouldstilldisplaywellinabrowser.Theamountofterriblemarkuponthewebwasnotjustanissueforpurists;itmeantthatmakingaparserthatcouldmakesenseofearly"WildWest"webpagesingeneralwasanearlyimpossibletask.XMLisvastlysimplifiedfromSGML,butitprovidesagenericspacewhereanHTMLvariant,XHTML,couldpickuptheworkdonebyHTMLbutnotpresentparserswithunpredictabletagsoup.XHTMLcouldbedescribedasHTMLbroughtbackintothefold,stillgoodfordoingwebdevelopment,butwithoutmakingmachineinterpretationsuchahopelesscause.WhereearlyHTMLwasdevelopedwithbrowsersthatweremeanttobeforgiving,XMLrequesteddraconianerrorhandling,andvalidatedXMLorXHTMLdocumentsaredocumentsthatcanbeparsedinasensibleway.XMLworksforexchanginginformation,anditworkswheremanyofitspredecessorshadfailed:itprovidesinteroperabilitybetweendifferentsystemsafteralonghistoryoffailedattemptsatautomatingB2Bcommunicationandfailedattemptsatautomatedconversionbetweentextdataformats.Notwithstandingthis,itisaheavyandverbosesolution,withabureaucraticambiance,comparedinparticulartoalean,meanJSON.XML-basedapproachestodatastorageandcommunicationareincreasinglycritiquedindiscussionsontheweb.IfyouhaveareasonablechoicebetweenXMLandJSON,wesuggestthatyouseriouslyconsiderJSON.JSONJavaScriptObjectNotation(JSON)isabrilliantlysimpleidea.WhileformatslikeXML,ReStructuredText,andsoonsharetheassumptionthat"ifyou'regoingtoparsethisfromyourlanguage,yourlanguagewillneedtohaveaparseradded,"JSONsimplytakesadvantageofhowanobjectwouldbespecifiedinJavaScript,andclarifiesacoupleofminorpointstomakeJSONconceptuallysimplerandcross-browserfriendly.JSONisclear,simple,andconciseenoughthatnotonlyisitaformatofchoiceforJavaScript,butitisgainingtractioninotherlanguages,anditisbeingusedforcommunicationbetweenlanguagesthatneeda(simple,added)parsertoparseJSON.Theotherlanguagescan'tuseeval()tosimplyrunJSON,andinJavaScriptyoushouldhaveJSONcheckedtomakesureitdoesnotcontainmaliciousJavaScriptyoushouldnoteval().However,JSONisturningouttohaveamuchbroaderimpactthantheinitial"incommunicatingwithJavaScript,justgiveitcodetodeclaretheobjectbeingcommunicatedthatcansimplybeevaluatedtoconstructtheobject."[18] Chapter1CSSCascadingStyleSheets(CSS)mayhaveintroducedsomenewpossibilitiesforpresentation,butquitealotofpresentationwasalreadypossiblebeforehand.CSSdidnotsomuchaddstylingcapabilities,asitaddedgoodengineeringtostyling(goodengineeringistheessenceof"separatingpresentationfromcontent"),andmakethecombinationofsemanticmarkupandattractiveappearanceafarmoreattainablegoal.Itallowsparlortrickssuchasin-placerebrandingofwebsites:makingchangesinimagesandchangingonestylesheetis,atleastinprinciple,enoughtoreskinanextensivewebsitewithouttouchingasinglecharacterofitsHTML/XHTMLmarkup.InAjax,asfortherestoftheweb,thepreferredpracticeistousesemantic,structuralmarkup,andthenaddstylesinastylesheet(notinline)sothataparticularelement,optionallybelongingtotherightclassorgiventherightID,willhavethedesiredappearance.Tablesarenotdeprecatedbutshouldbeusedforsemanticpresentationoftabulardatawhereitmakessensetousenotonlyatdbutathaswell.Whatisdiscouragedisusingthesideeffectthattablescanpositioncontentthatisnot,semanticallyspeaking,tabulardata.TheDOMAsfarasdirecthumanbrowsingisconcerned,HTMLandassociatedtechnologiesarevehiclestodeliverapickledDocumentObjectModel(DOM),andnothingmore.Inthisrespect,HTMLisameanstoanend:theDOMisthe"deserializedobject,"orbetter,the"liveform"ofwhatwereallydelivertopeople.HTMLmayhelpprovideacompleteblueprint,andthe"completeblueprint"isameanstothe"fullyrealizedbuilding."ThisiswhysolvingAjaxproblemsonthelevelofHTMLtextarelikeansweringthewrongquestion,oratleastsolvingaproblemonthewronglevel.Itislikedecidingthatyouwantapaintinghungonawallofabuilding,andthengoingaboutgettingitbyaddingthepaintingtotheblueprintandaskingconstructionpersonneltoimplementthespecifiedchange.Itmaybebettertohangthepaintingonthewalldirectly,asisdoneinAjaxDOMmanipulations.document.write()anddocument.getElementById().innerHTML()stillhaveaplaceinwebdevelopment.Itisasensibleoptimizationtowantastatic,cacheableHTML/XHTMLfileincludethatwillonlybedownloadedonceintheusualmulti-pagevisit.AJavaScriptincludewithaseriesofdocument.write()maybetheleastShanghaiingyoucandototechnologiesandstillachievethatgoal.ButthisisnotAjax;itisbarelyJavaScript,andthisisnotwhereweshouldbegettingourbearings.InAjax,aseriousalternativetothiskindofsolutionforalteringpartofawebpageiswiththeDOM.Asthebookprogresses,wewillexploreAjaxdevelopmentthatworkswiththeDOM.[19] jQueryandAjaxIntegrationinDjangoiframesandotherAjaxvariationsAjaxincludesseveralvariations;Cometforinstance,isavariationonstandardAjaxinwhicheitheranXMLHttpRequestobject'sconnectiontoaserveriskeptopenandstreamingindefinitely,oranewconnectionisopenedwheneveranoldoneisclosed,creatinganAjaxenvironmentinwhichtheserveraswellastheclientcanpushmaterial.Thisisused,forinstance,insomeinstantmessagingimplementations.OnemuchmoreessentialAjaxvariationhastodowithloadingdocumentsintoseamlesslyintegratediframesinsteadofmakingDOMmanipulationstoasingle,frame-freewebpage.IfyouclickaroundonthepageforaGmailaccount,youwillseepartialpagerefreshesthatlookconsistentwithAjaxDOMmanipulations:whathappenswhenyouclickonComposeMail,orafilter,oramessagesubject,looksverymuchlikeanAjaxupdatewheretheGmailwebapplicationtalkswiththeserverifitneedsto,andthenupdatestheDOMinaccordancewithyourclicks.However,thereisoneimportantdifferencebetweenGmail'sbehaviorandasimilarAjaxclonethatupdatestheDOMforoneframelesswebpage:whathappenswhenyouclickthebrowser"Back"button.Normally,ifyouclickonalink,youtriggeranAjaxeventbutnotawholepagerefresh,andAjaxoptionallycommunicateswithaserverandupdatessomepartoftheDOM.Thisdoesnotregisterinthebrowser'shistory,andhittingtheBackbuttonwouldnotsimplyresetthelastAjaxpartialpageupdate.IfyoumadeanAjaxcloneofGmailthatusedDOMmanipulationsinsteadofseamlesslyintegratediframes,therewouldbeoneimportantdifferenceinusingtheclone:hittingBackwoulddofarmorethanreversethelastDOMmanipulation.Itwouldtakeyoubacktotheloginorloadscreen.InGmail,thebrowser'sBackbuttonworkswithsurgicalaccuracy,andthereasonitcandosomethingmuchbetterthantakeyoubacktotheloginscreenisthatGmailiscarefullyimplementedwithiframes,andeverychangethattheBackbuttoncanundoisimplementedbyafreshpageloadinoneoftheseamlesslyintegratediframes.Thatcreatesbrowsinghistory.Forthatmatter,aproofofconcepthasbeencreatedforanAjaxapplicationthatdoesnotuseclient-sidescriptingorprogramming,insteadusing,ontheclientside,asystemofframes/iframes,targets,links,formsubmissions,andmetarefreshtagsinordertoperformpartialpageupdates.WhetherthisvarianttechniquelendsitselftocreatinggracefulalternativestostandardAjaximplementations,orisonlyacuriositymerelylendingitselftoproofsofconcept,itisinprinciplepossibletomakeanAjaxapplicationthatlosesnothingifavisitor'sbrowserhasturnedoffscriptingcompletely.CometandiframesaretwoofmanypossiblevariationsonthebasicAjaxtechnique;whatqualifiesasAjaxismoreamatterofPython-orJavaScript-styleduck-typingthanJava-stylestatictyping."AsynchronousJavaScriptandXML"describesareferenceexamplemorethanastrictdefinition,anditisnotappropriatetosay"if[20] Chapter1youreplaceXMLwithJSONthen,bydefinition,itisn'treallyAjax."Thisisacaseof,"theproofofthepuddingisintheeating,"notwhattechnologiesoreventechniquesareinthekitchen.JavaScript/AjaxLibrariesThisbookadvocatestakingadvantageoflibraries,andasalimitationofscopefocusesonjQuery.Ifyouonlylearnonelibrary,orifyouarestartingwithjustonelibrary,jQueryisagoodchoice,anditiswidelyused.Itispowerful,butitisalsoamucheasierenvironmenttogetstartedinthansomeotherlibraries;inthatway,itissomewhatlikePython.However,itisbestnottoask,"Whichonelibraryisbest?"but"Whichlibraryorlibrariesaretherighttoolsforthisjob?",anditiscommonreal-worldpracticetousemorethanonelibrary,possiblyseveral.JavaScriptlibrariesofferseveraladvantages.Theycanreducechoresandboilerplatecode,significantlylesseningthepainofJavaScript,andprovideamoreuniforminterface.Theycanalsoprovide(forinstance)ready-madewidgets;wewillbeworkingwithajQuerysliderlateroninthisbook.Andonabroadscale,theycanlettheJavaScriptyouwritebehigher-levelandalittlemorePythonic.Server-sidetechnologiesManyofthe"usualsuspects"inclient-sidetechnologieshavebeenmentioned.Thelistofclient-sidetechnologiesisgenerallyconstrainedbywhatisavailableincommonwebbrowsers;thelistofavailableserver-sidetechnologiesisonlyconstrainedbywhatwillworkontheserver,andanygeneral-purposeprogramminglanguagecandothejob.Thequestionontheserverisnot"Whatisavailable?"but"Whichoptionwouldyouchoose?"PythonandDjangomakeanexcellentchoiceofserver-sidetechnology,andwewillworkwiththeminthisbook.AlookatDjangoDjango'sdeveloperscallit"thewebframeworkforperfectionistswithdeadlines,"anditisoneofthemostpopularPythonwebframeworks,perhapsthemostpopular.IncontrasttotheMVCpattern,whichseparatesconcernsintoModel,View,andController,itcouldbedescribedasanMTVpattern,whichseparatesconcernsintoModel,Template,andView.TheModelisaclassthattiesintoanORMwhereinstancescorrespondtorowsinthetablebutactandfeellikePythonobjects.TheTemplateisasystemdesignedtobeeasyfornon-Pythondevelopers(thougheasyforPythonistastoo),andlimitstheextenttowhichHTMLneedstobesprinkledthroughoutthePythonsource.TheViewisafunctionthatrenders,inmostcases,fromatemplate.Let'slookatakickstartexampleofDjangoinaction.[21] jQueryandAjaxIntegrationinDjangoDjangotemplatingkickstartLetusbrieflygothroughhowtoinstallDjango,createasampleproject,andcreateanduseabasictemplatethatcanserveasabasisforfurthertinkering.Djangoinstallationinstructionsareathttp://docs.djangoproject.com/en/dev/intro/install/;forUbuntu,forinstance,youwillwanttorunsudoapt-getinstallpython-django.OnceyouhaveDjangoinstalled,createaprojectnamedsample:django-admin.pystartprojectsampleGointothesampledirectory,andcreatethedirectorytemplates.Enterthetemplatesdirectory.Createatemplatefilenamedindex.htmlcontainingthefollowingtemplate:{%blocktitle%}Hello,world!{%endblocktitle%}{%blockbody%}

{%blockheading%}Hello,world!{%endblockheading%}

{%blockcontent%}

GreetingsfromtheDjangotemplatingengine!

{%endblockcontent%}{%endblockbody%}Gouponeleveltothesampledirectoryandedittheurls.pyfilesothatthefirstlineafterurlpatterns=patterns('',is:(r'^$','sample.views.home'),Thencreatetheviews.pyfilecontainingthefollowing:#!/usr/bin/python/fromdjango.shortcutsimportrender_to_responsedefhome(request):returnrender_to_response(u'index.html')[22] Chapter1Editthesettings.pyfile,andadd:os.path.join(os.path.dirname(__file__),"templates"),rightafter:TEMPLATE_DIRS=(Then,fromthecommandline,run:pythonmanage.pyrunserverThismakestheserveraccessibletoyourcomputeronlybyenteringtheURLhttp://localhost:8080/inyourwebbrowser.Ifyouareinaprotectedenvironmentbehindafirewall,appropriateNATting,orthelike,youcanmakethedevelopmentserveravailabletothenetworkbyrunning:pythonmanage.pyrunserver0.0.0.0:8080Thereisonepointofclarificationwewouldliketomakeclear.Djangoispackagedwithaminimal,single-threadedwebserverthatisintendedtobejustenoughtostartexploringDjangoinadevelopmentenvironment.Django'screatorsareattemptingtomakeagood,competitivewebframeworkandnotagood,competitivewebserver,andthedevelopmentserverhasneverundergoneasecurityaudit.TheexplicitadvicefromDjango'screatorsis:whendeploying,useagood,seriouswebserver;theyalsoprovideinstructionsfordoingthis.AmorecompleteglimpseatDjangotemplatingBeforefurtherexploringtechnicaldetails,itwouldbeworthtakingalookattheopinionsandphilosophybehindtheDjangotemplatinglanguage,becauseanunderstandableapproachof,"Oh,it'sageneralpurposeprogramminglanguageusedfortemplating,"isarecipeforneedlessfrustrationandpain.TheDjangodevelopersthemselvesacknowledgethattheiropinionsinthetemplatinglanguageareonejustopinioninanareawheredifferentpeoplehavedifferentopinions,andyouarewelcometodisagreewiththemifyouwant.Ifyoudon'tlikethetemplatingsystemthatDjangocomeswith,Djangoisdesignedtoletyouuseanother.Butitisworthunderstandingwhatexactlythephilosophyisbehindthetemplatinglanguage;evenifthisisnottheonlyphilosophyonecoulduse,itiscarefullythoughtout.[23] jQueryandAjaxIntegrationinDjangoTheDjangotemplatinglanguageisintendedtofostertheseparationofpresentationandlogic.Initsdesigndecisions,bothlargeandsmall,Django'stemplatingengineisoptimizedprimarilyfordesignerstousefordesigning,ratherthanprogrammerstouseforprogramming,anditslimitationsarealmostascarefullychosenasthefeaturesitprovides.UnlikeASP,JSP,andPHP,itisnotaprogramminglanguageinterspersedwithHTML.Itprovidesenoughpowerforpresentation,andisintendednottoprovideenoughpowertodoseriousprogrammingworkwhereitdoesn'tbelong(intheDjangoopinion),andissimpleenoughthatsomenon-programmerscanpickitupinaday.Foraprogrammer,thedifficultyoflearningthetemplatingbasicsiscomparabletothedifficultyofsimpleHTMLorSQL:itissimple,andagoodbiteasiertolearnthanwrappingyourarmsaroundaregularprogramminglanguage.Someprogrammerslikeitimmediately,buttherearesomewhostartedbyasking,"Whydoesn'tthetemplatinglanguagejustletyoumixPythonandHTML?"andafterplayingwithit,foundthemselvessaying,"Thisisn'twhatIwouldhavecomeupwithmyself,butIreally,reallylikeit."Additionalbenefitsincludeitbeingfast(mostoftheworkisdonebyasingleregularexpressioncall,andthefounderstalkaboutdisablingcachingbecauseitwasn'tasfastasthetemplaterendering),secure(itisdesignedsothatitcanbeusedbyuntrusteddesignerswithoutallowingamaliciousdesignertoexecutearbitrarycode),andversatileenoughtogeneratewhatevertextformatyouwant:plaintext,HTML,XML,XHTML,JSON,JavaScript,CSV,ReStructuredText,andsoon.WewillbeusingittogeneratewebpagesandJSON,butDjango'stemplatinglanguageisageneral-purposetexttemplatingsolution.FollowingtheDjangosite'slead,letususeatemplateintendedasanexampleofhowonemightbeginabasetemplateforasite,thenstarttowalkthroughitscontents,andthenlookatsomeofhowitcouldbeusedandpartsoverriddentocreateaspecificdocument.Thisrendersasfollows,ifwestripoutblanklines:[24] Chapter1

Letusunwrapwhatisgoingonhere;thereismoretothetemplatethanhowitrenderstothispage,butletusstartwiththatmuch.The{%blockdtd%}styletagsbegin,orthecaseof{%endblockdtd%}end,asemanticblockoftextthatcanbeleftuntouchedorcanbereplaced.Inthecaseofthisonetemplate,theeffectistostripthemoutlikecomments,buttheywillyieldbenefitslateron,muchlikesemanticHTMLmarkupwithCSSyieldsbenefitslateron.Djangotemplatingreflectsachoicetogowithhooksratherthanincludesbecausehooksprovidethemoreversatilesolution.The"beginner'smistake"versionofaheadertoincludemightbesomethinglikethefollowing:Welcometomysite![25] jQueryandAjaxIntegrationinDjangoThissolutioncouldbecontinuedbyaddingasidebar,butthereisaminorproblem,oratleastitseemsminoratfirst:therearebitsofthisheaderthatarenotgeneric.Foraserioussite,havingeverypagetitled,"Welcometomysite!"wouldbeanembarrassment.Thelanguageisdeclaredtobe"en-US",meaningU.S.English,whichiswonderfuliftheentiresiteisinU.S.English,butifitexpandstoincludemorethanU.S.Englishcontent,hardcoding"en-US"willbeaproblem.IftheonlyconcernistoaccuratelylabelBritishEnglish,thenthemoreexpansive"en"couldbesubstitutedin,buthardcoding"en-US"and"en"areequallyunhelpfulifthesiteexpandstofeatureasectioninRussian.Thisheaderdoesnotincludeothermetatagsthatmightbedesirable,suchas"description",whichisideallywrittenforaspecificpageandnotdoneassite-wideboilerplate.Includingtheheaderverbatimsolvesaproblem,butitdoesn'tprovideaveryflexiblesolution.Thepreviousexamplebuildsinhooks.Itdoesspecify:{%blockhtml_tag%}{%endblockhtml_tag%}Ifnotoverridden,thiswillrenderas:However,inatemplatethatextendsthis,byhaving{%extends"base.html"%}asitsopeningtag,ifthebaseisloadedasbase.html,then:{%blockhtml_tag%}{%endblockhtml_tag%}willrender:AndRussianmaybedeclaredinthesameway:{%blockhtml_tag%}{%endblockhtml_tag%}willrender:Thetemplateasgivendoesspecify"en-US"morethanonce,buteachoftheseisinsideablockthatcanbeoverriddentospecifyanotherlanguage.[26] Chapter1Wedefineinitialblocks.First,theDTD:{%blockdtd%}{%endblockdtd%}Then,theHTMLtag:{%blockhtml_tag%}{%endblockhtml_tag%}Thenwedefinethehead,withthetitleandfavicon:{%blockhead%}{%blocktitle%}{{page.title}}{%endblocktitle%}{%blockhead_favicon%}{%endblockhead_favicon%}Thenwedefinehooksformetatagsinthehead.WedefineaContent-TypeofUTF-8;thisisabasicpointsothatnon-ASCIIcontentwilldisplaycorrectly:{%blockhead_meta%}{%blockhead_meta_author%}{%endblockhead_meta_author%}{%blockhead_meta_charset%}{%endblockhead_meta_charset%}{%blockhead_meta_contentlanguage%}{%endblockhead_meta_contentlanguage%}{%blockhead_meta_description%}{%endblockhead_meta_description%}{%blockhead_meta_keywords%}{%endblockhead_meta_keywords%}{%blockhead_meta_othertags%}{%endblockhead_meta_othertags%}{%blockhead_meta_refresh%}{%endblockhead_meta_refresh%}{%blockhead_meta_robots%}{%endblockhead_meta_robots%}{%endblockhead_meta%}WedeclareablocktospecifyanRSSfeedforthepage:{%blockhead_rss%}{%endblockhead_rss%}[27] jQueryandAjaxIntegrationinDjangoWeaddhooksforCSS,bothatthesitelevel,andsection,andpage.Thisallowsafairlyfinegranularityofcontrol:{%blockhead_css%}{%blockhead_css_site%}{%endblockhead_css_site%}{%blockhead_css_section%}{%endblockhead_css_section%}{%blockhead_css_page%}{%endblockhead_css_page%}{%endblockhead_css%}Weaddsection-andpage-specificheaderinformation:{%blockhead_section%}{%endblockhead_section%}{%blockhead_page%}{%endblockhead_page%}Thenweclosetheheadandopenthebody:{%endblockhead%}{%blockbody%}Wedefineasidebarblock,withahooktopopulateit:{%blockbody_sidebar%}{%endblockbody_sidebar%}wnloadfromWow!eBookoDWedefineablockforthemaincontentarea:{%blockbody_content%}Fortheheaderofthemaincontentarea,wedefineabannerhook,andaheaderforthepage'stitle,shouldsuchbeprovided.(Ifnoneisprovided,thereisnocrashorerror;theemptystringisdisplayedfor{{page.title}}.){%blockbody_header%}{%blockbody_header_banner%}{%endblockbody_header_banner%}{%blockbody_header_title%}

{{page.title}}

{%endblockbody_header_title%}[28] Chapter1Wedefineabreadcrumb,whichisoneofmanysmallusabilitytouchesthatcanbedesirable:{%blockbody_header_breadcrumb%}{{page.breadcrumb}}{%endblockbody_header_breadcrumb%}{%endblockbody_header%}Weaddaslotforannouncements,thenthebody'smainarea,andthenclosetheblockanddiv:{%blockbody_announcements%}{%endblockbody_announcements%}{%blockbody_main%}{%endblockbody_main%}{%endblockbody_content%}Wedefineafooterdiv,withafooterbreadcrumb,andahookforanythingourcompany'slawyersaskedustoput:{%blockbody_footer%}{%blockbody_footer_breadcrumb%}{{page.breadcrumb}}{%endblockbody_footer_breadcrumb%}{%blockbody_footer_legal%}{%endblockbody_footer_legal%}{%endblockbody_footer%}Nowweclosethatdiv,thebody,andthebodyblock:{%endblockbody%}Weaddafooter,withJavaScriptblocks,againatthesite/section/pagelevelofhooks:{%blockfooter%}{%blockfooter_javascript%}{%blockfooter_javascript_site%}{%endblockfooter_javascript_site%}{%blockfooter_javascript_section%}{%endblockfooter_javascript_section%}{%blockfooter_javascript_page%}[29] jQueryandAjaxIntegrationinDjango{%endblockfooter_javascript_page%}{%endblockfooter_javascript%}{%endblockfooter%}Andthat'sit.Youcanmakeasmanylayersoftemplatesasyouwant.Onesuggestedapproachistomakethreelayers:onebasetemplateforyourentiresite,thenmorespecifictemplatesforsectionsofyoursite(whatevertheymaybe),andthenindividualtemplatesforenduse.Thetemplategivenisabasetemplate,anditprovideshooksasnarrowasaspecificmetatagorasbroadasthemaincontentarea.Forourextendedexample,ifitisnamedbase.html,thenwecancreateanothertemplate,russian.html,whichwilldeclareitscontenttobeintheRussianlanguage.(Ordinarilyonewoulddomoreinterestingthingsinoverridingatemplatethanmerelyreplacingtags,butforillustrationpurposeswewilldothat,andonlythat:{%extends"base.html"%}{%blockhtml_tag%}{%endblockhtml_tag%}{%blockhead_meta_contentlanguage%}{%endblockhead_meta_contentlanguage%}Theseblockoverridesmayoccuranywhere;whilethe{%extends"base.html"%}tagmustbeplacedfirst,thetwotagsmaybeswapped.Itwouldworkjustaswelltocreatealanguage-agnosticbase.htmlwithonlyanemptyhook:{%blockhead_meta_contentlanguage%}{%endblockhead_meta_contentlanguage%}andadefaultHTMLtagof:Andthencreateenglish.htmlandrussian.html,oren-US.htmlandru-RU.html,asbasetemplatesforthoselanguages.However,therearemanyothertagsthanthoseusedforblocksandoverriding.Wewilljustbarelysamplethembelow,lookingathowtodisplayvariables,andthenothertags.[30] Chapter1Thereareothertagsthatlooklike{%…%},butIwouldcommentbrieflyonthevariabletagssuchas{{page.title}}.TheDjangotemplatinglanguageusesdottedreferences,butnotinexactlythesamewayasPython'sdottedreferences.WherePythonrequiresacoupleofdifferentthingstogetvalues,dottedreferencesprovideone-stopshoppinginDjango'stemplating.Whenareferenceto{{page.title}}occurs,itwilldisplaypage[u'title']ifpage[u'title']isavailable.Ifnot,itwilldisplaypage.titleifpage.titleisavailableasanattribute,andifthereisnosuchattribute,itwilldisplaypage.title()ifpage.title()isavailable,andfailingthat,ifthereferenceisanon-negativeintegerlike2,itwilldisplaypage[2]ifpage.2isrequested.Ifallofthesefail,thenDjangodefaultstotheemptystringbecauseit'snotacceptableforaprofessionalsitetocrashbecauseaprogrammingerrorhasthetemplateaskingforsomethingthatisnotavailable.YoucanoverridetheemptystringbysettingTEMPLATE_STRING_IF_INVALIDinyoursettings.pyfile,andindevelopmentitmaymakesensetosetTEMPLATE_STRING_IF_INVALIDtosomethinglikeLOOKUPFAILED,butyouwillwanttosetitbacktotheemptystringfordeploymentinanyproductionenvironment.Atthispoint,whilethereareexplicithookstopullinmultipleJavaScriptandCSSfiles,thepreferredpractice,perSteveSouders'sgroundrulesforclient-sideoptimizationsforhighperformancewebsites,is:foreachpagetoloadinitially,youshouldhaveoneHTML/XHTMLpage,oneCSSfileincludedatthetop,andoneJavaScriptfileincludedatthebottom.Thehooksaremoreflexiblethanthat,butthisisintendedmoreas"developmentleeway"thanwhatthetightenedfinalproductshouldbe.If-then,if-then-elsestatements,andforloopsarestraightforward,andelseclausesareoptional:{%ifresults%}
    {%forresultinresults%}
  • {%result.title%}
  • {%endfor%}
{%else%}

Therewerenoresults.

{%endif%}Thereareanumberofconveniencefeaturesandminorvariationsavailable;theseareseveralofthemajorfeatures.[31] jQueryandAjaxIntegrationinDjangoSettingJavaScriptandotherstaticcontentinplaceForthedevelopmentserver,puttingstaticcontent,includingimages,CSS,andstaticcontent,isstraightforward.Forproductionuse,therecommendedbestpracticeistouseadifferentimplementation,andDjangousersareadvisedtouseaseparateserverifpossible,optimizedforservingstaticmedia,suchasastripped-downbuildofApache,ornginx.However,fordevelopmentuse,thefollowingstepswillserveupstaticcontent:1.Createadirectorynamedstaticwithinyourproject.(Notethatothernamesmaybeused,butdonotusemedia,asthatcancollidewithadministrativetools.)2.Editthesettings.pyfile,andaddthefollowingatthetop,afterimportos:DIRNAME=os.path.abspath(os.path.dirname(__file__))3.ChangethesettingsofMEDIA_ROOTandMEDIA_URL:MEDIA_ROOT=os.path.join(DIRNAME,'static/')…MEDIA_URL='/static/'4.Attheendofthesettings.pyfile,addthefollowing:ifsettings.DEBUG:urlpatterns+=patterns('django.views.static',(r'^%s(?P.*)$'%(settings.MEDIA_URL[1:],),'serve',{'document_root':settings.MEDIA_ROOT,'show_indexes':True}),)ThiswillturnoffstaticmediaservicewhenDEBUGisturnedoff,sothatthiscodedoesnotneedtobechangedwhenyoursiteisdeployedlive,butthesubdirectorystaticwithinyourprojectshouldnowserveupstaticcontent,likeaverysimplifiedApache.Wesuggestthatyoucreatethreesubdirectoriesofstatic:static/css,static/images,andstatic/js,forservingupCSS,image,andJavaScriptstaticcontent.[32] Chapter1SummaryGuidovanRossum,thecreatorofthePythonprogramminglanguage,foroneprojectaskedaboutdifferentPythonframeworksandchosetheDjangotemplatingengineforhispurposes(http://www.artima.com/weblogs/viewpost.jsp?thread=146606).ThischapterhasprovidedanoverviewofAjaxandthenprovidedakickstartintroductiontotheDjangotemplatingengine.There'smoretoDjangothanitstemplatingengine,butthisshouldbeenoughtostartexploringandplaying.Inlearninganewtechnology,acrucialthresholdhasbeenpassedwhenthereisenoughofacriticalmassofthingsyoucandowithatechnologytobegintinkering,andtakingonestepofteninvitesthequestion:"Whatcanwedototakethisonestepfurther?".Inthischapter,wehaveprovidedakickstarttobeginworkingwiththeDjangotemplatingengine.Inthischapter,wehavelookedattheideaofPythonicproblemsolving,discussedDjangoandjQueryinrelationtoPythonicproblemsolving,anddiscussedAjaxasnotasingletechnology,butanoverlaidfunctionortechniquethatisoverlaidontopofexistingtechnologies.Wehavetakenanoverviewofwhatthe"usualsuspect"technologiesareforAjax;givenakickstarttotheDjangotemplatingengine,introducingsomeofitsbeautyandpower;andaddressedaminorbutimportantdetail:puttingstaticcontentinplaceforDjango'sdevelopmentserver.ThisismeanttoserveasapointofdepartureforfurtherdiscussionofjQueryAjaxinthenextchapter,andbuildingoursampleapplication.Interestedreaderswhowanttoknowmoreofwhattheycandocanreadtheofficialdocumentationathttp://docs.djangoproject.com/en/dev/topics/templates/.Inthenextchapter,wewillpushfurtherandaimforacriticalmassofthingswecando.WewillexplorejQuery,themostcommonJavaScriptlibrary,andbegintoseehowwecanuseittoreachthepointoftinkering,ofhavingsomethingthatworksandwondering,"Whatifwetrythis?","Whatifwetrythat?",andbeingabletodoit.[33] jQuery—theMostCommonJavaScriptFrameworkJavaScriptframeworkscanofferatleasttwokindsofadvantages,aswehavediscussedearlier.Firstly,theycanofferconsiderablefacilitieswhencomparedtobuildingfromscratch.TurboGearsandDjangoarebothserver-sidewebframeworksinPythonandbothoffermajoradvantagescomparedtowebapplicationdevelopmentinPythonusingonlythestandardlibrary,evenwiththeCGImoduleincluded.Secondly,theycanofferamoreuniformvirtualprogramminginterfacecomparedtowritingunadornedJavaScriptwithenoughdetectionandconditionallogictodirectlyconformtodisparateJavaScriptenvironmentssothatcodewritteninonebrowseranddebuggedinanotherbrowserstandsafightingchanceofworkingwithoutcrashingineverycommonbrowser.Asregardsthequestion,"Whichisbest?"ifyouonlylearnoneclient-sideJavaScriptframework,jQueryisanexcellentchoice.ItisoneofthemostpopularJavaScriptframeworks,partlybecauseitis(likePython)gentletonewcomers.However,comparingjQuerytoDojoislikecomparingahammertoawrench.(AgeneralJavaScriptframeworkcomparisonisathttp://en.wikipedia.org/wiki/Comparison_of_JavaScript_frameworks.)Unlikeserver-sidewebdevelopmentframeworkssuchasDjangoandTurboGears,youdon'tnecessarilyask,"Whichonewillweuseforthisproject?"astheremightbegoodreasonstousemorethanoneframeworkinawebapplication.jQuery'sdevelopersknowitisn'ttheonlythingoutthere.Itonlyusestwonamesintheglobalnamespace:$andjQuery.Bothdothesamething;youcanalwaysusejQuery()inlieuof$(),andifyoucalljQuery.noconflict(),jQuerywillletgoof$sothatotherlibrarieslikePrototype(http://www.prototypejs.org/)arefreetousethatname. jQuery—theMostCommonJavaScriptFrameworkFinally,wesuggestthatjQuerymightbesomethinglikea"virtualhigher-levellanguage"builtontopofJavaScript.Aprogrammer'sfirstreactionafterviewingtheJavaScriptcode,onseeingcodeusingjQuery,mightwellbe,"IsthatreallyJavaScript?Itdoesn'tlooklikeJavaScript!"AprogrammerusingjQuerycanoperateonelementsets,andrefineorexpand,withoutevertypingoutaJavaScriptvariableassignmentorkeywordsuchasif,else,orfor,andevenwithoutcross-browserconcerns,onerunsintojQuerycodethatdoestheworkofdozensoflinesof"bareJavaScript"inasingleline.Butthesearesinglelinesthatcanbeveryeasilyreadonceyouknowthegroundrules,notthecrypticone-linersofthePerlartform.This"virtualhigher-levellanguage"ofjQueryisnotPythonanddoesnotlooklikePython,butthereissomething"Pythonicinspirit"abouthowitworks.Inthischapter,wewillcover:•HowjQuerysimplifies"Hello,world!"inAjax•ThebasicAjaxfacilitiesjQueryoffers•HowjQueryeffectivelyoffersa"virtualhigher-levellanguage"•jQueryselectors,astheyillustratethekindofmorePythonic"virtualhigher-levellanguagefacilities"thatjQueryoffers•Asample"kickstartDjangoAjaxapplication,"whichcoverseverymajorbasicfeatureexceptforserver-sidepersistencemanagement•Anoverviewofamoreserious,in-depthDjangoapplication,tobecoveredinupcomingchaptersLet'sexplorethehigher-levelwaytodothingsinjQuery.jQueryandbasicAjaxLet'slookata"Hello,world!"inAjax.Arequestcontainingonevariable,text,issenttotheserver,andtheserverrespondswith"Hello,[contentsofthevariabletext]!"whichisthenputintotheinnerHTMLofaparagraphwithIDresult:if(typeofXMLHttpRequest=="undefined"){XMLHttpRequest=function(){try{returnnewActiveXObject("Msxml2.XMLHTTP.6.0");}catch(exception){[36] Chapter2try{returnnewActiveXObject("Msxml2.XMLHTTP.3.0");}catch(exception){try{returnnewActiveXObject("Msxml2.XMLHTTP");}catch(exception){thrownewError("CouldnotconstructXMLHttpRequest");}}}}}varxhr=newXMLHttpRequest();xhr.open("GET","/project/server.cgi?text=world");callback=function(){if(xhr.readyState==4&&xhr.status>=200&&xhr.status<300){document.getElementById("result").innerHTML=xhr.responseText;}}xhr.onreadystatechange=callback;xhr.send(null);Withthesimpler,"virtualhigh-levellanguage"ofjQuery,youaccomplishmoreorlessthesamewithonly:$("#result").load("/project/server.cgi","text=world");$()selectsawrappedset.ThiscanbeaCSSdesignation,asingleHTMLentityreferencedbyIDaswedohere,orsomeotherthings.jQueryworksonwrappedsets.Here,asiscommoninjQuery,wecreateawrappedsetandcallafunctiononit.$.load()loadsfromaserverURL,andwecanoptionallyspecifyaquerystring.HowdowedothingswithjQuery?Tostartoff,wecreateawrappedset.ThesyntaxisbasedonCSSandCSS2selectors,withsomeusefulextensions.[37] jQuery—theMostCommonJavaScriptFrameworkHerearesomeexamplesofcreatingawrappedset:$("#result");$("p");$("p.summary");$("a");$("pa");$("p>a");$("li>p");$("p.summary>a");$("table.stripedtr:even");TheyselectnodesfromtheDOMmoreorlessasonewouldexpect:•$("#result#")selectstheitemwithHTMLIDresult•$("p")selectsallparagraphelements•$("p.summary")selectsallparagraphelementswithclasssummary•$("a")selectsallanchortags•$("pa")selectsallanchortagsinaparagraph•$("p>a")selectsallanchortagswhoseimmediateparentisaparagraphtag•$("li>p")selectsallptagswhoseimmediateparentisalitag•$("p.summary>a")selectsallanchortagswhoseimmediateparentisaparagraphtagwithclasssummary•$("table.stripedtr:even")selectseven-numberedrowsfromalltablesbelongingtoclassstripedThedifferencebetween$("pa")and$("p>a")isthatthefirstselectsanytagcontainedinaptag,whetheritsimmediateparentistheptagorwhetherthereareinterveningspan,em,orstrongtags,andthesecondselectsonlythosetagswhoseimmediateparentisaptag;thelatteryieldsasubsetoftheformer.Thislastapproachlendsitselftoastraightforwardandcleanwaytoaddressthechoreoftiger-stripingtables:$("table.stripedtr:even").addClass("even");Thestatementsbeforethislastexamplearenotusefulinthemselves,buttheylayapowerfulfoundationbycreatingawrappedset.AwrappedsetisanobjectthatencapsulatesbothasetofDOMelementsandafullcomplementofoperations,suchas.addClass(),whichassignsaCSSclasstoallelementsfromawrappedset,or.load(),whichprovidedaone-lineAjaxsolutionearlier.[38] Chapter2Oneofthekeyfeaturesofawrappedsetisthattheseoperationsreturnthesamewrappedset,unlesstheyareoneofafewexceptions,suchasoperationsdesignedtochangethesetbyaddingorremovingmembers.Thismeansthatoperationscanbechained;forasomewhatartificialexample,ourcodetostripeitemsinatablecouldbeextendedtoslowlyhidingthem,waitingforasecond(specifiedas1000milliseconds),andthenshowingthemagain:$("table.stripedtr:even").addClass("even").hide("slow").delay(1000).show("slow");Inthisstatement,alloftheoperationsreturnthesamewrappedset.Dozensoffurtheroperationscouldbeappendedafterthe.show()callifdesired.Inchainingofoperations,theadditionalfunctionscanfunctionasseparate"virtualstatements".Thepreviouslineofcode,inpseudocode,couldbewritten:Selecteven-numberedtablerowsfromtableshavingtheclass"striped".Addtheclass"even"tothem.Slowlyhidethem.Wait1000milliseconds.Slowlyshowthem.Thefinalappearanceofthepageshouldbeasifmerely$("table.stripedtr:even").addClass("even");hadbeencalled.jQueryAjaxfacilitiesjQueryprovidesgoodfacilitiesbothforAjaxandotherJavaScriptusage.TheAPIisexcellentandcanbebookmarkedfromhttp://api.jquery.com/.Wewillbegoingthroughsomekeyfacilities,Ajaxandotherwise,andmakingcommentsonhowthesefacilitiesmightbestbeused.$.ajax()Themostfoundationalandlow-levelworkhorseinjQueryis$.ajax().IttakestheargumentsasshowninthefollowingSampleinvocation.Defaultvaluescanbesetwith$.ajaxSetup(),asdiscussedbelow:$.ajax({data:"surname=Smith&cartTotal=12.34",dataType:"text",error:function(XMLHttpRequest,textStatus,errorThrown){displayErrorMessage("Anerrorhasoccurred:"+textStatus);},success:function(data,textStatus,XMLHttpRequest){try{[39] jQuery—theMostCommonJavaScriptFrameworkupdatePage(JSON.parse(data));}catch(error){displayErrorMessage("Therewasanerrorupdatingyourshoppingcart.Pleasecallcustomerserviceat800-555-1212.");}},type:"POST",url:"/update-user"};Someofthefieldsthatcanbepassedaredescribedinthefollowingsections.contextThecontext,availableasthevariablethis,canbeusedtogiveaccesstovariablesincallbacks,eveniftheyhavefallenoutofscope.Thefollowingcodepromptsfortheuser'snameande-mailaddressandprovidestheminacontext,anonymously,sothatthedataareavailabletothecallbackfunctionbutareneverapartoftheglobalnamespace.$.ajax({success:function(data,textStatus,XMLHttpRequest){alert(this.name+",youremailaddressis"+this.email+".");processData(data,this.name,this.email);},context:{name:prompt("Whatisyourname?",""),email:prompt("Whatisyouremailaddress?","")},…});ClosuresTheprincipleatplayistheprincipleofclosures.Thewrongway,perhaps,tolearnaboutclosuresistolookforacademic-styleintroductionstowhatyouneedtoknowinordertounderstandthem.There'salotthere,andit'salothardertounderstandthanlearningbyjumpingin.ClosuresrepresentakeyconceptincoreJavaScript,alongwithotherthingssuchasfunctions,objects,andprototypes.Closuresarepartoftheconceptuallandscapeandnotonlybecausetheyarethebasisonhowtoeffectivelycreateanobjectwithprivatefields.Sowewilljumpinandgiveaclosurethatservesasaproofofconceptas,effectively,anobjectwithprivatefields.Togiveastandard,"bareJavaScript"exampleofusingaclosuretocreateanobjectwithprivatevariables,whichcreatesanobjectthatstoresanintegervalue,hasagetterandsetter,butinJavafashionensuresthatthefieldcanonlyhaveanintegervalue.[40] Chapter2Wedefineafunction,whichwewillbeimmediatelyevaluating;closureExamplestoresnottheanonymousfunctionbutitsreturnvalue.Thevariablefieldisalocalvariable:varclosure_example=function(){varfield=0;return{ThegetterisanunadornedgetterasinJava:get:function(){returnfield;},Thesettercouldbeanunadornedsetter,storingnewValueinafieldanddoingnothingelse.However,weprovideamorediscriminatingbehavior:wemakeastringoftheobjectandthenseeifitcanbeparsedasaninteger.ThiswillbeanintegerifweobtainanintegerandNaN(NotaNumber)ifitcannotbeparsedasaninteger.Ifthevalueisnotanumber,thenwereturnfalse;ifitgivesusaninteger,thenwestoretheintegerandreturntrue:set:function(newValue){varvalue=parseInt(newValue.toString());if(isNaN(value)){returnfalse;}else{field=value;returntrue;}}}}();Thiscreatesandevaluatesananonymousfunction.Localvariables(inthiscase,field)donotentertheglobalnamespacebutalsoremainaround,lurking,accessibletotheobjectreturnedanditstwomembers:functionsthatcanstillaccessfield.Aslongastheobjectstoredinclosure_exampleisavailable,itsmemberswillhaveaccesstoitslocalvariables,whicharenotgoneandgarbagecollecteduntiltheobjectinclosure_exampleitselfisgoneandeligibleforgarbagecollection.[41] jQuery—theMostCommonJavaScriptFrameworkWecanuseitasfollows:closure_example.set(3);varretrieved=closure_example.get();closure_example.set(1.2);varassignment_was_successful=closure_example.set(1.2)PrototypesandprototypalinheritancePrototypesandprototypalinheritanceareabasisforobject-orientedprogramming,withinheritance,butwithoutclasses.InJava,certainfeaturesofaclassarefixed.InPython,anobjectinheritsfromaclassbutfeaturesthatarefixedinJavacannotbeoverridden.InJavaScript,wegoonestepfurtherandsaythatobjectsinheritfromotherobjects.Class-basedobjectsruninaPlatonicfashion,wherethereisanidealtypeandconcreteshadowsorcopiesofthatidealtype.Prototypalinheritanceismoreliketheevolutionarypictureofsingle-celledorganismsthatcanmutate,reproduceasexuallybycelldivision,andpasson(accumulated)mutationswhentheydivide.InJavaScript,anobject'sprototypeissetby,forinstance:customer.prototype=employee;Redefinemembersforacustomerwhenonewantstochangefromtheattributesofanemployee.(Membersthatarenotredefineddefaulttotheprototype'svalues,andifnotfoundontheprototype,defaulttomembersontheprototype'sprototype,goingallthewayupthechaintotheobjectifneedbe.)Alsonotethatthisinheritancemayormaynotmeanmovingfrommoregeneraltomorespecific;inheritancemaybetterbeseenas"customermutatesfromemployee"than"customerisamorespecifictypeofemployee."Letusreturntotheparametersof$.ajax().dataThisistheformdatatopass,whethergivenasastringasitwouldappearinaGETURI,asfollows:query=pizza&page=2orgivenasadictionary,asfollows:{page:2,query:"pizza"}[42] Chapter2dataFilterThisisareferencetoafunctionthatwillbepassedtwoarguments:therawresponsegivenbyanXMLHttpRequest,andatype(xml,json,script,orhtml).Thisoffersanimportantsecurityhook:JSONcanbepassedtoaneval(),butmaliciousJavaScriptthatispassedinplaceofJSONdatacanalsobepassedtoaneval().Oneofthecardinalrulesofsecurityonboththeclient-sideandserver-sideistotreatallinputasguiltyuntilproveninnocentofbeingmalicious.Eventhoughitisa"doublework"chore,userinputshouldbothbevalidatedattheclient-sideandtheserver-side:client-sideasacourtesytotheusertoimprovetheuserexperienceandnotastrustworthysecurity,andontheserversideasasecuritymeasureagainstmaliciousdataevenifitismalformedmaliciousdatathatnonormalwebbrowserwouldsend.WemightcommentthattheDjangoprincipleofDon'tRepeatYourself(DRY),isamajorDjangosellingpointbutisnotareasontododgehandlingboththeuserinterfacesideofvalidation,andthesecurityside.Inpractice,thismeansbothvalidatingfromDjangoontheserver-sideandJavaScriptontheclient-side,forwhatwearedoing.NeedlesslyrepeatingyourselfinDjangoisasignofbadcode,butthisisnotneedlessrepetition,evenifitisachore.It'sneededrepetition.jQuerydoesnotautomaticallyincludefunctionalitytotestwhethersomethingservedupasJSONismalicious,andmethodslike.getJSON()trustinglyexecutewhatmightcontainmaliciousJavaScript.OneseriousalternativeistheJSON.parse()methoddefinedinhttp://www.json.org/json2.js.ItwillreturnaparsedobjectorthrowaSyntaxErrorifitfindssomethingsuspicious.dataTypeThisissomethingyoushouldspecify,andprovideadefaultspecifiedvaluevia$.ajaxSetup(),forexample:$.ajaxSetup({dataType:"text"});Thepossiblevaluesarehtml,json,jsonp,script,text,andxml.Ifyoudonotspecifyavalue,jQuerywilluseanunsecure"intelligentguessing"thatmaytrustinglyexecuteanyJavaScriptorJSONitispassedwithoutanyattempttodetermineifitismalicious.IfyouspecifyadataTypeoftext,yourcallbackwillbegiventherawtextwhichyoucanthentestcallJSON.parse()on.IfyouspecifyadataTypeofjson,youwillgettheparsedJSONobject.SospecifytextevenifyouknowyouwantJSON.[43] jQuery—theMostCommonJavaScriptFrameworkerror(XMLHttpRequest,textStatus,errorThrown)Anerrorcallbackthattakesuptothreearguments.Forexample:$.ajax({error:function(XMLHttpRequest,textStatus,errorThrown){registerError(textStatus);},…});success(data,textStatus,XMLHttpRequest)Acallbackforsuccessthatalsotakesuptothreearguments,butindifferentorder.Forexample:$.ajax({success:function(data,textStatus,XMLHttpRequest){processData(data);},…});Ifyouwanttodosomethingimmediatelyaftertherequesthascompleted,therecommendedbestpracticeistospecifyacallbackfunctionthatwillpickupaftertherequesthascompleted.Ifyouwanttomakecertainvariablesavailabletothecallbackfunctionthatwouldnototherwiseremainavailable,youmayspecifythecontextasdiscussedabove.typeThetypeoftherequest,forexampleGET,POST,andsoon.ThedefaultisGET,butthisisonlyappropriateinverylimitedcasesanditmaymakesensetosimplyalwaysusePOST.Themoreserioustheworkyouaredoing,themorelikelyitisthatyoushouldonlyusePOST.GETisappropriateonlywhenitdoesn'tmatterhowmanyextratimesaformissubmitted.Inotherwords,GETisonlyappropriatewhentherearenosignificantsideeffects,andinparticularnodestructivesideeffects.Inashoppingcartapplication,GETmaybeappropriatetoviewthecontentsofashoppingcart,althoughPOSTisalsoappropriateandhasthesideeffectofguaranteeingafreshload.GETisnot,however,appropriateforcreating,modifying,fulfilling,ordeletinganorder,andyoushouldusePOSTwhenanyoralloftheseareinvolved.urlTheURLtosubmitto;thisdefaultstothecurrentpage.[44] Chapter2$.aj0axSetup()Thistakesthesameargumentsas$.ajax().Allargumentsareoptional,butyoucanusethistospecifydefaultvalues.IntermsofDRY,ifsomethingcanbeappropriatelyoffloadedto$.ajaxSetup(),itprobablyshouldbeoffloadedto$.ajaxSetup().SampleinvocationAsampleinvocationisasfollows:$.ajaxSetup({dataType:"text",type:"POST"});$.get()and$.post()Theseareconveniencemethodsfor$.ajax()thatallowyoutospecifycommonlyusedparameterswithoutkey-valuehashsyntaxandareintendedtosimplifyGETandPOSToperations.Theybothhavethesamesignature.Thesampleinvocationisasfollows:$.get("/resources/update");$.post("/resources/update");$.post("/resources/update","user=jsmith&product_id=112");$.post("/resources/update",{user:"jsmith",product_id:112});$.post("/resources/update",function(data){("#result").html(data)});$.get("/resources/update","user=jsmith&product_id=112",function(data,textStatus,XMLHttpRequest){("#result").html(data);logStatus(textStatus);});$.post("/resources/update","user=jsmith&product_id=112",function(data,textStatus,XMLHttpRequest){("#result").html(data);logStatus(textStatus);},"text");Theseareconveniencemethodsfor$.ajax()andmakeseveralcommonfeaturesfrom$.ajax()available,butnotablynotanerrorcallbackfunction.Ifyouwantacallbackcalledinthecaseofanerrorforappropriatehandling,aswellaswheneverythinggoesperfectlywell,registeraglobalerrorhandleroruse$.ajax().Fortherealworld,thisisasubstantialendorsementofnotusingtheconveniencemethodsalone,buteitherspecifyingaglobalerrorhandlerorusing$.ajax().[45] jQuery—theMostCommonJavaScriptFrameworkSomethingcangowronginfrontofyourboss,orworse,workperfectlywhenyoushowyourbossandthenproceedtoblowupcompletelywhenyourbossshowsittoyourcustomer.Heisenbugs,(subtleandhard-to-pin-downbugsthatjustshowupundercircumstancesthataredifficulttorepeat),networkerrors,andservererrorswilloccur,andunlessanoopresponseisappropriateandproduction-readybehaviorwhenanerrorpreventssuccessfulcompletion,youwillwanttospecifyanappropriateerrorcallback.Or,alternatively,itmaybeacceptabletospecifyaglobalerrorhandlerusing$.ajaxSetup({error:myErrorHandler})ifyoucanwritesomethingappropriatetogenericallybutcorrectlyhandleallAjaxerrorconditionswhereyoudidnotdirectlycall$.ajax()andspecifyanerrorhandler.Thisincludesallcallsto$.get(),$.load(),and$.post()..load()Thisisaveryconvenientconveniencemethod.Ifyou'relookingforasimple,higher-levelalternativeto$.ajax(),youcancallthismethodonawrappedsettoloadtheresultsofanAjaxcallintoit.Therearesomecaveats,however.Let'sgivesomesampleinvocations,lookatwhy.load()isattractive,andthengiveappropriatequalificationsandwarnings.SampleinvocationsThesampleinvocationisasfollows:$("#messages").load("/sitewide-messages");$("#messages").load("/user-messages","username=jsmith");$("#hidden").load("/user-customizations","username=jsmith",function(responseText,textStatus,XMLHttpRequest){performUserCustomizations(responseText);});Onthesurface,thislookslikeagoodexampleofanalternativetodoingJavaScriptworkthatincorporatesjQuery,andinsteadusingthe"virtualhigher-levellanguage"thatjQueryprovides..load(),likemanyjQueryfunctions,isafunctionofawrappedsetandreturnsawrappedset(asoften,thewrappedsetitwasgiven).Itis,therefore,inprinciplesomethingthatcanbeputinachain.Why"inprinciple"?Itmakessense,uponsomecondition,tohidealloftheparagraphsinaformcontainingacheckboxthatisnotchecked:$("formp:has(input[type=checkbox]:not(:checked))").hide("slow");Thissays,"Forallpelementsinaformthathaveanuncheckedcheckbox,slowlyhidethem."[46] Chapter2Furthermore,onecouldwanttodoseveralactionsinsteadofone:$("formp:has(input[type=checkbox]:not(:checked))").addClass("strikethru").delay(500).hide("slow");Thatisamoreelaborateanimation:"Forallpelementsinaformthathaveanuncheckedcheckbox,addtheclassstrikethru(whichcanbedefinedinCSStohaveastrikethroughlinethroughtext),thenwaithalfasecond(500milliseconds),andthenslowlyhideitaswasdonebefore."Butforawrappedsetlikethis,itwouldnotmakemuchsensetoinserta.load()afterthewrappedsetisgenerated:$("formp:has(input[type=checkbox]:not(:checked))").load("/updates");Whatthatsaysis,"Forallpelementsinaformthathaveanuncheckedbox,loadthecontentsoftherelativeURL/updatesandreplacewhatevertheselectedpelementscontainwithwhatwasloaded."That'slegal,butit'snotasclearwhysomeonewouldwanttodothis.Ordinarily,ifyouaregoingtoloadsomethingfromAjaxtoaddtothewebpage,itwillmakesensetoinsertitinoneplaceandnoteveryelementinamulti-elementwrappedset.Sothepossibilityofcalling.load()onanywrappedset,insteadofasingleDOMelementascanbeencapsulatedinawrappedsetlike$.("#results"),isnotobviouslysuchaterriblygreatimprovement.Furthermore,.load()willreturnimmediately,notwhenthingsareloaded,soitemsfollowingitinthechainshouldnotbeassumedtohavethedataloaded.Buttheyalsoshouldnotbeassumednottohavethedataloaded;wehavearacecondition,wnloadfromWow!eBookandweshouldonlychainotheritemsafter.load()whenraceconditionsabouttheoDorderofexecutiondonotmatter.Sothefactthatwecanchainotheroperationsafter.load(),whichisarguablythegloryofjQuery,doesnotmeanthatitisalwayswisetodoso.Anotherpointtobemadeisasabove:.load(),like$.get()and$.post(),effectivelyforcesanooperrorhandler,andthereforeshouldbeusedonlywhenitisacceptablefornothingtobedonewhenanyofanumberofthingsthatcangowrong,dogowrong.Theremaybesomecaseswhereanoopisthebesterrorhandler,butusuallybestpracticesinAjaxaretogivesomefeedbackthatsomethinghasgonewrong.Conveniencemethodsexist,butwerecommendusing$.ajax()becauseitprovidescallbackfacilitiesforerrorsituationsaswellasforsuccess,orwritinganappropriategeneric,all-purposeerrorcallbacktogiveto$.ajaxSetup()orequivalent(thereareotheralternatives,including$.ajaxError()).Thebestpracticesaretouseaconveniencemethodinconjunctionwithaglobalerrorhandler,whichcanbemademoresophisticatedbyexaminingtheinformationinitsargumentstotellwhatwentwrong.[47] jQuery—theMostCommonJavaScriptFrameworkjQueryasavirtualhigher-levellanguageTherearesomeotherbasicfunctionsthatwehaveseeninjQuerybesidesdirectAjax.Oneexampleofotherkindsoffunctionsincludesselectors.TheselectorsSelectorscreateawrappedset.Someexamplesofselectorsinclude:•$("*"):SelectsallDOMelements.•$(":animated"):Selectsallelementsthatareintheprocessofananimationatthetimetheselectoriscalled.•$("id|=header"):Selectsallelementswithattributeid(inthisillustration),whosecontentmatchesheaderorheader-*,likeheader-image.Inthiscase,thiswouldbeanelementID.Thisandfollowingselectorsmatchingtextarecasesensitive,meaningthatanIDof"HEADER"oreven"Header"wouldnotbematched.•$("value*=import"):Selectsallelementswithattributevaluecontainingthestringimport.Thiswouldincludebothstringslike"Thenimportthefollowingclass."and"Thisisimportant."•$("value~=import"):Matcheselementshavingavaluethatcontainsthestringimport,butisdelimitedbyspaces.Thiswouldinclude"Thenweimportthefollowingclass."butexclude"Thisisimportant."•$("id$=wrapper"):MatchesallelementshavinganIDthatendswithwrapper.ThiswouldincludeanIDof"comment-wrapper"aswellaswrapper.•$("http-equiv=refresh"):Matchesallelementswherethehttp-equivattributeexactlyequalsrefresh.•$("id!=result"):MatchesallelementshavingIDs,wheretheIDdoesnotequalresult.ThiswillnotmatchelementsthatdonothaveIDs.•$("class^=main"):Matchesallelementshavingaclassthatbeginswithmain.•$(":button"):Selectsallbuttons,whetherbuttonelementsdirectlyorinputsoftypebutton.•$(":checkbox"):Selectsallinputsoftypecheckbox.•$(":checked"):Selectsallcheckedinputs.•$(":contains('important')"):Selectsallelementscontainingthetextimportant.•$(":disabled"):Selectsallinputsthataredisabled.[48] Chapter2•$(":empty"):Selectallelementsthathavenochildren,notevenatextnode.Thiswouldincludeanodefrom"or"

",butnot"

Hello.

"ortheouterelementof"

.•$(":enabled"):Selectsallelementsthatareenabled.•$("p").eq(0):Thisgivestwoexamples.First,alltagsofanamemaybefoundbycallingtheirtagname;hence$("li")returnsalllielements(regardlessofthecaseinthesourceHTML).The.eq()functionperformszero-basedarrayindexingontheset.ItisconceptuallylikehowaJavaScript$("p")[0].$("p").eq(0)returnsawrappedsetcontainingoneitem:thefirstpelement.•$(":even"):Selectsalleven-numberedelements,butzero-based.Counter-intuitively,$("table#directorytr:even"),whichsays"TakethetablewithIDdirectoryandreturnalleven-numberedtrelementsfromit,"willreturnthefirst,third,fifth,andsoon,trelementsfromthetableifthetablecontainsatleastfivetablerows.•$(":file"):Selectsallinputsoftypefile.•$(":first-child"):Selectsallelementsthatarethefirstchildoftheirparent.Forexample:Welcometoourcommunity!

Welcometoourcommunity!Weoffer:

  • Experiencedleadership.
  • Well-furnishedfacilities.
  • Goodneighbors.
•$(":first-child")willreturntheheadelement,thelinkelement,thepelement,andthefirstlielement.NotethatthisdoesnotnoisilyandlegalisticallyincludeeveryDOMtextelement.Thetextnodecontaining"Goodneighbors."istechnicallythefirstchildofitsliparent,but$(":first-child")returnsamoreuseful,andconceptuallycleanertouse,versionofthefirstchild.[49] jQuery—theMostCommonJavaScriptFramework•.(":gt(2)")willreturnallelementswith(zero-based)indexgreaterthantwointhewrappedset.$("p").(":gt(2)")willreturnthefourthandsubsequentpelements,oranemptywrappedsetiftherearenotatleastfourpelements.•$(":has(a)"):Returnsallelementscontainingananchor.$("p:has(a)")returnsallpelementscontaininganaelement.•$(":header"):Returnsallh1,h2,h3,andsoon,elements.•$(":hidden"):Returnsallelementsthatarehidden.•$(":image"):Returnsallimages.•$(":input"):Returnsallbuttons,inputs,selects,andtextareas.•$(":last-child"):Like$("first-child"),butselectsthelastinsteadoffirstelement.•.(":last"):Returnsthelastelementinawrappedsetifthatsetisnon-empty;thePythonequivalentto$("p").(":last")wouldbeparagraphs[-1].•.(":lt"):Like.(":gt"),butselectselementslessthanthe(zero-based)index.$("p").(":lt(2)")wouldreturnthefirsttwooutofanypelements.•.(":not(pa)"):$("p:not(pa)")wouldreturnamatchedsetofallparagraphsthatdonotcontainanaelement.•$(":nth-child(3n)"):Wouldreturneveryelementthatistheone-basedthirdchildofitsparent.Aswellas$(":nth-child(3n)"),$(":nth-child(4n)"),andsoon,beingallowed,thereisamoreintuitive,one-based$(":nth-child(even)")and$(":nth-child(odd)"),whichmorepredictablyhaveoddnthchildrenstartingwiththefirstchildandevenwiththesecond.Thereasonforthisinconsistencyishistorical:otherelementsarezero-basedinfollowingJavaScript'sandotherlanguages'wideprecedent,whilethisoptionisone-basedinstrictlyfollowingtheCSSspecification.•$(":odd"):Aselectorthatiscounter-intuitivelyzero-based.$("p:odd")returnsthesecond,fourth,sixth,andsoon,paragraphelementsifavailable.•$(":only-child"):Selectsallelementsthataretheonlychildoftheirparent.•$(":parent"):Selectsallnodesthatareparentsofothernodes,includingtextnodes.(Wouldreturnthepelementfor

Hello,world!

.)•$(":password"):Selectsallinputsoftypepassword.•$(":radio"):Selectsallinputsoftyperadio.[50] Chapter2•$(":reset"):Selectsallinputsoftypereset.•$(":selected"):Returnsallelementsthatareselected.•$(":submit"):Returnsallinputsoftypesubmit.•$(":text"):Returnsallinputsoftypetext.•$(":visible"):Returnsallelementsthatarevisible.Thereareseveralremarkstobemadehere.Thefirstisthattheselectorsintheseexamplesare(usually)givenalone,butthisislikewordsbeinglistedaloneinanold-fashionedpaperdictionary.InEnglish,thereareafewthingsyoucansaywithasingleword,like"Stop!"butusuallyyousaythingsbycombiningthem,aswehavedoneforafewexamples.Theseselectorsarepowerfulbythemselves,buttheyaremorepowerfulwhenviewedaswordsthatmakeupsentenceslike$("div.productul:nth-child(odd)"),whichreturnsthefirst,third,fifth,andsoon,lielementsineachunorderedlistcontainedinadivofclassproduct,suchasonewouldusefortiger-stripingunorderedlists.Manyoftheseselectorsareusefulbythemselves,buttheyareintended,likepipableUnixcommand-linetools,toworkwelltogetherandtobeassembledlikeLegobricks.OneofthefirstremarksonemightmaketosomeonelearningPerlis,"YouarenotreallythinkingPerluntilyouarethinkingdictionaries/hashes/associativearrays."IfyouaresolvingproblemsliketheydoinaCclass,fromthebasicdatatypessuchasint,long[],char**,andvoid*,thenyouaremissingoneofthemostimportantworkhorsesPerlhastooffer(andPython,forthatmatter).Andinsimilarfashion,youarenotreallythinkingjQueryuntilyouarethinkingwrappedsetsascreatedbyselectorsliketheexamplesabove.It'safoundationalpartofidiomaticuseofthe"virtualhigher-levellanguage"jQueryoffers.Insomeotherlibraries,andinunadornedJavaScript,youoperateontheDOMoneelementatatime.Ifyouwanttooperateonseveralelements,youstilloperateonthemoneatatime,butyoudothisseveraltimesinsequence.However,thebasicunitofworkinjQueryisthewrappedset;itmaybeawrappedsetofone,suchasonemaycreatebycalling$("#main:first-child")or$("div").eq(0),oronemayendupgettingitbycalling$("h2")whentheDOMcontainsexactlyoneh2header.However,eventhenitismissingsomethingaboutjQuerytothinkofthesetassimplyawrapperforanisolated,unitaryelement.Thelistoftheselectorsaboveissomethinglikeapaperdictionaryofnouns.Wehaven'tyetdiscussedtheverbs,orhowtoputthemtogetherinspeech.Let'sexploreoneexampleofincludingjQueryinsimpleDjangoAjax.ThisexampledoesnotdemonstratejQuery'spowerandeleganceyet.Thatispartofthegoalinsubsequentchapters,astheyshowDjangoAjaxputtogetherusingjQuery.[51] jQuery—theMostCommonJavaScriptFrameworkAclosure-basedexampletomeasureclockskewLet'sputsomethingstogetherinmakingasimpleAjaxexample.Wewillloadawebpagethatwillmeasurehowlongittakestomakearequestfromtheserver,andgivenarequestthatgivesthetimeontheserver,estimateclockskewbetweentheserverandtheclient.TheserverwillgiveitsanswerinJSON,andwewilldosomeDOMmanipulations:wewillusejQueryratherthananinlineonclick-styleattributetoregisterwiththebutton'sclickevent,andwewillupdatetheDOMwithouteverusinginnerHTML.And,forgoodmeasure,wewilluseaclosuretomakeoneincursionintotheglobalnamespace,sothatifourcodeorsomeextensionofitisreused,itwillnotoverwriteotherglobalvariables.First,letusmakethetemplate,forthesakeofdiscussionstartingfrombase.htmlasdefinedattheendofthelastchapter.Wewillincludeitinthesamedirectoryasbase.html,savedasclockskew.html:{%extends"base.html"%}{%blocktitle%}MeasureClockSkew{%endblocktitle%}{%blockbody_header_title%}MeasureClockSkew{%endblockbody_header_title%}{%blockhead_css_page%}{%endblockhead_css_page%}{%blockfooter_javascript_page%}{%endblockfooter_javascript_page%}{%blockbody_content%}MeasureClockSkew{%endblockbody_content%}[52] Chapter2Nowthispagecouldstandtoberefactoredonacoupleofgrounds.Firstly,itincludesaspage-specific/static/js/json2.js,whichasalibraryforsaferparsingofJavaScriptpresentedasJSONshouldprobablybeincludedsitewide,ortogoonestepfurther,itshouldbeconcatenatedwith/static/js/jquery.jsatleastforsitewidedeployment,andanyothersitewideincludes,sothatonlyoneJavaScriptHTTPrequestslowsthingsdown.(AsfarasHTTPrequestsgo,a2KBdownloadplusanother2KBdownload,especiallyiftheyare2KBJavaScriptdownloads,adduptomoreslownessinpagerenderingthanone4KBdownload.)Secondly,oneofthemajorprinciplesofDjangoisDRY,andthefactthatthispagerepeats"MeasureClockSkew"threetimesisaninvitationforrefactoring.Thisexampleandwhatfollowsdeliberatelyhaveroomleftforrefinements.(Canyouspotanyfurtherimprovementstomake?)Wewillalsodefineacoupleofmodels,onetoserveupthistemplateandonetoserveJSON,adaptandextendtheurls.pyfile,andwritetheAjaxtoaddbehaviortothepage.HereistheJavaScriptclock_skew.jsfile:varMeasureClockSkew=function(){varthat=this;varlastButtonPress=newDate().getTime();varregisterError=function(XMLHttpRequest,textStatus,errorThrown){$("#results").append("Error:"+textStatus+"");$("#button").removeAttr("disabled");};varregisterSuccess=function(data,textStatus,XMLHttpRequest){try{varremote=JSON.parse(data).time;varhalfway=(lastButtonPress+newDate().getTime())/2;varskew=(remote-halfway)/1000;$("#results").append("Estimatedclockskew:"+skew+"seconds.");}catch(error){$("#results").append("ErrorparsingJSON.");}[53] jQuery—theMostCommonJavaScriptFramework$("#button").removeAttr("disabled");};varbuttonPress=function(){lastButtonPress=newDate().getTime();$("#button").attr("disabled","disabled");$.ajax({data:"",dataType:"text",error:registerError,success:registerSuccess,type:"POST",url:"/time/json"});};return{buttonPress:buttonPress}}();$("#button").click(MeasureClockSkew.buttonPress);Beforegoingfurther,wewouldliketomakeafewcommentsaboutdoublesubmissionandraceconditions.Ifourtestscriptisdeployedliveonafarawaywebserver,withnetlagwecouldfairlyeasilyclickthebuttontwicebeforetheresponsecameback,andthenitwouldcalculateinvaliddata.Theobject,asameansofaccountingfornetlag,estimatesthattheservergaveitstimestamphalfwaybetweenwhenwesubmittedtheclickandwhenwereceivedit,andifweclickedabuttontwicebeforetheresponsecameback,inadditiontoanyinaccuraciesinthisestimation,itwouldcombinethetimethesecondclickwasmadeandwhenthefirstclick'sresponsecameback,makingthecalculationcorrupt.Thereareotherwaysthiscouldhavebeendealtwith;wecouldmakeaclosurewithinaclosurethatwouldcreateaseparateobjectforeachclickandallowoverlappingtrialswitheachendtimematchedtothecorrespondingstarttime.Butherewehavefollowedamuchmoregenerallyapplicablepatternfromthee-commerceworld,whichistodisablethesubmitbuttonsotheusershouldnotbeabletogenerateoverlappingclicks.Thisisanotherareawheredoingthingsright,evenifitmeansdoingdoublework,meansthatontheclientsideyoupreventasecondsubmissionwhenthatwouldnotbeinyourvisitor'sbestinterests(youdon'twantyourcustomerschargedtwicebecausetheygotimpatientandpressedthebuttonagain),andontheserversideyoualsotakeactionstopreventundesirableformsofdoublesubmission(remember,notallvisitorshaveJavaScriptenabled).Furthermore,thereisoneotherbestpracticeworthmentioning:thisisavailableonlywhenanobjectisbeingconstructed;butwecansaveareferenceasthat.[54] Chapter2ADjangomodelisaclassthatcorrespondstoatableinadatabaseinDjango'sobject-relationalmapping.ADjangomodelinstancecorrespondstoatablerow.Thedivisionoflabor,orseparationofconcerns,isnotexactlyMVC,ormodel-view-controller,butMTV,model-template-view,wherethemodelisaclassofobjectthatcorrespondstoatableinthedatabase,atemplateisadesigner-editablecomponentthatgetsmostHTMLoutofPythoncode,andaviewiswhatrendersatemplateorotherwisegeneratesaloadedpage.WewillcreatetwoDjangoviewmethodstoservethingsupontheserverside.Oneislikewhatwehaveseenbefore,andanotherisnewbutinprincipleself-explanatory.AviewthatservesupJSONisasfollows:#!/usr/bin/python/importjsonimporttimefromdjango.coreimportserializersfromdjango.httpimportHttpResponsefromdjango.shortcutsimportrender_to_responsedefhome(request):returnrender_to_response(u'clock_skew.html')deftimestamp(request):returnHttpResponse(json.dumps({u'time':1000*time.time()}),mimetype=u'application/json')Wesavebothviewsinclock_skew.py,andthenediturls.py,sothatafter(r'^$','sample.views.home'),wealsohave:(r'^time$','sample.clock_skew.home'),(r'^time/json$','sample.clock_skew.timestamp'),Thisisabriefnutshellexampleofmany,butnotall,ofthekindsoffeatureswewilluseinourmorein-depthcasestudy.Theastutereadermayhavenoticedthatthisbriefmicrocosmdoesnothavetheserverstoringinformationandsavingthestatefortheuserlater.However,thisdoesprovideAjax,jQuery,JSON,andsomeofthemostfoundationalfeaturesthatDjangooffers.Aswemoveonwewilltakethesefeatures,incorporateDjangomodelsanddatabaseusage,andmoveintoamorecomplexandmoresophisticatedusageofthefeaturesthatarepresentedinthisbriefexample.[55] jQuery—theMostCommonJavaScriptFrameworkCasestudy:Amorein-depthapplicationInthefollowingchapters,wewillbuildaDjangoAjaxwebapplicationandusejQuery.Thewebapplicationwillbemeanttoworkasacompany'sintranetemployeephotodirectory,andwehopetoputyouinapositionbothtouseourmodelapplicationandcustomizeittoyourcompany'sspecificneeds.Inthistestapplication,wewilldemonstratebothbasicfeaturesandbestpracticesinputtingtogetherawebapplicationusingourcoretechnologies.Thechaptersinourcasestudywillincludethefollowingsections.Chapter3:ValidatingFormInputontheServerSideInthischapter,wewillsendanAjaxrequesttotheserverviajQuery,andvalidateitontheserversidebasedontheprinciplethatallinputisguiltyuntilproveninnocentofbeingmalicious,malformed,incomplete,orotherwiseinvalid.Wewilllookatstandardservervalidationapproachesinlightofusabilitypracticesandlookforimprovement.Chapter4:Server-sideDatabaseSearchwithAjaxWewillcomparethemeritsofserver-sideandclient-sidesearching,andlookatthelimitationsofJavaScript,bothintermsoflanguagelimitations,andintermsofclient-sideperformanceissuesassociatedwithdoingtoomuchintheclient.Wewilllookbothatthemeritsofhandlingsearchingandotherbackendfunctionswiththefullpowerofabackendenvironment,andexplorewhy,ontheclientside,weshouldworkhardtobeaslazyaspossibleindoingnetwork-relatedwork.Wemightclarifythat"lazy"heredoesnotspecificallyrefertotheprogrammer'svirtueofaproactivelazinessthattriestosolveaproblemonce,correctly,ratherthandashoffabadsolutionandthenspendalotoftimecleaningupafterasuboptimalsolution.Thatisworthencouraging,butitisnotwhatwearetalkingabouthere."Lazy"refers,forinstance,toPython'sxrange(),ageneratorwhichyieldsintegersoneatatimeastheyarerequested,ratherthanPython'srange(),thatbuildsacompletearrayimmediatelyandtakessignificantlymorememoryforlargeranges.[56] Chapter2Inourcase,"lazy"refersinparticulartonothavingtheclienttrytoanticipateuserneedsbyfetchingwhatmightormightnotbeneededbeforehand,butrequestingtheminimumnecessarytomeetuserrequests,onlywhenrequested.Wewillbeexploringseveralapproaches;"lazy"isoneapproachthatweshoulddefinitelyknowaboutandbeabletouse.Chapter5:SigningUpandLoggingintoaWebsiteUsingAjaxThischapterwillintroduceDjangoauthenticationfacilitiesandaccountmanagement,andexplorehowwecanattractivelyhandletheclientsideofauthenticationthroughAjaxclient-sidecommunicationwiththeserverandcorrespondingAjaxclient-sideupdates.Chapter6:jQueryIn-placeEditingUsingAjaxInthischapter,wewillshowawaytousejQuerytomakeanin-placereplacementofatablethatallowsin-placeediting,whichcommunicateswiththeserverinthebackground,addingpersistencetochanges.Chapter7:UsingjQueryUIAutocompleteinDjangoTemplatesWewilldiscussjQuery'sbasicintentionashavingausefulcorethat'sdesignedtoinvitepluginstothepointofitnotbeinguncommonforprogrammerstowritepluginstheirfirstdaydoingjQuery.WewilluseautocompletefromthejQueryUI.WewillthenintegratethisintoDjangotemplatesandourproject'suserinterface.Chapter8:DjangoModelForm:aCSSMakeoverDjangocomeswithastraightforwardwaytoeasilybuildformsfromDjangomodels.Wewillexplorethisfeatureandhowtouseit.Chapter9:DatabaseandSearchHandlingInthischapter,wewillbeshowing"lazy"bestpracticesindevelopingourapplication.[57] jQuery—theMostCommonJavaScriptFrameworkChapter10:TinkeringAround:Bugfixes,FriendlierPasswordInput,andaDirectoryThatTellsLocalTimeIfyouareinterestedinhavinganemployeephotodirectoryforyourintranet,wenotonlywanttoprovideanapplicationbutalsohelpyouhaveastartingpointtocreateacustomizedapplicationaroundyourcompany'sneeds.Chapter11:UsabilityforHackersIfyouarereadingthisbook,youmayhavesomesurprisingstrengthsforusability.Thischapterexploresthem.Withthischapterwetakeastepbackfromourapplicationandtakealookatusabilityandthebedrockcompetencieshackerscanleveragetodousability.Appendix:DebuggingHardJavaScriptBugsInthisappendix,wetakealookatthestateofmindthatisneededtodebugdifficultbugs.SummaryThegoalofthesefirsttwochaptershasbeentoprovidebothabigpictureandasenseforhowthingsfittogether.Ajaxisabitinterdisciplinary;itinvolvesserver-sidetechnologies(Djangoforus),client-sidescriptingandlibraries(includingjQueryforus),CSS,HTML,andtheDOM,andthegoalisveryhumanincharacter.Ajaxisinterestingbecauseitopensdoorsinuserinterface,usability,anduserexperience,andinthatregarddoingwellwithAjaxisn'tjusttechnical;it'salsoabitliketheartsandhumanities.[58] Chapter2WehavecoveredthetechnicalsideofDjangoAjaxwithjQueryinbroadstrokes,including:•Atourof"Hello,world!"inAjax,andhowjQuerymakesthisaneasieroperationthanlibrary-free,"baremetal"JavaScript•ThebasicfacilitiesjQueryoffersforAjax•AtourofjQueryselectorsasanillustrationofhowjQueryoffersamorePythonic"virtualhigher-levellanguage"•AminimalDjangoAjaxapplicationwithjQuery,withadiscussionofwhatisgoingon•Anoverviewofthein-depthapplicationtobecoveredintheupcomingchaptersTheseupcomingchapterswillmovefrombroadstrokestomorein-depthcompetencieswithspecifictechnologiesintegratedintoamorein-depthapplication.Let'sbegin![59] ValidatingFormInputontheServerSideThischapterwilllookatthetopicofserver-sidevalidation,albeitfromalensthatisunorthodoxonacoupleofcounts.First,partoftheestablishedschoolofthoughtisthatserver-sidevalidationshoulddefinewhatiscorrectandbeasstrictaspossibleinenforcingtheexpectations.Whatthisclaimmeansisthatserver-sidevalidationshouldimposeassumptionsasrigidlyaspossible.PerhapstherewasadaywhenitwaslegitimatetoassumethatalladdressesworkedlikeUnitedStatesaddressesandwerewritteninASCII,notUnicode.Intoday'sworld,websiteswillseeusersfromthewholeworld,anditisnolongerbestpracticetostrivetobestrictinenforcingassumptions,suchascanbemadeaboutUnitedStatespostaladdresses.Second,inabygoneerawhereaharddrivewascomparableinsizetoalargelaundryapplianceandhadastoragecapacityinthetensofmegabytes,itmadesensetobeasthriftywithindividualbytesaspossibleandtruncatedatasothedatabasescouldhavelimitedcolumnwidths.Now,bothobsoleteassumptionsshouldbemodifiedinfavorofusabilityandglobalizationbestpractices.Inthischapter,wewill:•First,lookatwhathasbeenthestandardsecurityperspective,aperspectivestillworthunderstandingevenaswedepartfromitonsomepoints.•Lookatwhatisprovided"forfree"intermsofsecurityandvalidationinDjango.•LookatwhatDjangoprovidesasofversion1.2intermsofhooksthatcanbeusedandadaptedforspecificvalidationneeds. ValidatingFormInputontheServerSide•DiscussusabilitybestpracticesastheyaremostlyfollowedinPython—butnotaswellinDjango'sdefaultwayofdoingthings—andhowthedefaultscanbecompensatedfor.•LookatGPScoordinatesasanexampleofalegitimateplacetoexerciseserver-sidevalidation.•Discussvalidationasanareawherelessismore.•Lookatwhytraditionalvalidationis"negotiatingwithpistols",andwhatcanbedoneasanalternative—underscoringthatPythonandDjangoalreadytakecareofseveralimportantsecurityconsiderations.•Lookatabetterusabilility/UXalternativerequiringeveryfieldbefilledoutbeforeausermaymoveon.Thestandardlecture:low-levelvalidationLetuslookfirstatthemindsetforlow-levelvalidation,andlookattheuseofregularexpressionsinvalidationforsecurity.MatchingregularexpressionsThough,strictlyspeaking,validationontheserver-sideismorethanasecuritymeasure,client-sidevalidationisnottherightplaceforsecurityvalidation.Client-sidevalidationisacourtesytotheuserthatcanallowmoregracefulhandlingofhonestend-usermistakes.Server-sidevalidationshouldbebasedonthepremisethatinputisguiltyuntilproveninnocentofbeingmalicious,andnoassumptionmaybemadethatinputcomesfromyourclient-sidesoftwareatall.IfyouhaveapageorsubsitethathandlesAjaxrequests,itshouldstanduptoanymaliciousrequestthatcouldbesenttothatURLfromastandardornon-standardclient.Forexample,youhaveaJavaScriptdatepickerthatsetsaformfieldtoanISO-8601yyyy-mm-ddformat,suchas"2001-01-01"forwhenaneventistobescheduled.Youwanttoatleastperformaregularexpressioncheckorequivalent,suchas:is_valid=re.match(r"^d{4}-d{2}-d{2}$")Thismaybeappropriateasafirstlayerofvalidation,althoughbyitselfitisincomplete."9999-99-99"matchesthisregularexpressionbutdoesnotqualifyasavaliddate.Furthermore,ifthedateisforaneventbeingscheduled,itshouldbeinthefuture."1327-12-25",howevervalidasadate,cannotbeavaliddateinthefuture.Onthatpointthereisamoraltale:Aprogrammerhasaprobleminvolvingvalidatingstrings.Theprogrammerthinks,"Iknow!I'lluseregularexpressions."Nowtheprogrammerhastwoproblems.[62] Chapter3Regularexpressionsmayhaveaplace,buttheybearliabilities:theyareterseandcryptic.SoerrorswillnotjumpoutaseasilyasinPython-style"executablepseudocode".Thisisamajorproblemforatoolusedforsecurity,andtherearesomethingsthatareeitherverydifficulttodoorimpossible.Aregularexpressionthatwouldaccuratelytestforvaliddateswouldbeacompletelyundecipherablescreenfulofcode.Aregularexpressionthatwouldinadditiontestforadatethatistodayorinthefuturewouldnotonlybeacompletelyundecipherablescreenfulofcode—itwouldhavetobeadynamicallygeneratedcompletelyundecipherablescreenfulofcode!Allthisistosaythatregularexpressionsshouldnotbeouronlytoolforvalidation,evenifitistemptingtousethemthatway.YoucannotguaranteeabsolutelyvaliddataOnefurtherpointisthatthoughwespeakof"validation",ingeneralitisnotpossibletofullyvalidateinput,inthesensethatwehaveguaranteeditiscorrect.Ifauserhasselectedadatefromadatepicker,andaccidentallyclickedontheoptionnexttotheintendeddate;thereisnorealwaywecanprotectagainstsucherrors.Theremightbesomethingswecandototheuserinterfacesosucherrorsaremorelikelytobenoticedbytheuser,but"validation"can'tprovethatwehavethedatetheuserintended.Likewise,ifwerequestauser'sname,"dfsjkhlasdlhjksdfjhklds"isnotappropriateinput.Although,itwouldbeafool'serrandtotrytomakeaninputvalidationroutinethatwouldacceptanyrealpersonalnameinUnicodebutwouldavoidgarbagelike"dfsjkhlasdlhjksdfjhklds".Thepointofvalidationisnottoguaranteethatinputisvalid,buttoguaranteethatcertainforeseeabletypesofinvalidinputarenotgiven,andinparticulartoavoidmaliciousinput.Validatingcandetect(some)maliciousinputAcanonicalexampleofmaliciousinputisSQLinjection.If,inPHP,youexecuteaconstructedqueryof:"INSERTINTOcomments_log(comment,timestamp)VALUES('".$_POST['comment']."',NOW());"And$_POST['comment']issomethinglike:"',NOW());DELETEFROMcomments_log;--"[63] ValidatingFormInputontheServerSideThentheexecutedSQLstatementswilleffectivelybe:INSERTINTOcomments_log(comment,timestamp)VALUES('',NOW());DELETEFROMcomments_log;--',NOW());Withthat,thecomments_logtableiseffectivelywipedout.IgavetheexampleinPHP,notspecificallytopickonPHP,butbecausethereisnoparticularlystraightforwardwaytoaccidentallycreatethisvulnerabilityinPython(orDjango).InPythonandDjangoyoucan'tshootyourselfinthefootlikethisunlessyoureallygooutofyourwaytodoso.IfyouwritedirectSQLinPythoninthepreferred,standardway,Pythonwillhandle"escaping"oftheSQLsothatthisdoesn'thappen.AndifyoudostandardthingsinastandardwayinDjango,DjangowillusePythoninsuchawaythatthisdoesn'thappen.TominimallycorrectourPHPexample,ifthequeryisconstructedasfollows,withabackslashaddedbeforeeachsinglequote,wehave:"INSERTINTOcomments_log(comment,timestamp)VALUES('".str_replace("'","\'",$_POST['comment'])."',NOW());"Thatstillisn't100%correctbecausebackslashesthemselvesaren'tescaped.But,evenwithoutcorrectingthatadditionalbug,wehaveaddressedthevulnerabilitysothatthecodebehavesintheprogrammer'sintendedwaywhenpeopleentertextthatincludesasinglequote.(ThegenuinelycorrectsolutioninPHPistouseprovidedescapingfunctions:pg_escape_string(),mysql_escape_string(),mssql_real_escape_string(),andsimilarfunctions.)TheDjangowayofvalidationInDjango,manyofthelow-levelrequirementsareirrelevant.Thisisnotbecausetheyneedtobedone,butbecausetheframeworktakescareofcertainthingsforyou.Djangoassumesthattheinputisguiltyofbeingpotentiallymalformed,untilproveninnocent.DjangogivesyousomethingsforfreeInDjango,muchvalidationisalreadytakencareofforyou.Djangooffersasetoffieldsincluding:(forms.)BooleanField,CharField,ChoiceField,TypedChoiceField,DateField,DateTimeField,DecimalField,EmailField,FileField,FilePathField,FloatField,ImageField,IntegerField,IPAddressField,MultipleChoiceField,NullBooleanField,RegexField,SlugField,TimeField,andURLField.[64] Chapter3Thesefieldsarestoredinthedatabaseusingthedatabase'sbuilt-intypes:anEmailFieldoraURLFieldendsupbeingstoredbydefaultasaVARCHAR.However,Djangoprovidesvalidationthattheyarecorrectase-mailaddressesorURLs,and(thoughthiscanbeoptionallyturnedoff)DjangowillcheckthataURLexistsanddoesnotgivea404onattemptedaccess.Thevalidationthatcomesalongmoreorless"forfree"inDjangoisquitealot;malformedinputlikeGET/?%?%?%?%%%%&&&&HTTP/1.1willberejectedasmalformedbeforeyourvalidationhastodealwithit.Ifinputispassedon,ithasprobablypassedasanitycheck.FloatFieldandIntegerField,forinstance,willbecoercedtofloatsandintegerswithanexceptionraisedifthetextoftheCGIinputvaluecannotbesocoerced.Thereareseveralstepsinvalidation;thedifferentstepsserveashooksthatcanbecustomized,often(butnotalways)byoverridingtheappropriatemethod.Notethatinsomecasesthebestpracticeistoaddspecificvalidationtoanexistingfieldtyperatherthancreateanewfieldtype.Ifwewishtoconfirmthataproductkeypassesourcryptographiccheckforavalidproductkey,wecouldmakeaCharFieldofappropriatesizeandsetitsvalidatortothrowaValidationErrorifitdidnotpassourcheck.ThestepsinDjango'svalidationThespecificstepsofvalidationasofDjango1.2,inroughorder,are:1.to_python()returnsanappropriatePythondatatype,suchasintforIntegerField,orelsethrowsaValidationError.Hereandelsewhere,anyValidationErrorshouldbeconstructedwithastringexplaininghowtheforminputfailedvalidation.2.validate(),forthefield,handlesanyvalidationthatshouldn'tbehandledbyvalidators(whicharefunctionsthatcanbespecifiedinafield'sconstruction).IttakesavalueinthecorrectdatatypeandraisesaValidationErroronerror.Itshouldnotalterthevalue.3.run_validators()runsallofafield'svalidators,andaggregatesanyValidationErrors(iftherearemorethanone)intoasingleValidationError.Itshouldnotusuallybenecessarytooverridetherun_validators()methoditself;validatorstofeedintorun_validators()canbespecifiedonfieldconstruction.4.Thefield'sclean()methodrunsto_python(),validate(),andrun_validators(),andpropagatesanyerrorsgenerated.Thecleanedversionisreturned,andusedtopopulatetheform'scleaned_datadictionary.[65] ValidatingFormInputontheServerSide5.Anyformsubclass'sclean_()methodcanbeoverridden,willneedtolookupdatafromtheform'scleaned_datadictionary,andwillreturnthecleaneddata(whetherchangedorunchanged).Ifwesubclassedformandwantedtoconfirmthatanincludedserialnumberwaslegitimate,aclean_serialnumber()methodwouldbeappropriateandshouldraiseaValidationErroriftheserialnumberisnotlegitimate.6.Anyformsubclass'sclean()method.Thishookinparticularisintendedforrequirementsthatdonotstrictlybelongtoindividualfields.Forinstance,ifyourequirethatsomeonefillingoutahelprequestprovideeitherphoneore-mailcontactinformation,thenneitherthephonenorthee-mailfieldindividuallyisrequired.But,aformsubclass'sclean()methodisanaturalplacetocheckandraiseanerroriftheuserhasnotprovidedatleastone.Alternatively,afeedbackformmightbeexplicitlyintendedforuserswhowishtoleavefeedbackbutmayormaynotwishtobecontacted.Inthatcase,itmaymakesensetohaveacheckboxforwhethertheuserwishesaresponse,andrequirecontactinformationonlyiftheuserdesiresaresponse.Ifauserispresentedaformtospecifyoptionsforanorderandsomecombinationsdonotmakesense,theformsubclassclean()methodcanalsobeusedtocheckthattheuserhasnotselectedacombinationofoptionsthatcannotbefulfilled.Amoresensibleandcruelty-freeapproachtovalidationTogiveanexampleofuse,letusinitiallydevelopaformtocollectatelephonenumberfromendusers.Onebasicusabilityconcernisthatwhileitmaybeentirelyappropriatetostoreatelephonenumberinthedatabaseasastringofdigits,itisbothlazyandrudetouserstoimpose"validation"thatsays"Ifyoutypeanynon-digitcharacteratall,I'mgoingtorejectitaswrong."Thesameprincipleistruewithcreditcards,althoughifwearewritingcodetohandlecreditcardsinDjangoandwearenot(forinstance)thedeveloperofamajorecommerceapp,weareprobablyreinventingthewheelandthereforecreatingtroubleforourselves,whenwecouldreuseexistingdebuggedsoftware.Forcreditcards,itisrudetodemandthatpeopletypeincreditcardsas"4111111111111111"becauseyouchoosenottoshowthecourtesytowritesoftwarethatwilloverlooknon-numericcharactersandacceptcreditcardnumbersenteredlike"4111111111111111"or"4111-1111-1111-1111".[66] Chapter3Returningtotelephonenumbers,forverygoodreasonpeopleusuallydonotwrite,forexample,U.S.telephonenumberslike"8005551212".Thereissomevarietyinthewildbetweenformatslike"(800)555-1212","800-555-1212",and"800.555.1212";butallofthembreakthenumbersupbecausegivingpeople10digitsinarowisunnecessarilycruel.Furthermore,standardizingacreditcardnumbertoastringofdigitsdesiredfordatabasestorageisanintroductory-levelexercise:fromdjango.dbimportmodelsclassCreditCardNumber(models.TextField):defclean(self):returnre.sub(r'D','',str(self))Or,ifwewishtobeabletostore"(800)555-1212x123"as"8005551212x123"andperhapsvanitynumberslike"(888)4-VANITY"as"8884VANITY,"then:fromdjango.dbimportmodelsclassPhoneNumber(models.TextField):defclean(self):returnre.sub(r'W','',str(self))Andperhapsadd.lower()or.upper()tostandardizecase.(Alternatively,itmaymakesensetoonlyallowawhitelistofletters,perhapsjust'x'.Whereadigitmeansanumberdialledandan'x'onlymeans"Thisisnotfordialling;waitforthephonetobepickedupandthengoondiallingdigits,"orperhapsanalgorithmcouldstoreupper-caselettersforvanityvaluesandalowercase'x'forextensions.)ThingsgetmurkierSowhatwouldthislooklikeinDjango?Wewouldsubclassformandaddavalidator,butthere'sanotherissuethatcomesup.What'sthemaximumlengthofaphonenumberthatweneedtosupport?EvenifwerestrictourattentiontoU.S.telephonenumbers,thepreviousexamplesshowa10characterlimittobeshortsighted,andsometimesabitofastraitjacket.Itisarelativelycommonoccurrencethatthenumberapersonleavesis10digits,plusanextensionof3toperhaps5digits.Lesscommonly,conferencecallscanhavemorelike7-10digitsconferencePINs.IntheU.S.alone,possiblephonenumberscanbeabitopen-endedinlength,andwritingcodewithonlytheU.S.inmindisbreakagebydesign.Howmanydigitscanphonenumbersbeworldwide?[67] ValidatingFormInputontheServerSideThezero-one-infinityrule:acardinalruleofthumbinusabilityThisisagoodtimetomakeapointthattheDjangodocumentationathttp://docs.djangoproject.com/en/1.2/orhttp://djangobook.com/nevermakeclearly:thereisaruleofthumbininterfacedesignof"Don'tallow_____atall,orallowuptooneof______butnotmore,orallowasmanyof_____asavailableresourcessupport."Thisprincipleiscalledthezero-one-infinityrule,andwhilezeroandonebothhaveaplace,twoortwentybutnotmorearebreakagebydesign.IntraditionalSQL,breakingthezero-one-infinityruleisalmostrequired.Normallyindeclaringacolumn,youdeclarewhereitwillbreakthezero-one-infinityrule.Youmaytrytoaddahealthybitofslackinhowyoudothis,butyouordinarilycreateacolumnforpeople'snamesasVARCHAR(20),orVARCHAR(100),orwhatever,butwhateverthelimityouchoose,youspecifyanarbitrarylineandguaranteethatifauserentersonecharactermore,letalonetwenty,youwilldropitonthefloor.AnimprovementonDjango'sadvertisedapproachDjangoisPythonicinmanyways,andPythonobservesthezero-one-infinityrulewell:•Stringscanbeofanylengththatavailableresourceswillsupport•Listsandtuplesmaylikewisebeaslargeascanbesupported•Partlythroughdefthandlingofintegeroverflow,youcanhavecorrectintegercalculationswithintegersofthreedigitsorthreethousandHowever,DjangofollowsSQLandnotPythontopresent"breakagebydesign"asthenormalwayofhandlingmostsituations:fieldssuchasCharField,EmailField,andthelikearebuiltonSQL'sVARCHARandpresentedinthedocumentationwiththerequestthatyouspecifyanarbitrarylengthforCharField,EmailField,andthelikesothatifauserprovidesjustonecharactermore,thedatawillbetruncated.FieldslikeEmailFieldcanbesubclassedtouseTEXTinsteadofVARCHAR,sothatthereisnoarbitrarythresholdbeyondwhichdatawillbecorrupted.Forinstance:fromdjango.dbimportmodelsclassTextEmailField(models.EmailField):defget_internal_type(self):return'TextField'[68] Chapter3Toreturntotelephonenumbers,yetanothersolution,andperhapsabetterone,wouldbetopreservetheformattingcharacterssothattheformattingprovidedbytheuserisstillavailable,saveitasTEXT,anddeclarethattwophonenumbersareequaliftheycontainthesamedigits:fromdjango.dbimportmodelsclassTextPhoneField(models.TextField):def__eq__(self,other):try:returnself.remove_formatting()==other.remove_formatting()except:returnFalsedefremove_formatting(self):returnre.sub(ur'D',u'',str(self))Thisoverloads==,andrespectsPython'sducktypingbyavoidingisinstance:ifsomeoneelsecreatesaclassthatdoestheworkofaTextPhoneFieldbutisnotdescendedfromit,wedon'twanttodeclareallofitsinstancesunequaltoanyTextPhoneField,becauseitisnotaninstanceorasubclass.Thewayweremoveformattinghasbeenoffloadedtoitsownmethod,bothtomakeitavailableandtoallowchanges(forinstance,ifwedecidetochangewhatcharacterswedoanddonotremove,perhapsbyreplacingtheregularexpressionur'D'withur'W').LetuslookatonemoredetailofDjangovalidationbeforebuildingavalidatedformforourcasestudy.Djangofieldscanbecreatedwiththeargumentrequired=TruewnloadfromWow!eBookoorDrequired=False,defaultingtorequired=True.Forinstance,aCharFieldorTextFieldcreatedthedefaultwaywillregisteravalidationfailureonanemptyvalue.Thereareatleasttwodifferentcaseswhereyouwillwanttosetrequired=False:ifyouevermightwanttoallowanemptyvalue(itisusuallynotauserinterfacebestpracticetomakepeoplefilloutallfields,allthetime);orifyouwanttohaveausefulBooleanField,normallyrenderedasacheckbox(theBooleanField,likeotherfields,defaultstobeingrequiredthatyoufillitin,meaningthatitdefaultstobeingrejectedasinvalidifyoudonotclickacheckbox—themeaningofthecheckboxisnot"Doyoumean'Yes'or'No'?"but"Say'Yes',andgiveyourrubberstamphere.").[69] ValidatingFormInputontheServerSideAvalidationexample:GPScoordinatesLet'stakeaslightlymoreinvolvedexampleofinputvalidation:afieldforGPScoordinates.Thisiscomplicatedinpartbecause"GPScoordinate"doesnotexactlymeanasinglesystem,butoneofafewdifferentsystems.Forthisspecificsystem,whichissomethingofanexception,wecouldusetheVARCHARbasedCharField.ThisisbecauseliketheinformationstoredinaDateorDateTime,thereactuallyisanupperlimittohowlongausefulvaluewillbe.Ameasurementtothenearestthousandthofasecondisspecifictowithinadistanceyoucouldmeasurewithahandheldruler.Wewillallowsomeaddedspace,butthegoalisnotspecificallytocreateaGPScoordinatefieldthatallowssubatomicprecision.Amongthewaysanaddresscouldbespecifiedarethefollowing:36°09'55.8"N,86°46'58.8"W36.16586-86.7842537°25.330'N,122°5.039'WN360955.781W864658.287Wemayfindithelpfultouseregularexpressionsatsomepoint.Whileitshould,inprinciple,berecommendedtomakeasingleregularexpressioncalltohandleallthesecases,muchbetterwouldbetobreakitdownintoaseriesofmanageabletests,inthiscasereturningsuccessifinputmatchesatestasvalid,andthenraisingaValidationErrorifnoneofthetestscouldinterpretitasvalid.Wewillspecifyamaximumlength,butfornowleaveitunspecified,somethingtobedeterminedafterweknowhowlongthelongestvalidatingentrywillbe.Ourcodeisasfollows:fromdjango.dbimportmodelsfromdjango.core.exceptionsimportValidationErrorimportredefgps_validator(value):#Createanormalizedworkingcopyofthevalue.working_copy=valueworking_copy=working_copy.replace(u' ',u',')working_copy=working_copy.replace(u'r',u',')working_copy=re.sub(ur',*$','',working_copy)working_copy=re.sub(ur',+',u',',working_copy)ifnotu','inworking_copyandnotre.match(ur'.*.*.*',working_copy):working_copy=working_copy.replace(u'',u',')[70] Chapter3working_copy=re.sub(u'[0B020182019201C201D'"]','',working_copy)working_copy=working_copy.replace(u',',u',')working_copy=re.sub(ur's+',u'',working_copy)working_copy=working_copy.strip()working_copy=working_copy.upper()#Testthenormalizedworkingcopyagainstregular#expressionsfordifferentkindsofGPSformat.ifre.match(ur'[-NS]??d{1,3}[0-5]d[0-5]d(.d+)[NS]?,[-EW]??d{1,3}[0-5]d[0-5]d(.d+)[EW]?',working_copy):returnworking_copyelifre.match(ur'[-NS]??d{1,3}[0-5]d(.d+)[NS]?,[-EW]??d{1,3}[0-5]d(.d+)[EW]?',working_copy):returnworking_copyelifre.match(ur'[-NS]??d{1,3}(.d+)[NS]?,[-EW]??d{1,3}(.d+)[EW]?',working_copy):returnworking_copyelse:raiseValidationError(u'WecouldnotrecognizethisasavalidGPScoordinate.')classGPSField(models.TextField):default_error_messages={u'invalid':u'WecouldnotrecognizethisasavalidGPScoordinate.',}default_validators=[gps_validator]Theregularexpressionsarecrypticandugly,but,inanutshell,whatisdoneistocoercethedatatoanormalizedform,andthentestforwhetherthenormalizedformoftheinputlookslike"N360955.781,W8646258.287"orothercomparableforms.Avoidingerrormessagesthatpointfingersandsay,"You'rewrong!"Wemightalsobrieflystoptonoteaprincipleofuserexperienceanduserinterface.Thecomputermustattimesbearbadnews,butthereisadifferenceinhowthesoftwarecomesacrossbetweensaying,"Wecouldnotrecognizethisasvalidinput"and"Youmessedup."[71] ValidatingFormInputontheServerSideYoursoftwarewillbebetterlikedandbetterreceivedifithandlesvalidationfailuresbysaying"Wecouldnotrecognizethisasvalidinput","Weneedadditionalinformationbeforewecancontinue",andsoonthanifitsays,"Youentereddatathatwasinvalid",or"Youfailedtoproviderequiredinformation".Ifnothingelse,thereismuchlesseggontheprogrammer'sfaceiftheseniorvice-presidentisanavidgeocachinghobbyistwhoknowsGPSinsideandout,andentersGPSdatainaperfectlyvalidformatthatwedidn'tknowabout.Itismuchbettertotellahobbyistvice-presidentwhoknowsGPSbetterthanwedo,"Wecouldn'trecognizethisasavalidformat."than,effectively,Youidiot,weknowbetterthanyouthatyoudon'tknowhowtowriteavalidGPSlocation.Now,afterthefact,ifweallowforspecificationofuptoathousandthofasecond,a"maximallylongbelievablecleanedinput"mightbe:N1001010.111,S1001010.111ThatcreatesaUnicodestringoflength30.Wemightsuggestthatagoodprogrammermightbeslightlynervousaboutfixingalengthof30,probablybecauseifsomeoneaddsextrawhitespace,orinlightofunforeseenfuturedevelopments,itmakessensetouseGPScoordinatestosub-millimeterprecisionandsopeoplestarttoenterdatalike:N100°,10',10.11111",S100°,10',10.11111"Ourregularexpressionsmaybeslightlymorefuture-proofbecausetheyarewrittenflexibly.Butevenhere,thereisreasontobeabitnervousaboutchoosingVARCHARoverTEXT.ValidationasdemandingthatassumptionsbemetTovalidateistodemandthatuserdataconformtoyourexpectations,meaningassumptions,aboutwhatconstitutesvaliddata.It'shardtomakeassumptionsthatarevalidtheworldaround.Old-school:conformtoourU.S.-basedassumptions!Let'stakeasomewhatstandard,old-schoolapproachtovalidationofwhatgoesinthedatabase.Thisapproachsaysthatyoudon'twantjunkdatainyourdatabaseandyoudefinestrictrulesandtakeeverypossiblesteptopreventjunkdataentering.Theinitial,U.S.-centricformhas:[72] Chapter3•Streetaddress,line1(required)•Streetaddress,line2(optional)•City(required)•State(required,andspecifiedbyadropdownmenu)•Zipcode(required)ThisisU.S.-centricbutnotentirelyfairtoU.S.residentsthemselves.Forstarters,someformslikethisoutinthewildrejectvalidinput.Thereisafive-digitandanine-digitversionofaZIPcode.Althoughthediligentnine-digitzipcodeispreferredandisintendedtoallowmailtoreachitsdestinationfaster,enteranine-digitzipcodeonmanyformslikethisandyouwillgetaresponseamountingto"Thisisunacceptable;pleasetryagainandgivearealzipcodethistime."Butevenifthatproblemisnotpresent,thereisanotherproblemasfarasthestategoes.Ifwejustletpeopletypeinatwo-letterstatecode,weareinvitingtypos,evenifmanyofthemcouldbecaughtbystraightforwardserver-sidechecks.Butifwerequireadrop-downmenusotheycan'ttypeaninvalidstate,somethingreallyfunnyhappensevenifeverysinglerecordhasarealstate.ValuedcustomerswhomaynothavespentlonghoursonPac-Man(letalonegrowinguponsmartphonesandSecondLife)trytheirbesttohandlethelongdropdownmenubutstillleaveaddresseslike:AshleyJones1745BroadwayNewYork,NC10019Whereaswecouldtelltherewasaproblemifthispersonaccidentallymadeatypocorrespondingtoaninvalidstate,heretheerroristhatadifferentvalidstatehasbeen(mis-)registeredasthecustomer'sstate.Andwhatisworse,wegetmanymoreoftheseinvalidrecordsthanifwejustletcustomerstypeintheirownstates.Notonlyisthelongdropdownmenucrueltousers,itmakesagoodmanyusershavemoretroubleenteringthedatatheyintended.Asaresult,thedropdownmenudeliversmorethanapainfuluserexperience.Itdeliverssignificantlyandsubstantiallymorecorruptdatainthedatabasethanifwehadjustbeen"lazy"andletthemtypeatwo-letterstatecode.Anditdeliverscorruptdatathatishardertoidentifythantyposlike"NU"whichareusuallynotvalidstatecodesandarethereforeoftenmorestraightforwardtoidentifyascorruptdata.[73] ValidatingFormInputontheServerSideWhatdowedoifwewanttotakeaformlikethisandmakeitmoreinternational?Onesolutionthatperhapsallofushaveseenis:•Address,line1(required)•Address,line2(optional)•City(required)•State,province,district,region,orterritory(required)•Ziporpostalcode,ifapplicable(optional)•Country(required)AndwecanparticularlyoffendCanadians,whowinceat(realorimagined)evidencethattheU.S.lumpstheminasthe51ststate,bymakinga"state/province/…"menuthatinterminglesU.S.statesandCanadianprovincesandthenleavestherestoftheworld'sregionsas"Other(pleasespecifybelow:)".ThissolutionisprobablymeantasagestureofwarmthfromtheU.S.toCanada,butitisagestureofwarmththatquiteprobablydidnotincludeconsultingaCanadianaboutCanadiansensibilities.ThereisatleastoneU.S.-specificconcessionthatactuallymakesalotofsense.FortheNewYorkerwhodidn'tgrowuponvideogames,puttingallofthecountriesinalphabeticalordermayendupwithaproblematicnumberofstreetaddresseslike:AshleyJones1745BroadwayNewYork,NC10019UnitedArabEmiratesIfwewantedtotrusttheuser'sjudgmentalittlemore,wecouldletpeoplefillouttheircountryastext.Thatcouldresultinless-uniformdata,as"USA","US","U.S.A.",and"U.S."wouldprobablyallappearinanynumberofU.S.residents.Butitwouldbothbemercifultotheenduserandrefrainfromthecrueltyofmakingpeoplepickaneedleoutofthehaystackofalongdropdownmenu,andresultinbetterandmorecorrectinformationinourdatabase.Addingthewrongkindofband-aidThesolutionthatistaken,inmanycases,istomake"UnitedStatesofAmerica"thefirstoptionofthemenusoastoavoidgivingU.S.residentstheneedle-in-a-haystackcrueltyoffindingtheircountryburiedunderthe"U"sectionofanalphabeticallistofeverycountryintheworld.AnditisneedlesslycrueltoresidentsoftheUnitedStatesofAmerica…ortheUnitedKingdom…orUnitedArabEmirates…or…[74] Chapter3ThisleavesouttheinternationalizationissuethatnotalladdressesareconstructedlikeU.S.addresses.Ifyoutaketheoriginalstructureof:•Streetaddress,line1(required)•Streetaddress,line2(optional)•City(required)•State(required,andspecifiedbyadropdownmenu)•Zipcode(required)Appendingcountryandmakingthe"State"and"Zip"slotsgenericdoesnotmakethestructureflexibleenoughtoaccommodateinternationalizedaddresshandling.Somecountrieshavemorelinesintheirstreetaddresses,whilesomehavefewer.Partofthisisabsorbedbytheoptionalsecondstreetaddressline,buttherearestilladdressesworldwidethataremangledifyoudemandthattheyfitthisformat.Evenifyouwillallowlettersinapostalcodeandfreeformtextforthestate-likefield.Thenwhatcanbedone?Itshouldbealmosttheoreticallypossibletojustpresentadropdownmenufirst,specifyingcountry,andthenhaveAjaxadjusttheformsothatthereisanappropriateformforthecountry.Onthebackend,anequallyRubeGoldbergsystemofrulescouldenforcethismentalityforeverylocalityworldwide,andonthebackendatleast,peoplewouldn'tgetfrustratedwaitingfortheenormousAjaxapplicationtoloadandpossiblycrashtheirbrowsers.Butatleast,inprinciple,thismentalitycanbeinternationalizedtoputitsheavyhandoneveryinternationaladdressinfullylocalizedform.Butthereisanalternative—Asingletextarea.Perhapssomeprogrammersmightfeellikethey'renotdoingtheirjobsifthey"just"giveatextareaforaddresswithoutfurtherdoingdiligentwork,likemakingpeoplesufferthroughlongdropdownmenus,toconvincethemselvesthey'repreventingpeoplefromgettingbaddataintothesystem.Evenifitmeansthatforeveryonecasewheretheypreventanaddresslike"…NewYork,NU…"fromenteringthesystem,tenaddresseslike"…NewYork,NC…"getthroughvalidation.Butreally,honestly,it'sbettertoletgoofthecontrolfreakmentality,stopmakingpeoplejumpbetweenfieldsonyourparticularform,andjusttypeanaddresstheyknowwellinanordinary,clearlylegible,andattractivelystyledtextarea.Really,lessismore.[75] ValidatingFormInputontheServerSideAndoneadditionalpointtounderscorethatlessismore:EvenintheU.S.,I'verunagroundwith"moreismore"computersthatnegotiatedwithpistols.One"negotiatedwithpistols"demandisthatifyouhaveavalidaddress,youincludeastreetaddresswithahousenumber.Andmyaddresswas:JonathanHaywardDepartmentofTheologyFordhamUniversityBronx,NY10458-9993Andthiswastreatedasnotanacceptableaddress,period.NevermindthattheZIP+9alonetoldwhichdepartmentintheuniversitytosendcorrespondenceto.Ididnothavetheexpectedstreetaddressbeginningwithanumber,andtherefore,bydefinitionitseemed,IcouldnothaveenteredavalidaddressandIcouldnotbeallowedtomoveforward.AndthisoccasionallossofU.S.customersisawaytodriveawaycustomerstheworldaround.Peopleinotherpartsoftheworldmaywishtopatmeontheheadandsay,"Poorbaby!Youhadataste,once,ofwhathappenstousallthetime!"MakingassumptionsanddemandingthatusersconformLet'slookatoneothertypeofbasicinformation:aperson'sname.Forthisdiscussion,wewillignoreUnicodeissuescompletelyandlookatproblemsthatcomeupevenifwesticktoASCII.LinkedInandFacebookalikerequestafirstandlastname,butthisisn'tquiteright.Atleastnamesaresimple,right?Onthesurface,oneobviousvalidation,ifwedonotcoercecase,isthatapersonwillhaveafirstandlastname,eachofwhichisanuppercaseletterfollowedbyoneormorelowercaseletters.That'seasyenoughtotestfor,butitdoesn'tallowforalotmorethanafirstinitial,optionallyfollowedbyaperiod.Andstrictlyspeaking,itdoesn'tallowevenmostAmericanfulllegalnamesatbirth.Though"FirstnameLastname"isoftentreatedasaperson'sfullname,mostfulllegalnamesare"FirstnameMiddlenameLastname."AndmostpeoplewilltreatthecommonexceptionoftheCamelCasedlastname,suchastheScottish"MacDonald"ortheIrish"McDonald".[76] Chapter3ButherewehavetheMontyPythonsketchwheretheCardinalsannounce"Ourtwochiefweaponsarefearandsurprise,"becausethere'satleastonemorecommonexception.TheFrench"deBalzac"/"deBALZAC",theGerman"vonFriar",ortheDutch"vanDriel"or"vandenDriel",whichinEuropeanconventionarealphabetizedunderthecapitalizednamebecause"de"/"von"/"van…"isnotstrictlypartofthename.SoourtwochiefexceptionsareCelticCamelCase,andEuropeanwordstranslatingto"of"(namely"de"/"von"/"van…")thatareaddedbeforetheproperpartofthename.AndalsothecasewhereAmericansofDutchancestryhaveCamelCasedorotherwisealteredtheEuropeanconvention,makinganameofVanDrielorVandriel.EveninASCII,thingskeepgettingmurkierSonowourthreechiefexceptionstoourgeneral-purposevalidationruleareasabove,andalsothatitisbecomingmorecommonpracticeforwomentolistboththeirmaidenandmarriednamesaslastnames.Ourfourchiefexceptionstothegeneral-purposevalidationruleincludeCelticCamelCasing,variationsonEuropean"de"/"von"/"van…",womenlistingtwolastnames,andOrthodoxmonksandnuns.AnOrthodoxwomannamedSarahSmithwhobecomesanunwilltakeanewfirstnamesuchasXeniaandgiveupherlastname,willbeproperlyaddressedas"Mo.Xenia"(thatis"MotherXenia"),andnotbereferredtowithherlastnameatall.WhenitisnecessaryfromcontexttoperhapsspecifywhichMo.Xeniaisbeingreferredto,parenthesesareexpectedaroundherlastname.Soshewouldthenforclarificationbereferredtobynameas"Xenia(Smith)".Soourvalidation,ifitallowsforthis,willneedtoallowfornolastname,oralastnamethatbeginsandendswithparentheses,orboth.Orthodoxbishopsaremonksandthushaveonlyonename,butitiswritteninALLCAPS,forexample"HisGraceBASIL".Oursimplevalidationruleisimposingcertainassumptions,andthelistofexceptionsseemstogetlongereachtimewelistit.Furthermore,thepatternofafirstandlastname,wherethefirstisapersonalnameandthelastisafamilyname,isfarfromuniversal.InChineseculture,peoplehaveafirstandlastname,butthefirstnameisthefamilynameandthelastisapersonalname.Wewouldproposethefollowing,whichalmostassuredlyimposessomeculturalassumptionsthatdonotholdinallcultures:•AnoptionalUnicodeslotforanyhonorific(s)•AUnicodeslotforaperson'sname[77] ValidatingFormInputontheServerSide•AnoptionalUnicodeslotforwhatinWesternculturearelettersafteraperson'sname,includingnotonlyacademicdegreesbutreligiousordermembership,titles,andanopen-endedlistofotherthingsthatshouldnotexcludebeingaLinkedInOpenNetworkerInU.S.culture,thereisaninformalsocietywhereitisconsideredunpretentioustosimplybeaddressedbyyourfirstnameandnohonorific.Thisisnotapatternworldwideandgivingpeopleaplacetoindicatewhathonorificsarepropertoaddressthembyisappropriate.Or,asanalternativewhichmaybepursuedforthegreatergoodoftheUI,wemayhaveasingleslotthatholdshonorifics,name,andpost-nominals.Nowthereisnothingwrongwithcreatingheuristicsthatwilltrytoextractafirstandlastname,andDjangoserver-sideheuristicscouldbemadethatwouldbequiteaccurateinhandlingU.S.-stylenames.BettervalidationmaybelessvalidationAsfarasserver-sidevalidationgoes,really,lessismore.Perhapsweasprogrammersarealwayslookingforwayswecanbemorediligent,andwelookforwayswecanworkharderthatwilldeliverabettersolution.Butthere'ssomethingweneedtounderstandaboutvalidation.Eachrequirementinvalidationisademandthatdatameetanassumption.Eachassumptionisawaytomakeasolutionmoreculture-specificandhardertointernationalize,andalsolessfuture-proof.Validationthatrequiresarealfive-digitU.S.zipcodecreatesanunpleasantsurprisetothecustomerwhodiligentlygaveaperfectlyvalidnine-digitforminthehopethataproductmightarriveonedaysooner.OldU.S.validationrequirements,fromwhenadatabasereallywouldonlyholdU.S.information,arealmostinvariablyalegacyobstacletogracefullyhandlinginternationalizedandlocalizedinformationintoday'sworld.TheremayaswellnotbeasingledatabasevalidationmeasurefordatalikeaddressesthatoriginallyonlytookU.S.dataintoconsiderationthatdoesnotcreateobstaclestohandlinginternationalizeddatagracefully.Almostbydefinition,theydemandthatdatameetassumptionsthatthingsbehavethewaytheydointheU.S.WhatifourGPSvalidatorisusedinFrenchlocalization?InEnglish,aperiodisusedtospecifyadecimal;πtotwodecimalplaceswouldbewritten,"3.14".InFrenchnumbers,thecommadoesthisjob,asin"3,14".OurGPSvalidationroutineabove,inEnglishlocalization,wouldvalidate"36.16586-86.78425"butrejectaFrench-style"36,16586-86,78425".[78] Chapter3Caveat:EnglishissomethingofalinguafrancaSurelyourGPSvalidatorcouldbeadjustedtotolerateeitherlocale'sdecimalmarker,butthisistheroadtoRubeGoldbergnightmarecode.ItmightmakesensetoaskpeopleenteringGPSdatatouseEnglishlocalizationaslinguafrancaforthatpartofthecode.AndnotonlybecauseaGPSfieldissomethingwemightwanttoparseandhavethecomputerunderstand,anditwouldbeaRubeGoldbergcodenightmaretowriteaparserforeverypossiblelocalizedformofGPScoordinates.TonativeEnglishspeakersconcernedaboutculturalchauvinism,wemightsuggestthatitmakesalotmoresense,internationally,thanwemightsuspect.WhenIwasstudyinginParis,Irememberseeingakeyboardsynthesizer,lookingtoseewhatwasselected,andbeingutterlyshockedtosee"HONKY-TONKPIANO,"inEnglish.IhadassumedthatintheU.S.synthesizerswerelabeledinEnglish,andinFrancesynthesizerswouldjustasnaturallybelabeledinFrench.Butthemarkings,instrumentnames,andsoonwerepureEnglishandIwastheonlyonetofindthisstrange.Englishislinguafranca,orclosetoit,fortechnology,andspeakersofsomeotherlanguagesfinditnormaltohaveanonlineforumwiththeuserinterfaceinEnglish(andforumthreadsintheirnativelanguage).LatinremainedthelanguageofscholarshipandinternationaldiscourseinEuropewhetherornottherewereanynativeLatinspeakersatall.WhenEuropemovedtowritingscholarshipinpeople'snativelanguages,booktitlesremainedinLatinformuchlonger.The20thcenturyGermanLudwigWittgenstein'sseminalTractatusLogico-PhilosophicusiswritteninGerman,butthetitleisinLatin.AndwhentheworkisdealtwithinEnglish,thecontentsaretranslatedtoEnglish.Butthetitle,whichcouldbetranslatedinEnglishlike"Logical-PhilosophicalTreatment",isabbreviatedtoTractatusbutseemstoonlybegiveninLatinevenamongEnglishspeakerswhohaveneverstudiedLatin.EnglishnowisabitlikeLatin:itissomethingofastandardlanguage,especiallyinmattersoftechnology.Andmanynon-nativespeakersmakeittheirfirstchoiceindiscussingtechnology.Furthermore,itmightbepointedoutthatGooglehasmadeextraordinaryeffortsatlocalization,withmeticulousattentiontodetailforgivingdirectionsinJapanforinstance.GoogleMapsatthetimeofthiswritingaccepts"36.16586-86.78425"andpullsitupimmediately,butforalocationof"36,16586-86,78425",answers,"Wecouldnotunderstandthelocation36,16586-86,78425".Wemightpointoutthattheerrormessage,"Wecouldnotunderstand…"iswelldoneandminimizesanymessageof"Youdon'tknowwhatyou'redoing."[79] ValidatingFormInputontheServerSideDjangocomeswithvalidated,andworldwide,EmailFieldandURLFieldfields.Anditwasdesignedbyjournalistprofessionalswhowouldknowperfectlywellthatitisdesirabletobeabletostorephonenumbersandpostaladdresses.However,thereisnoPhoneFieldorStreetAddressFieldbecausemakingworldwidevalidationfortheseisatarpit.TherearetoolstomakeaU.S.-centricaddressfield,completewithlong,crueldropdownmenustospecifyavalidstate,butnoWorldStreetAddressFieldbecausethatwouldbeaRubeGoldbergtarpit.Thenisthereanyvalidationtobedone,ordowesimplydropit?Wemightproposeonealternative.Wedon'thavetonegotiatewithpistolsThestandardruleofpracticeisforvalidationtonegotiatewithpistols.Soifdatadoesn'tmeetyourassumptions,yousay"IwillnotletyoumoveforwarduntilyouchangethisdatatowhatIassumeitshouldbe."AsfarasDjangoserver-sidevalidationgoes,ifyouspecifythataninputfailsvalidation,youguaranteethattheinputassubmittedcouldneverbeenteredintothedatabase.Butthereisanotherclient-sideoption.Fortheclientside,possiblythemostcommonerrorisnotspecifyingacompleteaddressincludingcountry.Anditwouldbebothpossibleandsensibletorunaclient-sidetesttoseeifyoucanrecognizetheprovidedaddressasincludingacountry.IfyoucatertoamainlyU.S.clientele,itisalsoentirelypermissibletohavethesysteminferaU.S.addressiftheaddressendsinsomethinglike"NewYork,NY10019-4343".Butyoudon'thavetonegotiatewithpistols.Insteadofrefusingtocontinueunlesstheuserentersanaddressthatmeetsourassumptions,wecouldgiveadialogthatsays,"Wecouldnotidentifywhatcountrythisaddressbelongsto"withbuttonsofGobackandspecifyacountryandThisaddressiscorrect.Thisprovidesawaytoidentifyforeseeabledataerrors,andaskstheusertocorrectthem.Butatthesametime,itavoidscreatinganabsoluteroadblocktotheuserwhospecifiesanentirelyappropriateaddressthatourvalidationroutinesdidn'tforesee.ForaU.S.companykeepingadirectoryoftelephonenumbers,with,say,four-digitphoneextensions.Aphonenumbermightbetested,client-side,bystrippingoutnon-wordcharacters,andacceptingaphonenumberofatleasttendigitsasvalid;oraphonenumberoftendigits,an'x',andatleastonemoredigitasvalid;orexactlyfourdigitsoptionallyprecededbyan'x'asvalid;orwarnforanyothernumber.ThatofferssignificantlybetterflexibilityforpeoplecomingfromoutsidetheU.S.,evenifitdoesnotundertakethe(again)RubeGoldbergtaskofcheckingforcountrycodesandnumbersthatcorrespondtothegivencountrycode.Butifthevalidationwarns,insteadofdemandingthatitsassumptionsbemet,someonewhotriestomakeanentryof"466453"(fortextingqueriestoGoogle)willbeallowed,afterawarning,toenteranumbernotforeseeninthevalidator'sassumptions.[80] Chapter3Doingourbesttosolvethewrongproblem:astoryPerhapsthemostinappropriatetextbookexampleofwhyweneedpreciserequirementstellsofaprofessorwhoassignedashomeworkthatstudentsshouldprogramafour-functioncalculator.Onestudentturnedinaflawlessfour-functioncalculatorusingRomannumerals,completewithafulluser'smanualinLatin.Theobviouspointthatwaspresentedintheexampleisthattheprofessorwouldhavebeenbettertobemorepreciseinthehomeworkspecification,butthisisnaïveandstupid.Thestudentwasgivingacrystal-clearsignal,"I'minthewrongclass!"ItisstupidityworthyofDilbert'sbosstotrytosolvethisproblemwithmorespecificrequirements.Hadtheprofessorbeenmorespecific,statingthatArabicnumeralswereacceptableandRomannumeralswerenot,thiswouldhaveaccomplishedtwothings:•First,itwouldhavemadetheassignmentmoreconfusingformostofthisstudent'sclassmates•Second,itwouldhavethrowndownthegauntlettoanystudentwhowasinthewrongclassThestudent,perhaps,wouldhaveproducedafullyfunctionalfour-functioncalculator,implementingthespecificationtothelegalisticletter,andtheprofessor,perhaps,wouldhavebeensurprisedtotype:>7*856Andthenfind,ostensiblytoprovideenhancedaccessibility,thattheprogramplayedasoundclipofapainfullyloudnoteonanout-of-tunepianofifty-sixtimesbeforemovingon.AndthestudenthadtakenthoughttosetaUnixterminalmodethatincludeddisablingtheusualwaystosendasuspendorinterruptfromthekeyboard.Theproblemwhenastudenthandsinafour-functioncalculator—withRomannumeralsandafullLatinuser'smanual—isthatthestudentisboredsillyinthewrongclass.Thecorrectsolutionistobumpthestudentupsomeclassesuntilthestudent'sabilityandtheclass'schallengelevelareinsync.Kneejerkreactionstomovetowardsmorelegalisticrequirementsdonothelp.PartoftheAgileexperienceisthathandlingrequirementsmorelegalisticallydoesnothelpintherealworld,either,notasmuchasonemightthink.(ThedocumentsandcommunicationofAgiledeliveringitsbestsolutionsprobablyneverhavethelegalisticprecisionofaheavyweightwaterfallrequirementsdocumentsolvingabadlyidentifiedproblemaspreciselyaspossible.)Communicatingclearly,ofcourse,isalwaysanasset,buttryingtomanageriskandchangebytryingtobelegalisticandpinthingsdownthatwayisanattempttosolvethewrongproblem,whichrarelydoesagoodjobofaddressingthecorrectreal-worldproblem.[81] ValidatingFormInputontheServerSideItreallydoesapplytovalidationAndthesameinsightholdsforthe"BigDesignUpFront,"heavyweightdesignprocessmentalityforvalidation.IfwedealonlywithU.S.contacts,wecanuseaformwith:•Address,line1(required)•Address,line2(optional)•City(required)•State(required)•Zip(required,anyvalidzipcodeaccepted)Ifweareafraidthatpeoplewillgivejunkvalues,withacitylike"afdjkfdkj",wemaybeabletouseUSPSwebservicesorthewebsitetotestwhetheranactualstreetaddressisgiven.Butthenwhatifpeopleuseanaddressacrossthecountry?Conceivably,wecouldlocatetheirIP'sgeographicallocationinrelationtotheaddress.Butthisisbeingsilly,nottomentionthatcustomersmightfinditatadcreepy.Peoplewhoreallywanttoprovidejunkdata,perhapstopreservetheanonymityoftheiraddresseswhenitisinappropriatelydemanded,willdoso.Wecantrytodothingsthatwillcatchcommondataentryerrors;buttryingtolegalisticallykeepinvaliddataoutofourdatabaseswillaccomplishlittlemorethanamorelegalisticwordingforthefour-functioncalculatorassignment.Butdon'tweneedtoassumedataisguiltyuntilproveninnocentofbeingmaliciousandpossiblymalformed?NotpersonallyifweareusingDjango,becauseDjangoisintendedtotakecareofthosebasicsforus.IfwecanfindawaytostraightforwardlybuildamodelwithDjango-providedfields,withDjangomanagingtheSQL,andstillsucceedinexecutingarbitrarySQLbyaninjectionattack,weshouldcontactsecurity@djangoproject.comabouthowwemanagedtostraightforwardlyuseDjangomodelsandfieldsandcreatesuchavulnerability.Forthatmatter,ifyoumanagetofindastrangeandconvolutedsetupthatexposesanyvulnerabilitythatyoudidn'tdeliberatelycreate,pleasenotifysecurity@djangoproject.com.Theywouldliketoknowevenifitturnsout,onlaterinspection,tobespurious.Theyhavetriedveryhardtomake"naïve"useoftheirmodelsandfieldsassafeaspossible.Andwhilenoprogrammerworthtrustingwouldsay"Weknowthatourprogramisfreefromvulnerabilities,"theyhavetriedveryhard,andundergoneintensepublicscrutiny,sothatDjangocantakecareofbeingparanoidaboutattackslikemalformedinputandSQLinjectionforus.Atleastasfarassecuritybasicsareconcerned,Djangoisdesignedtotakecareofassumingthatdataisguiltyuntilproveninnocentofbeingmaliciousandpossiblymalformed.[82] Chapter3FacebookandLinkedInknowsomethingbetterWewouldlastmakeanobservationfromsocialnetworks.Socialnetworks,likeFacebookandLinkedIn,thriveonfullprofilesbutdemandsurprisinglylittleinformationtobeletin,atleasttostartoffwith.Theinitiallyrequiredinformationaskslittlebeyondthecoreessentialswithoutwhichthenetworkcannothandletheuser.Butthentheyuseanotherdynamic:whiledemandinglotsofinformationupfrontisoff-puttingandwouldkillthem,peopleliketohavecompleteprofiles.Andbothsocialnetworksprovidefeedbackthataparticularaccountis25%filledin,or80%filledin,andsuggestaconcreteandmanageablesteptotaketoimproveonthatpercentage.Wewillbedevelopingasourmodelapplication,anintranetemployeephotodirectory.Whilenotintendedtocompetewithsocialnetworks,wewilltakeacueandrequiretostartwithaminimalamountofinformationandthenletpeoplemovetowardsa100%completeprofile,onestepatatime.SummaryWe'velookedatserver-sidevalidationwithanemphasisonusabilityandinternationalizationconcerns,andraisedtheideathatavalidationrequirement,especiallyontheserverside,isademandthatuserinputconformtoyourassumptions.ThatisexactlyhowU.S.-centricassumptionsindatavalidationcanleavenon-U.S.visitorswithnowaytomoveforwardgivingtheiraddressinaformthatwillpassvalidation.Djangodoesalotforus,includingalotofworktogracefullyhandleUnicode.WewnloadfromWow!eBookoDmightbeadvisedtouseTextFieldinsteadofCharField,andTEXTinpreferencetoVARCHARduetoabasicusabilityconcern,butDjangoalreadydoesalotforus,includingquitealotofholdingdatainnocentuntilprovenguiltyofbeingmaliciousandpossiblymalformed.Inthischapterwehavecoveredtheinsightstraditionallyconsideredessentialtooptimalserver-sidevalidation.ThenwemovedontohowDjangoaddressescertainbasicsecurityconcernsandprovidesaframeworkforvalidation.Weintroducedoneusabilityprincipleandbestpractice—the"zero-one-infinityrule"—anddiscussedhowDjangocanbeadaptedtoobserveit.WealsoprovidedanexampleofhowtomakeavalidatedfieldforGPScoordinates,discussedwhenandwhere"lessismore"withvalidation,andhowthisrelatestousabilityandinternationalization.Wethendiscussedusabilitybestpracticestoencourageprofilecompletionwhilenotdemandingthatusersfillineveryfieldbeforebeingallowedtocontinue;thiswewillseeinthenextchapter.Next,let'slookatsearchingontheserversidewithDjango,forwhichitprovidesexcellenttools.[83] Server-sideDatabaseSearchwithAjaxInthischapterwewillcoversomeofDjango'sdatabaseandpersistencebasics,createamodel,andcreateanAjax-orientedsearchforthatmodel.WewillbuildontheobservationthatcontextswitchingbetweenPythonandSQL,especiallyifitisfrequent,bearsa"cognitivetax"andmakesforbuggiercode.ThewaytheDjangodevelopershavetriedtoofferanimprovementistocreatefeaturessothatmostoftheworkaddressedbywritingSQLcannowbemanagedwithoutdisruptingaprogrammerfrom"Pythonmode."Wewillbothtakeanoverviewofthebasicsandthenseeasimplecaseofthesebasicsinaction.Morespecifically,wewill:•Compareclientandserverrolesanddiscussreasonsforaserver-sidehometosearchfunctionality•IntroduceDjangomodels,andbuildanin-depthsampleDjangomodel•Beginworkonourintranetemployeephotodirectory•Discusswhatkindoffunctionalityisdesirableinasearchtool•CreateaDjangoviewtoserveastheJSONbackendpartofthesearchtool•TourtheDjangodatabasesearchfunctionality,anddiscusshowitissimilartothingswehavealreadyseeninjQueryselectorsLet'stakealookatsearchingontheclientorserverside. Server-sideDatabaseSearchwithAjaxSearchingontheclientsideandserversideThereexistpowerfuloptionsforanin-memoryclient-sideJavaScriptdatabase.Thismuchmeansthatthedifferencebetweenserver-sideandclient-sideoptionsisnotachoicebetweenexcellentandterrible.However,twofeaturesareworthconsidering.Persistence:Muchoftheattractionandreasonforusingaserver-sidedatabaseisthatitofferspersistence.Havingadatabasethatdoesnotsupportpersistencedefeatsthepoint,likeacarwithoutanyroomforpassengersorthedriver.Thisisnottosaythataclient-sideJavaScriptdatabasecannothavepersistence.WithAjax,itispossibletocreateapersistencesolution.However,itistosaythatcreatingaserver-sidedatabaseisasolutiontothepersistenceproblem,andaclient-sidein-memorydatabaseisnot.Scaling:Fortheapplicationweareusing,andforpersonalandtestuseofthatapplication,deliveringeverythinginthedatabasewemightpossiblyneedexceptthefilesthemselvesisnotthatterriblybigadownload.Forsomeproductionpurposes,"everythinginthedatabasewemightpossiblyneed"isstillnotthatbigadealintermsofdownloadspeedormemoryfootprint.However,formanyproductionpurposesthedatabasebecomeslargeenoughtomakedeliveringanin-memorydatabaseproblematic.Inmanycasestherecommendedbestpracticeistoworkhardtobeaslazyasyoucan.Inotherwords,thebestpracticeisnottohavetheclientdownloadwhatmightsometimebeneeded,butdownloadwhatactuallyisneededona"just-in-time"basis.Aproductiondatabaseisdesignedsothatifanticipatedorunanticipatedreasonscausethedatasettogrowtentimesbigger,performancewillnotnecessarilytaketoomuchofahit.Butifitdoes,therearethingsthatcanbedonetocompensate.Ifyoudefineyoursolutiontodeliveranin-memorydatabase,theproblemswithatenfoldincreaseinthedatasetarenotsoeasilyresolved.Letusexploretheserversidefirst.HandlingdatabasesthroughDjangomodelsThepowerofAjaxisderivedpartlyfromstorageofinformationontheserver-side,quiteofteninadatabase.Djangoprovidesanexcellentplatformforhandlingthedatabaseside.Djangoattemptstooffercertainfacilitiesandthenletyougoyourownwayifyouwant.ItisnotasopinionatedaframeworkasRubyonRails.Youcanchoose,ifyouwant,touseotherpackageslikepsycopg2andsqlite3andbypassDjango'spersistencefacilitiesasmuchasyouwant,andyoucanalsohandlerawSQLwhile[86] Chapter4usingDjango'spersistencefacilities.However,Djangodatabasehandlingisverycarefullythoughtout,anditiswellworthtakingtheefforttounderstand.OnePythonicobservationfeedingintoDjango'sapproachisthatfrequentcontextswitchingbearsa"cognitivetax,"evenifthetemplatingisdonebyadeveloperwhohandlesthemodels,ratherthanthewebdesignersDjango'stemplatingisdesignedfor.TheDjangoapproachismeanttosparethedeveloperthecognitivestrainofrepeatedlyswitchingbetweenPythonandHTMLonasinglescreenofcode.WithSQLthecognitivetaxisevenmoreimportant.RepeatedswitchingbetweenPythonandSQLintroducesastrainthatmakesiteasiertointroducebugsandtheneasierforthebugstopassunnoticed.Django'sapproachismeanttoremovethisstrainandallowforpersistencetobehandledwithoutswitchingoutof"Pythonmode."Letuslookatthismoreconcretely.ModelsforanintranetemployeephotodirectoryLetuslookatthemodelsforanemployeeintranetphotodirectory.Forourdirectorywewillattempttocoverroutineinformationforemployees,suchasoffice,extension,taggedskills,andsoon.Ingeneral,solvingaproblemcorrectlyinDjangomeanstakingadvantageofexistingworkingparts,thosethatcomestandardwithDjangoandthosethatareavailable.Forthepurposesofanintranetemployeephotodirectory,itmightmakegoodsensetouseaPinaxsocialnetworkasabasis.OurreasonsfornotusingPinaxarenotmainlyduetoitsdeficiencies,butbecauseweneedasampleapplicationthatwillletustourDjangoandAjax.Pinaxmaysolveenoughoftheproblemforusthatwewouldnothaveasinformativeatour.ThisisnotacriticismofPinax;it'sjustastatementaboutwhatmakesagoodsampleapplicationthatwillgetthereader'shandsdirtywiththepowerofDjangoandAjax.TheOFFICE_CHOICESlistisonlyillustrative;weexpectthatitwouldbereplacedforproductioncode.Thisoneusesatwo-charactercode.Ifyouweremakingachoicelikethis,youcoulduseone,orthree,orten.Theshortformisstoredinthedatabaseandthelongformisdisplayedintheexampledropdownmenu:[87] Server-sideDatabaseSearchwithAjaxThecodedefiningthischoice,andthebeginningofourmodels.pysourcefile,is:#!/usr/bin/pythonfromdjango.dbimportmodelsimportdatetimeOFFICE_CHOICES=((u'CN',u'ChicagoNorthOffice,Illinois,USA'),(u'CS',u'ChicagoSouthOffice,Illinois,USA'),(u'WH',u'WheatonOffice,Illinois,USA'),(u'SY',u'SydneyOffice,NewSouthWales,Australia'),)TheExtensionFieldinthiscaseisimplementedasaTextField,butthismaybeonecasewhereaVARCHAR-basedCharFieldmakessensetostore,forexample,afourorfivedigitextensionfieldcanbesafelyassumed,andispossiblydeeplyentrenched.TheexistingExtensionFieldis:classExtensionField(models.TextField):passIfyouwantedtospecifyanextensionofuptofivedigits,youcoulddeclareafieldlikethefollowing:importdjango.formsimportreEXTENSION_LENGTH=5defis_extension(number):iflen(str(number))>EXTENSION_LENGTH:raiseforms.ValidationError(u'Thisextensionistoolong.')#eliflen(str(number))opagewillpresentaregularsearchinterfacewhetherornotauserisloggedin.IfaDuserisnotloggedin,thescreenshouldbedimmedandtheusershouldseeanAjaxmodaldialogallowingtheusertologin,asinthescreenshot.Inthischapterwewilllookatbothserver-sideandclient-sidecode,covering:•admin.py,usedtohavefunctionalitycalledonceandonlyonceontheserver-side.•functions.py,withdifferentutilityfunctions,suchasthe@ajax_login_requireddecorator.•views.py,whichhasfunctionsthatrenderwebpagesandtheserver'sresponsestoAjaxrequests.Oneviewshowshowwecanslowlybuildaviewbyhand,andanothershowsanexcellentDjangoshortcut.•style.css,whichhasbasicstylingforutilitarianneedsandusability.[106] Chapter5•search.html,whichhasbasicclient-sideAjaxforoursearch.NormallyAjaxisfactoredoutintoitsownfiles;thiscouldbedonetosomedegreebutwehavechosenanimplementationthatmeansthatsettingschangesarecentralizedtothetopofsettings.py.PartofoursolutionentailsJavaScriptfilesthatarepopulatedasDjangotemplatesratherthanbeingcompletelystatic,separatefiles.•TheDjangoadmininterface,whichwecanusetocreatetestdata.Let'sbegin!admin.py:administrativefunctionscalledonceInadmin.py,wehavecertainthingsthatwewishtohavecalledonceandonlyonce.ThissetsuptheadmininterfacetobeabletohandleEntities:importdjango.contrib.adminimportdirectory.modelsdjango.contrib.admin.autodiscover()django.contrib.admin.site.register(directory.models.Entity)django.contrib.admin.site.register(directory.models.Location)functions.py:project-specificfunctions,includingour@ajax_login_requireddecoratorThisisourfunctions.pyfile,whichincorporatesaStackOverflowsolution.TraditionallyinDjangothe@login_requireddecoratorisplacedbeforeaviewtohaveDjangochecktoseeifauserisauthenticatedandrequestauthenticationtheold-fashionedwayifauserisnotloggedin.This@ajax_login_requireddecoratorworksalongthesamelines:itisintendedforAjax-orientedviewsthatreturnJSON,shouldpassalongtheview'soutputiftheuserisauthenticated,andshouldotherwiseoutputJSONwithnot_authenticatedasTRUE.[107] SigningUpandLoggingintoaWebsiteUsingAjaxOntheclientside,theappropriatebehavioristotakeareturnedJSONvalue,andseeifithasnot_authenticateddefinedasTRUE.Ifso,theclientshouldbehaveinanappropriatefashiontotheusernotbeingauthenticated.Inourcase,wewillpresentamodaldialogformviajQueryUI,whichwillallowausertologinandeventuallyregisterforanaccount.Notethatweshouldallowanescapehatch,assometimestheuserwouldrathernotregister.Howeverimportantthewebsitemaybetous,itmaynotbesoimportanttoallusers,andwewanttogracefullyhandlethecasewheretheuserisn'tinterestedenoughtologinorregister.fromdjango.httpimportHttpResponseimportjsondefajax_login_required(view_function):defwrap(request,*arguments,**keywords):ifrequest.user.is_authenticated():returnview_function(request,*arguments,**keywords)output=json.dumps({'not_authenticated':True})returnHttpResponse(output,mimetype='application/json')wrap.__doc__=view_function.__doc__wrap.__dict__=view_function.__dict__returnwrapThisisagoodworkingexampleofcreatingadecorator.Itsdocstring(__doc__)anddictionary(__dict__)areassignedtobethoseoftheinnerfunction,andtheinnerfunctionisreturned.Weseethisdecoratorusedinthefirstfunctioninviews.py,aswillbeshowninthefollowingsection.views.py:functionsthatrenderwebpagesThepatternthathasbeenperhapsthemostpopular,themostused,andthemostmisusedistheMVCpattern.HerethereisaseparationofconcernsbetweentheModel,theView,andtheController.Theoretically,theconcernsareseparated.InDjango,themostcommonpatternistheMTVpattern,wherethethreelettersmeanModel,Template,andView.Wehaveeditedmodelsbefore,andwewillbeusingasimpletemplatelateroninthischapter.Herewewillbelookingattwoviews.Atleastintheirclassicaldefinitions,aDjangoMTVviewisnotquitethesamethingasanMVCview.WhileMVCandMTVmodelsmaybedoingbasicallythesamething,aDjangoMTVviewhasajobdescriptionthatincludessomeresponsibilitiesofanMVCcontroller.TherearemanysimilaritiesbetweenMVCandMTV,andbothmakeacarefulseparationofconcerns,butthedivisionoflaborisdifferentandtheDjangoviewhassomeoftheresponsibilitiesofboththeviewandcontrollerunderMVC.[108] Chapter5#!/usr/bin/pythonfromdjango.contrib.authimportauthenticate,loginfromdjango.coreimportserializersfromdjango.httpimportHttpResponsefromdjango.shortcutsimportrender_to_responsefromdirectory.functionsimportajax_login_requiredimportdirectory.modelsimportjsonimportreRESULTS_PER_PAGE=10defajax_login_request(request):try:request.POST[u'login']dictionary=request.POSTexcept:dictionary=request.GETTheprecedingcodeisimportantfordebuggingpurposes:itchecksforabasickeyfirstinrequest.POST,defaultingtorequest.GETifthisisnotfound.Thebenefitofthisisnotrelevanttoproduction,andmorepointedlyitshouldnotbeusedinproduction,wherecredentialsshouldbesubmittedviaPOSToverSSL.InproductionAjaxcallstothisfunctionshouldalwaysusePOSTratherthanGET,butfordebuggingpurposesitcanbehighlydesirabletofallbacktoGET.Thereasonistoprovideonewayofdealingwithanuncaughtexception.IfthereisanuncaughtexceptionandDjangohasDEBUGsettoTrue,itwillserveupadetailedandinformativeerrorpage.Unfortunately,ifjQueryisexpectingJSONandgetsadetailedHTMLerrorpage,itwillofferasingularlyuninformativeaccountoftheerror,whichisnotusefulfordebugging.Asaworkaroundforthisphenomenon,youcanmanuallymakeGETrequestsforatleastsomequeries.If,forexample,thebaseURLishttp://127.0.0.1:8000/ajax/loginforthisview,youcanaddGETdataattheendandmanuallyvisitaURLlikehttp://127.0.0.1:8000/ajax/login?login=mylogin&password=mypassword.[109] SigningUpandLoggingintoaWebsiteUsingAjaxIfyoursystemisn'tworkingcorrectly,andyour(POST)Ajaxqueryisthrowinganerror,manuallyqueryingbyGETinabrowserwindowcanbeagoodwaytoaccessadetailederrorpagedesignedtohelppinpointwhatexactexceptionisbeingthrown,andwhere.user=authenticate(username=dictionary[u'login'],password=dictionary[u'password'])ifuseranduser.is_active:login(request,user)result=Trueelse:result=Falseresponse=HttpResponse(json.dumps(result),mimetype=u'application/json')returnresponseTheauthenticate()andlogin()functionsdodifferentjobs.ForDjango'snormal(non-Ajax)loginprocedures,inactiveaccountscannotlogin.Buthereweneedtocheckforourselvesthatauthenticate()returnsavaliduser,andfurthermorethatthevaliduseris_active(thatis,hasnotbeenmarkedinactive).Thisfunctionbothlogstheuserin(ifappropriate)andthenreturnsaBooleanvaluetellingwhethertheloginhasbeensuccessful.Itisrecommendedprocedurenottodeleteuseraccounts,butsetthemtoinactive.Thisbestpracticeavoidscreatingholesinthedatabasewheresomethingreferstoauserwhoisnolongeravailable.Thefollowingfunctionisprotectedbythe@ajax_login_requireddecorator.Itisrelativelylong;letuswalkthroughit:@ajax_login_requireddefsearch(request):try:query=request.POST[u'query']dictionary=request.POSTexcept:query=request.GET[u'query']dictionary=request.GETHerewerepeatwhatwehavediscussedbefore:besidesthedecorator,welookforPOSTbutdefaulttoGETtomakedebuggingeasier.Notethatthedebugginginformationisexposedonlyifsettings.pyhasDEBUGsettoTRUE.Ingeneral,itisbadsecuritypracticetogiveinformationaboutinternalsinerrormessages.AndifDjango'ssettings.pyhasDEBUGsettoFALSE,itwillshutoffdetailederrormessagesandgivegenericmessagesintendedtoavoidexposingyourprogram'sinternalstructure.However,whenwearedevelopingwithDEBUGsettoTRUE,this[110] Chapter5givesusachancetoloadanequivalenttoaquerythattriggeredanerrorbytypingtheequivalentGETqueryinourbrowser.ThiscouldbemadeaDEBUGonlyfeature,butitmaynotbeclearwhattheadvantageoraddedsecuritywouldbetothat.split_query=re.split(ur'(?u)W',query)Hereareacoupleofnotesonsyntaxforthisregularexpressionoperation.Wehavebeenusingstringslikeu'test'toindicateaUnicodestring.Herewewanttogivearawstring,asisusualforregularexpressions.Thismeansthat(inthiscase)thebackslashwillnotbeinterpreted,asu'(?u)W'willbeinterpretedsimplyasthebackslashescapingtheW,whichdoesn'tdoanythingparticularlyinteresting,givingastringthatwilleffectivelybetreatedasu'(?u)W'.Wecouldachievetheintendedeffectbygivingu'(?u)\W',butmanuallykeepingtrackofextrabackslashesisaconsolationprizeasasolutioncomparedtosimplyaskingPythontointerpretthestringasraw.Thisisdonebychangingtheutour;notethattheucomesfirst:wewantur'(?u)W',notru'(?u)W';thelatterwillcauseasyntaxerror.Itisgenerallyrecommendedtomakeapracticeandhabitofusingrawstringswhendealingwithregularexpressions.Intheregularexpression,wepasstheUnicodeflagwith(?u)atthebeginningofthestring.ThiswillcausetheW,theregularexpressionsequenceforanon-wordcharacter,torecognizeUnicodewordversusnon-wordcharacters,ratherthan(forinstance)recognizingonlyASCIIwordcharactersandtreatingotheralphabets,ideograms,andsoonasnon-wordcharacters.ThepossibilityofconsistentlydoingthisinPythoniswhywearedoing,inPython,thekindofworkthatDjangodevelopersoftenoffloadtothedatabases.ThedatabasebackendsofferinconsistentsupportforUnicode-sensitivewordbreaks.whileu''insplit_query:split_query.remove(u'')Weareinterestedinblocksofwordcharacters;theregularexpressioncallmayleaveboththeblocksofwordcharacterswewant,andemptystrings.Weremovetheemptystringstogetthesequencesofwordcharactersincludedinthesearchstring.Thissolutionmaynotseparatewordsforlanguageswhereonecharacterrepresentsonewordratherthanasoundorpartofaword.Ifaworkaroundisneeded,userscanputaspacebetweeneachword.(Butifthisisasignificantconcern,itwouldbebettertoadaptthesolutionsothatpeoplecangivesearchtermsinthewaythatisnaturaltothem,andthecomputerseemstoauto-magicallydotherightthing.)results=[]forwordinsplit_query:forentityindirectory.models.Entity.objects.filter(name__icontains=word):[111] SigningUpandLoggingintoaWebsiteUsingAjaxifre.match(ur'(?ui)b'+word+ur'b',entity.name):entry={u'id':entity.id,u'name':entity.name,u'description':entity.description}ifnotentryinresults:results.append(entry)Foreachwordinthesplitquery,wesearchforobjectscontainingtheword,andthenifthewordmatches,weaddittoourlistofresultsifitisnotalreadythere.Thedirectory.models.Entity.objects.filter(name__icontains=word)isabriefexampleofthekindofworkhorseoneusesinDjango'sORM;theusualcorrectsolutionwouldbebasedonthat.Herewedoacase-insensitivesearchforobjectscontainingtheword.Soifthewordis"ed",thiswillturnupresultscontainingthenames"Ed","Ted","Edna",andothersimilarnames.Forthepurposesofthisdiscussion,weonlywantexactmatches,soweperformaregularexpressioncheck:(ui)requestsboth"Unicodeaware"and"case-insensitive,"andbspecifiesawordboundary,so"Ed"willmatch"ed"while"Ted"and"Edna"willnot.forentryinresults:score=0forwordinsplit_query:ifre.match(ur'(?ui)b'+word+ur'b',entry[u'name']):score+=1entry[u'score']=scoreThisisabare-bonesscoringalgorithm,andoneofmanyplaceswhereyoushouldbeabletoplayaroundwiththeprovidedstartingpointandmakeitbetter.defcompare(a,b):ifcmp(a[u'score'],b[u'score'])==0:returncmp(a[u'name'],b[u'name'])else:return-cmp(a[u'score'],b[u'score'])results.sort(compare)Thisdefinesafunctionthatsortsfirstbyscore,andthenbynameinalphabeticalorder.try:start=int(dictionary[u'start'])except:start=0try:results_per_page=int(dictionary[u'results_per_page'])except:results_per_page=RESULTS_PER_PAGE[112] Chapter5returned_results=results[start:start+results_per_page]response=HttpResponse(json.dumps([returned_results,len(results)]),mimetype=u'application/json')returnresponseHerewetakeasliceoftheresultset,andsendaresponsewithaJSONencodingoftheappropriatesliceofaresultset(sotheclientcanobtainpage2at10resultsperpage,orpage3of50resultsperpage,andsoonandsoforth),andthetotalnumberofresults.Bothofthesearehooksmeanttosupportpagination.ThedefaultbehavioriftheclientdoesnotspecifyourPOST/GETfieldsforpaginationistoreturnthefirst10results(orallresultsiftherearelessthan10)andthenumberofresultstotal.defhomepage(request):returnrender_to_response(u'search.html')Thistwo-lineviewisaDjangoshortcutinaction,andanexampleofhowDjangocansaveustime.style.css:basicstylingforusabilityTheCSSweuseisstraightforward.Thereislittleornament,butthereisstylingtomaketexteasiertoread,suchasrequestingafontthatwasverycarefullydesignedforreadabilityonscreen:oneofthebestfontsoptimizedforusability.Someofthestylingisutilitarian:theformtologinviaAjaxisnotshownbydefault.Thereisanotificationsdiv,whichhascertainstyles,andthetextinputforthesearchqueryisprominentandlarge:body{font-family:Verdana,Arial,sans;}#login_form{display:none;}#notifications{background-color:#ff8080;border:3pxsolid#800000;display:none;padding:20px;}[113] SigningUpandLoggingintoaWebsiteUsingAjax#search_form{margin-left:40px;}#submit{border:2pxsolidblack;font-size:2em;margin-left:10px;}search.html:atemplateforclient-sideAjaxNextwewillgothroughthetemplatethatdefinesthesearchpage.Wenoteinadvancethatwhilethistemplategetsalotdone,itisnotashowcaseofDjangotemplatingfeatures.TheDjangotemplatingengineisintendedfordesignersandallowsdesignerstoaccessvariables,performloopingandconditionals,andothermajorfeatureswhilenotbeinggiventhepowertoexecutearbitrarycode.Butevenwithoutharnessingmostofthetemplatingengine'sfeatures,wecanstillgetquitealotdone.{%extends"base.html"%}{%blockhead_title%}PhotoDirectory:Search{%endblockhead_title%}Thefirstlinestateswhichtemplateitextends,andthesecondgivesitatitle.ThecodethatfollowscontainsHTML,andinmanycaseshooksforAjax,toserveasabaseforclient-sidework.{%blockbody_main%}ThenotificationsdivismeanttoserveasahookthatwillbeavailableforAjaxmanipulationsasamessagearea.WithabitofAjaxshownlateron,notificationswillfadein,staythereforfiveseconds,andfadeout.Whenanotificationisfullyfadedinitcanlooklikethefollowing:[114] Chapter5TheHTMLis:
Thisisthesearchform.Inkeepingwithsemanticmarkup,wedefinetheformsemantically,andthenusejQuerytosubmitsearchesanddopartialpageupdateswiththeresults.NotethateveryHTMLelementhasanID.Thisisnotrequiredforsemanticmarkupassuch,butithelpsforstylingelementsviaCSSwhendesired,forcustomizingbehaviorviajQueryorotherAjaxtools,andforgracefuldegradation.Althoughwehavenotimplementedserver-sidesupportforthis,theformasitstandscouldworkperfectlywellforagracefuldegradationstrategytoletpeoplesearchtheold-fashionedwaywithJavaScriptturnedoff.(Suchsupportwillbeaddedlater.)[115] SigningUpandLoggingintoaWebsiteUsingAjaxLikethenotificationsdivseenearlier,thisisahookforAjaxtomanipulateand,inthiscase,populatewithresults.
Login
Password
Thisdiv,initiallyhidden,isusedforAjaxlogin.Fordemonstrationpurposes,thedirectoryisonlysearchabletouserswhoareloggedin,andthisistheloginformthatisdisplayedwhensomeonewhoisnotloggedintriestosearch.Ifyouwishthesearchfunctionalitytobeavailableregardlessofwhetherauserisloggedin,simplycommentorremovethe@ajax_login_requireddecoratorbeforesearch()inviews.py.Inthenexttwolines,weindicatethatwearedonewithcontentforthebody_mainhook,andaremovingontothepage-specificJavaScripthook,footer_javascript_page.{%endblockbody_main%}{%blockfooter_javascript_page%}WeincludeaJavaScriptfile,andthenwritefunctionalityforthispage.ThemainjQueryscripthasalreadybeenincludedinthebase.htmlDjangotemplate;wedon'tneedtodoanythingmoreforthathere.Thefollowingisahelperfunction.Rightnow,onboththeserver-sideandclient-sidewehavenotdiscussedtheseveralreasonsausermightnotbetreatedasloggedin:•Noattempthasbeenmadetologin•Theuserhasattemptedtologinwithaninvalidaccount•Theuserhasattemptedtologintoavalidaccountbutgotthepasswordwrong•Theuserhascorrectlyauthenticatedtoanaccountthatdoesnothaveis_validset[116] Chapter5Intermsofroomforexpansion,itmightbehelpfultodistinguishwhichoftheseseveraloptionshasoccurred.ParticularlyinthePalaeolithicera,whenprogrammerssatdownonstonebenchesandloggedintomonochrometerminals,itmadesensetoanswereitheraninvalidloginorpasswordwith"loginincorrect"andavoiddivulgingwhethertheloginorpasswordiswrong.Nowrefusingtogiveanyhintofananswerto"DidIgetmyloginorpasswordwrong?"isneedlessandusuallydoesn'tdomuchtoimprovewebsites'security.Onewaythisprogramcouldbeimprovedisfortheserverandclienttodistinguishbetweenpossiblereasonsanattemptedlogindidnotsucceed.{%endblockfooter_javascript_page%}TheDjangoadmininterfaceTheDjangoadmininterfaceisafullCreate,Read,Update,andDelete(CRUD)wnloadfromWow!eBookcapableinterface.ThebasicphilosophybehindtheinterfaceisthatsomeformsofoDCreate,Read,Update,andDeleteoperationsneedtobedoneinproject-specificways,whichareofteninterestingchallenges,andtheDjangoadmininterfaceisnotintendedforthatpurpose.WhattheadmininterfaceisintendedforistoaddresscertainroutineboilerplateCreate,Read,Update,andDeletefunctionality,sothatthemorechore-likefeaturesaretakencareofforyouwell.ThestepstomaketheadminsiteinDjango1.2.xavailableare:1.Gotoyoursettings.pyandensurethatINSTALLED_APPScontainsdjango.contrib.admin,django.contrib.auth,anddjango.contrib.contenttypes.2.Putalinelikethefollowinginyouradmin.pyfileforeverymodelthatyouwanttobeavailableviatheadmininterface:django.contrib.admin.site.register(directory.models.Entity)[122] Chapter53.Includeinyoururls.pythefollowinglines,firstlyatthetop:importadminAndinyoururlpatternsincludethefollowinglineoranythingofitsequivalent:(r'^admin/',include(admin.site.urls))4.Restarttheserver.Afterthat,weshouldseeascreenlikethefollowingoncewehaveloggedinwiththeuserwe'vecreated:WecanthenclickonEntity(orLocations),andaddanentity.Wehavesetthingsupsoallfieldsareoptional.Wewillbeworkinglaterontomakeamoreresponsive,Ajax-basedinterfacewithin-placeediting.Ithasbeensaid,"Wherethereisoutput,lettherebeinput."Ideally,ifsomeonewantstochangeinformationthatisdisplayed,andthatpersonhasprivilegestodoso,itshouldbepossibletoclickonthatinformationandupdateitthere,ratherthanscoutoutwhatotherpartofthesiteisresponsibleforgivinginput.However,theDjangoadmininterfaceisdecent,andwecanuseitfortestdatainparticular.[123] SigningUpandLoggingintoaWebsiteUsingAjaxSummaryWithallthepiecesputtogether,youshouldbeabletousetheDjangoadmininterfacetocreatetestdata,andthensearchandseeabasicresultspage.Thereisroomtoexpandandimproveinalmostallofwhatwehavedone,andweinviteyoutotinkeraroundandmakesomethingbetter.Butthisisasubstantialworkingsystem,andwehavedealtwithrealandseriouscode.Wehavecoveredadmin.py,forfunctionsthatwewantcalledexactlyoncewhentheserverstarts.Wehavealsocoveredfunctions.py,afilewehavecreatedforutilityfunctionsandinparticularan@ajax_login_requireddecoratorthatrequiresuserstoauthenticatebeforeseeingaview,butunlikethestandard@login_requiredworkhorseisoptimizedtorespondappropriatelytoAjaxclientsexpectingJSONratherthanwebbrowsersrenderingwholeHTMLpages.Wehavecoveredviews.py,whichdefinestwoviews.Again,theseareviewsunderDjango'sMTVModel-Template-Viewdivisionoflaborandseparationofconcerns,andnottheclassicMVCModel-View-Controllerseparationofconcerns.Wehavecoveredsearch.html,atemplatethatrendersthedesiredwebpagewithoutevenneedingtousemanyDjangotemplatingenginefeatures.WehavealsocoveredtheDjangoadmininterface,whichletsushandleroutineCreate,Read,Update,andDeleteoperationsalmostforfree.Wecan(andinthiscasewill)workonanotherinterface,butfornowwehaveapowerfulandfriendlywaytocreatetestdataandonethatwouldserveuswellforproductionpurposesifwewanted.jQueryisalibrarydesignedtoallowanecosystemofplugins.Inournextchapter,wewilllookatasnazzierinterfacethatheedsthewords,"Wherethereisoutput,lettherebeinput."WewilluseajQueryplugintoallowin-placeAjaxediting,givingaWeb2.0shine.Furthermore,wewilllogchanges:intermsofsuccessfulintranets,companiesthatturnonanonymitytendtoturnitoffveryquickly.Makingthedirectorymorelikeawikicanresultinbetterandmoreup-to-dateinformation.Thisisn'ttheonlyoption,butitistheonewewillexplorenext.Ontoin-placeediting![124] jQueryIn-placeEditingUsingAjaxjQueryasalibraryisintendedtohaveagood,solid,lightweightcorethatinvitesanecosystemofplugins.OrdinaryJavaScriptprogrammershavebeenknowntolearnjQueryandstartwritingnewpluginsontheirfirstday.Inthischapter,wewilltakeadvantageoftheJeditable–EditInPlacePluginForjQuery,withhomepageathttp://www.appelsiini.net/projects/jeditable.Jeditableisnottheonlypluginoutthere,northeonlygoodone;itisoneofanumberofinterestingandusefulpluginsthatareavailableinthejQuerypluginecosystem.IfyouwouldliketofindorexplorejQueryplugins,http://plugins.jquery.com/isagoodplacetostart.Inthischapter,wewillcover:•Howtoincludeapluginonapage•HowtousejQuerywiththeJeditableplugintoadd"edit-in-place"functionality•HowtouseDjangotokeeptrackoftheserver-sideresponsibilities•Howtomakeadetailedprofilepagethatsupportsin-placeediting,aswellasaddingthesamefunctionalitytothesearchresultspageInournextchapter,wewillcompletetheprofilepages,buildingonautocompletefeaturestoallowanemployee'ssupervisortobespecifiedwithoutalongdropdownmenu,aswellasexploringautocompleteforsearch. jQueryIn-placeEditingUsingAjaxThiswillallowustocreatearesultspageasshown:Ifsomeoneclicksonthename,forinstance,itbecomes:[126] Chapter6WhensomeoneclicksOK,thedataissavedontheserver,andalsoshownonthepage.Let'sgetstartedonhowthisworks.IncludingapluginWeincludeajQuerypluginonapagebyincludingjQuery,thenincludingtheplugin(orplugins,ifwehavemorethanone).Inourbase.html,weupdate:{%blockfooter_javascript_site%}{%endblockfooter_javascript_site%}Thisisfollowedbythefooter_javascript_sectionandfooter_javascript_pageblocks.Thismeansthatifwedon'twanttheplugin,whichisthelastinclusion,tobedownloadedforeachpage,wecouldputitinoverriddensectionandpageblocks.ThiswouldrenderasincludingthepluginafterjQuery.HowtomakepagesmoreresponsiveWewouldalsonotethatthesetup,withthreeJavaScriptdownloads,isappropriatefordevelopmentpurposesbutnotfordeployment.IntermsofYSlowclient-sideperformanceoptimization,therecommendedbestpracticeistohaveoneHTML/XHTMLhit,oneCSShitatthetop,andoneJavaScripthitatthebottom.Oneofthebasicprinciplesofclient-sideoptimization,discussedbySteveSouders(seehttp://developer.yahoo.com/yslow/)is,sinceHTTPrequestsslowthepagedown,therecommendedbestpracticeistohaveone(preferablyminified)CSSinclusionatthetopofthepage,andthenone(preferablyminified)JavaScriptinclusionatthebottomofeachpage.EachHTTPrequestbeyondthismakesthingsslower,socombiningCSSand/orJavaScriptrequestsintoasingleconcatenatedfileislow-hangingfruittoimprovehowquickandresponsiveyourwebpagesappeartousers.[127] jQueryIn-placeEditingUsingAjaxFordeployment,weshouldminifyandcombinetheJavaScript.Aswearedeveloping,wealsohaveJavaScriptincludedintemplatesandrenderedintothedeliveredXHTML;thismaybeappropriatefordevelopmentpurposes.Fordeploymentthough,asmuchsharedfunctionalityaspossibleshouldbefactoredoutintoanincludedJavaScriptfile.Forcontentthatcanbedeliveredstatically,suchasCSS,JavaScript,andevennon-dynamicimages,settingfar-futureExpires/Cache-Controlheadersisdesirable.(OnepracticeistoneverchangethecontentofapublishedURLforthekindofcontentthathasafar-futureexpirationset,andthenifitneedsupdating,insteadofchangingthecontentatthesamelocation,leavethecontentwhereitis,publishatanewlocationpossiblyincludingaversionnumber,andreferencethenewlocation.)Atemplatehandlingtheclient-siderequirementsHere'sthetemplate.Itsviewwillrenderitwithanentityandotherinformation.Atpresentitextendsthebasedirectly;itisdesirableinmanycasestohavethetemplatesthatarerenderedextendsectiontemplates,whichinturnextendthebase.Inoursimpleapplication,wehavetwotemplateswhicharedirectlyrenderedtowebpages.Oneisthepagethathandlesbothsearchandsearchresults—dealtwithearlier—andtheother,thepagethathandlesaprofile,fromthefollowingtemplate:{%extends"base.html"%}Followingearlierdiscussion,weincludehonorificsbeforethename,andpost-nominalsafter.Atthispointwedonotdoanythingtomakeiteditable.{%blockhead_title%}{{entity.honorifics}}{{entity.name}}{{entity.post_nominals}}{%endblockhead_title%}{%blockbody_main%}ThereisoneimportantpointaboutDjangoandthetitleblock.TheDjangodevelopersdonotfinditacceptabletowriteatemplatingenginethatproduceserrorsinproductionifsomeoneattemptstoaccessanundefinedvalue(bytypos,forinstance).Asaresultofthisdesigndecision,ifyouattempttoaccessanundefinedvalue,thetemplatingenginewillsilentlyinsertanemptystringandmoveon.Thismeansthatitissafetoincludeavaluethatmayormaynotexist,althoughtherearewaystotestifavalueexistsandisnonempty,anddisplayanotherdefaultvalueinthatcase.Wewillseehowtodothissoon.Let'smoveontothemainblock,definedbythelastlineofcode.[128] Chapter6Onceweareinthemainblock,wehaveanh1whichisalmostidenticaltothetitleblock,butthistimeitismarkeduptosupporteditinginplace.Letuslookatthehonorificsspan;thenameandpost_nominalsspansworkthesameway:

{%ifentity.honorifics%}{{entity.honorifics}}{%else%}Clicktoedit.{%endif%}Theclasseditisusedtogiveall$(".edit")itemssomebasicspecialtreatmentwithJeditable;thereisnothingmagicalabouttheclassname,whichcouldhavebeenreplacedbyuser-may-change-thisorsomethingelse.editmerelyhappenstobeagoodnamechoice,likealmostanygoodvariable/function/objectname.Wecreateanamingconventioninthespan'sHTMLIDwhichwillenabletheserversidetoknowwhich—ofalongandpossiblyopen-endednumberofthingswecouldintendtochange—istheonewewant.Inanutshell,theconventionismodelname_fieldname_instanceID.Thefirsttokenisthemodelname,andiseverythinguptothefirstunderscore.(Evenifwewereonlyinterestedinonemodelnow,itismorefutureprooftodesignsothatwecanaccommodatechangesthatintroducemoremodels.)ThelasttokenistheinstanceID,aninteger.Themiddletoken,whichmaycontainunderscores(forexamplepost_nominalsinthefollowingcode),isthefieldname.Thereisnospecificrequirementtofollowanamingconvention,butitallowsustospecifyanHTMLIDthattheserver-sideviewcanparseforinformationaboutwhichfieldonwhichinstanceofwhichmodelisbeingedited.Wealsoprovideadefaultvalue,inthiscaseClicktoedit,intendednotonlytoserveasaplaceholder,buttogiveusersasenseonhowthisinformationcanbeupdated.Wemightalsoobservethathereandinthefollowingcode,wedonotpresentlyhavechecksagainstraceconditionsinplace.Sonothinghereorinthefollowingcodewillstopusersfromoverwritingeachothers'changes.Thismaybetakenasachallengetorefineandextendthesolutiontoeitherpreventraceconditionsormitigatetheirdamage.{%ifentity.name%}{{entity.name}}[129] jQueryIn-placeEditingUsingAjax{%else%}Clicktoedit.{%endif%}{%ifentity.post_nominals%}{{entity.post_nominals}}{%else%}Clicktoedit.{%endif%}

Thisapproachisanexcellentfirstapproachbutinpracticeisanh1withthreeslotsthatsayClicktoeditonaprofile,creatingneedlessconfusion.Wemovetoasimplified:{{entity.name}}Takentogether,thethreestatementsformtheheadinginthisscreenshot:[130] Chapter6Ifweclickonthename(forinstance)itbecomes:Theimageispresentlyaplaceholder;thisshouldbeexpandedtoallowanimagetobeuploadediftheuserclicksonthepicture(implementingconsistent-feelingbehaviorwhetherornotwedosoviathesameplugin).Wealsoneedtheviewandurlpatternonthebackend:{%ifentity.image%}{%endif%}[131] jQueryIn-placeEditingUsingAjaxThebulkoftheprofileForsmallbitsoftext,weusetheeditCSSclass,whichwillbetransformedtoaninputoftypetextonclick(ordouble-clickormouseover,ifwewereusingJeditabledifferently).Thedescriptionisanexampleofsomethingthatwouldmorenaturallylenditselftoatextarea,sowewillusetheedit_textareaCSSclass,whichwillbeconfiguredtouseatextarea.

Description{{entity.description}}

TheDepartment,aswellasReportstofield,arenotarbitrarytextinourimplementation;theyareanotherentity(ifoneisspecified).Thiscouldappropriatelyenoughbeimplementedasadropdownmenu,butevenacarefullypruneddropdownmenucouldbelongandunwieldyforalargecompany.Wewill,inournextchapter,useanautocompletepluginforthisjob.Oneadditionalnoteonusability:Whendisplaying"label:value"informationonpages,particularlyheavilyusedpages,themostbasicoptionisnottouseanyemphasis:Name:J.SmithTohelppeople'seyesfindwhattheywant,oneobvioussolutionistoemphasizethelabel,asin:Name:J.SmithThisworkswellthefirsttime.However,ifpeoplearelookingatthesamesetoffields,inthesameorder,onawebpagetheyvisitrepeatedly,itisnolongerbesttoemphasizethelabels.Regularvisitorsalreadyknowwhatthelabelsare,andthemotiveforevenlookingatthelabelsistoseethevalue.Therefore,forourdirectory,wewillbeusingboldforthevalueratherthanthelabel:Name:J.Smith

Department:{{entity.department.name}}

Homepage:{%ifentity.homepage%}[132] Chapter6{%endif%}{%ifentity.homepage%}{{entity.homepage}}{%else%}Rightclicktochange.{%endif%}{%ifentity.homepage%}{%endif%}

Ifahomepageisdefined,wegivetheURL,wrappedinalinkandastrongthatmakesthelinkeditablebyrightclicking.Ifthelinkwerejusteditablebyaregularclick,Jeditablewouldshort-circuittheusualandexpectedbehaviorofclickingonalinktakingyoutothecorrespondingpageoropeningthecorrespondinge-mail.Toalloweditingwhilealsoallowingnormaluseoflinksonaprofilepage,weassigntherightclickratherthanclickeventtobethewaytoallowediting.FromaUIconsistencyperspective,itmightbedesirabletoadditionallyalwaysallowarightclicktotriggerany(possible)editing.However,wewillleavethatonourwishlistfornow.WewilldefineJavaScriptlateroninthischapterthatwilladddesiredbehavior.WhitespaceanddeliveryTheformattingusedaboveispreferablefordevelopment;foractualdelivery,wemaywishtostripoutallwhitespacethatcanbestrippedout,forthispage:{%ifentity.homepage%}{%endif%}{%ifentity.homepage%}{{entity.homepage}}{%else%}Rightclicktochange.{%endif%}{%ifentity.homepage%}{%endif%}
Somebrowsersnowarebetteraboutthis,butithashappenedinthepastthatifyouhavewhitespacesuchasalinebreakbetweentheintendedtextofalinkandthetag,youcouldgetunwantedtrailingwhitespacewithavisibleunderlineontherenderedlink.Inaddition,pagesloadfasterifminified.Fordevelopmentpurposes,though,wewilladdwhitespaceforclarity.Inthenextcode,wewillhaveaspuriousspacebeforerenderedcommasbecausewearenotstrippingoutunnecessarywhitespace:

Email:{%foremailinemails%}[133] jQueryIn-placeEditingUsingAjax{{email.email}}{%ifnotforloop.last%},{%endif%}{%endfor%}Clicktoaddemail.

Thisallowse-mailstobeadded,likeso:[134] Chapter6FortheLocationfield,wearedeferringanintelligentwaytoletpeoplechooseanexistinglocation,orcreateanewone,untilthenextchapter.Fornowwesimplydisplayalocation'sidentifier,whichismeantasahuman-readableidentifierratherthanamachine-readableprimarykeyorotheridentifier:

Location:{{entity.location.identifier}}

ThisentailsachangetotheLocationmodel,toallow:classLocation(models.Model):identifier=models.TextField(blank=True)description=models.TextField(blank=True)office=models.CharField(max_length=2,choices=OFFICE_CHOICES,blank=True)postal_address=models.TextField(blank=True)room=models.TextField(blank=True)coordinates=GPSField(blank=True)ThePhonefieldisthelastonethatisusereditable.

Phone:{%ifentity.phone%}{{entity.phone}}{%else%}Clicktoedit.{%endif%}

Thefollowingfieldsarepresentlyonlydisplayed.TheReportstofieldshouldbeautocompletebased.TheStartdatefieldmightwellenoughbeleftaloneasafieldthatshouldnotneedtobeupdated,orfordemonstrationpurposesitcouldbesettoajQueryUIdatepicker,whichwouldpresumablyneedtohaveAjaxsavingfunctionalityadded.

Reportsto:{{entity.reports_to.name}}

[135] jQueryIn-placeEditingUsingAjax

Startdate:{{entity.start_date}}

{%endblockbody_main%}Page-specificJavaScriptThepage-specificJavaScriptfollows.Thefirstfewlinesenabletheedit,edit_rightclick,andedit_textareaCSSclassestohavein-placeediting:{%blockfooter_javascript_page%}{%endblockfooter_javascript_page%}[136] Chapter6SupportontheserversideThisfunctionprovidesaratherunadornedloggingofchanges.Thiscouldbeexpandedtologginginaformintendedformachineparsing,displayinviews,andsooninfunctions.py:deflog_message(message):log_file=os.path.join(os.path.dirname(__file__),directory.settings.LOGFILE)open(log_file,u'a').write(u"%s:%s "%(time.asctime(),message)Insettings.py,aftertheDATABASE_PORTisset:#RelativepathnameforuserchangeslogfilefordirectoryLOGFILE=u'log'Intheurlpatterninurls.py:(ur'^ajax/save',views.save),(ur'^profile/(d+)$',views.profile),Inviews.py,ourimportsectionhasgrowntothefollowing:fromdjango.contrib.authimportauthenticate,loginfromdjango.contrib.auth.decoratorsimportlogin_requiredfromdjango.coreimportserializersfromdjango.db.modelsimportget_modelfromdjango.httpimportHttpResponsefromdjango.shortcutsimportrender_to_responsefromdjango.templateimportContext,Templatefromdjango.template.defaultfiltersimportescapefromdjango.template.loaderimportget_templatefromdirectory.functionsimportajax_login_requiredimportdirectory.modelsimportjsonimportreInviews.pyproper,wedefineaprofileview,withtheregular@login_requireddecorator.(Weuse@ajax_login_requiredforviewsthatreturnJSONorotherdataforAjaxrequests,and@login_requiredforviewsthatreturnafullwebpage.)@login_requireddefprofile(request,id):entity=directory.models.Entity.objects.get(pk=id)emails=directory.models.EntityEmail.objects.filter(entity__exact=id).all()returnHttpResponse(get_template(u'profile.html').render(Context({u'entity':entity,u'emails':emails})))[137] jQueryIn-placeEditingUsingAjaxThefollowingviewsaveschangesmadeviain-placeedits:@ajax_login_requireddefsave(request):try:html_id=request.POST[u'id']value=request.POST[u'value']except:html_id=request.GET[u'id']value=request.GET[u'value']ifnotre.match(ur'^w+$',html_id):raiseException(u'InvalidHTMLid.')Firstwetest,specifically,forwhetheranewe-mailisbeingadded.ThelastparsedtokeninthatcasewillbetheIDoftheEntitythee-mailaddressisfor.match=re.match(ur'EntityEmail_new_(d+)',html_id)ifmatch:model=int(match.group(1))WecreateandsavethenewEntityEmailinstance:email=directory.models.EntityEmail(email=value,entity=directory.models.Entity.objects.get(pk=model))email.save()Welogwhatwehavedone,andforaviewservicingJeditableAjaxrequests,returntheHTMLthatistobedisplayed.Inthiscasewereturnanewlink,andre-runthescriptthatappliesin-placeeditfunctionalitytoallappropriateclasses,asdynamicallyaddedcontentwillnothavethishappenautomatically.OurmotiveisthatpeoplewillsometimeshitSaveandthenrealizetheymadeamistaketheywanttocorrect,andweneedtohandlethisasgracefullyasthecasewherethein-placeeditisperfectonthefirsttry.Weescapefordisplay:directory.functions.log_message(u'EntityEmailforEntity'+str(model)+u')addedby:'+request.user.username+u',value:'+value+u' ')returnHttpResponse(u''+value+u''+u'''Clicktoaddemail.'''%str(email.id))[138] Chapter6Theelseclauseisthenormalcase.Firstitparsesthemodel,field,andid:else:match=re.match(ur'^(.*?)_(.*)_(d+)$',html_id)model=match.group(1)field=match.group(2).lower()id=int(match.group(3))Thenitlooksuptheselectedmodel(underthedirectorymodule,ratherthananywhere),findstheinstancehavingthisID,setstheinstance'sfieldvalue,andsavestheinstance.Thesolutionisgeneric,anddoestheusualjobthatwouldbedonebycodelikeentity.name=new_name.selected_model=get_model(u'directory',model)instance=selected_model.objects.get(pk=id)setattr(instance,field,value)instance.save()Finally,welogthechangeandreturntheHTMLtodisplay,inthiscasesimplythevalue.Aswithpreviousexamples,weescapetheoutputagainstinjectionattacks:directory.functions.log_message(model+u'.'+field+u'('+str(id)+u')changedby:'+request.user.username+u'to:'+value+u' ')returnHttpResponse(escape(value))SummaryWehavenowgonefromabasicfoundationtocontinuingpracticalapplication.Wehaveseenhowtodividethelaborbetweentheclientsideandserverside.Weusedthistomakeaprofilepageinanemployeedirectorywhereclickingontextthatcanbeeditedenablesin-placeediting,andwehavestartedtolookatusabilityconcerns.Morespecifically,wehavecoveredhowtouseajQueryplugin,inourcaseJeditable,inasolutionforAjaxin-placeediting.WesawhowtouseJeditableinslightlydifferentwaystomoreappropriatelyaccommodateeditableplaintextandeditablee-mail/URLlinks.Wediscussedtheserver-sideresponsibilities,includingbothagenericsolutionforwhenanamingconventionisrequired.Welookedatanexampleofcustomizingbehaviorwhenwewantsomethingmorecloselytailoredtospecificcases(whichoftenispartofsolvingusabilityproblemswell),andalsohowadetailedprofilepagecanbeputtogether.Inthenextchapter,wewilladdresspartoftheprofilepagenotsolvedhere,namely,howtouseautocomplete-stylefunctionalitytoprovideawell-scalingalternativetoletpeoplechooseEntitiesforthedepartmentandreports_tofield.Let'slookatthatmoreclosely.[139] UsingjQueryUIAutocompleteinDjangoTemplatesInthischapter,wewillcovergroundintwodifferentdimensions.Firstofall,wewillusejQueryUI'sautocomplete,withDjangotemplatesandviews,coveringboththeserver-sideandclient-sideaspectsandexploreDjangoAjaxalittlemoredeeply.Second,wewillusesomethingmorelikeareal-worldprocessofdiscoverywhilewearedoingthis.Thatistosay,insteadofstartingimmediatelywithafinishedsolution,wewillshowwhatitisliketomeetroadblocksalongthewayandstilldeliveraworkingproject.Inthischapterwewillcover:•jQueryUI'sautocompleteandthemeroller•A"progressiveenhancement"combobox•Whatneedstobedoneontheserver-sideandclient-sidetodothefollowing:°UsingDjangotemplatingtodynamicallycreateelements°Client-sideeventhandlingtosendautocomplete-basedselectionstotheserver°DOMLevel0andiframe-basedalternativesontheclient-side°Extendingserver-sideDjangoAjaxviewstohandleupdatesfromtheclient°Refiningtheworkingsolution•AnexampleofpracticalproblemsolvingwhenissuesariseInpreviouschapters,wehaveexploredhowtogetasolutionworkingunderoptimalconditions.Herewe'lllookatwhatwecandowhentoolsdon'talwayswork. UsingjQueryUIAutocompleteinDjangoTemplatesAddingautocomplete:firstattemptForfurtherdevelopment,wewillbeusingajQuerytheme.Whatspecificthemecanbeusediscustomizable,butautocompleteandotherpluginsrequiresomethemesuchasjQueryUIThemerollerprovides.jQueryUIThemeroller,whichletsyoucustomizeandtweakatheme(orjustdownloadadefault),isavailableat:http://jqueryui.com/themeroller/Whenyouhavemadeanycustomizationsanddownloadedatheme,youcanunpackitunderyourstaticcontentdirectory.Inourbase.htmltemplate,afteroursite-specificstylesheet,wehaveaddedanincludetothejQuerycustomstylesheet(notethatyoumaydownloadadifferentversionnumberthanwehaveusedhere).{%blockhead_css_site%}{%endblockhead_css_site%}WewillbeusingthejQueryUIcombobox,whichoffersa"progressiveenhancement"strategybybuildingapagethatwillstillworkwithJavaScriptoffandwillbemoreaccessiblethanbuildingasolutionwithnothingbutAjax.Progressiveenhancement,abestpractice"Progressiveenhancement,"inanutshell,meansthatasmuchaspossibleyoubuildasystemthatworkswithoutJavaScriptorCSS,withsemanticmarkupandsimilarpractices,thenaddappearancewithCSS,andthencustomizebehaviorwithJavaScript.Atextbookexampleofcustomizingbehavioristomakeasortabletablewhichisoriginallysortable,inWeb1.0fashion,byclickingonalinkinatableheaderwhichwillloadaversionofthetablesortedbythatlink.Thenthelinksare"hijaxed"byusingJavaScripttosortthetablepurelybyJavaScriptmanipulationsofthepage,sothatifauserdoesnothaveJavaScripton,clickingonthelinksloadsafreshpagewiththetablesortedbythatcolumn,andiftheuserdoeshaveJavaScript,thesameendresultisachievedwithoutwaitingonanetworkhit.Inthiscase,wemarkupandpopulateadropdownmenuofavailableentities,whichtheJavaScriptwillhideandreplacewithanautocompletebox.Theoptiontagsfollowanamingconventionoffieldname.id;alltheautocompletefieldsareforfieldsofanentity,andthenamingconventionisnotdirectlyforthebenefitofserver-sidecode,butsothataneventlistenerknowswhichfieldithasbeengivenavaluefor.[142] Chapter7Herewefollowthesamebasicformulafordepartment,location,andreports_to.Weproducealistofallavailableoptions.Thefirstentryisfornoselecteddepartment/location/reports_to;asacourtesytotheuserweaddselected="selected"tothepresentlyselectedvaluesothattheformissmartenoughtorememberthelastselectedvalue,ratherthandefaultingtoa(presumablyunwanted)choiceeachtimetheuservisitsit.Thismuchofthecodeisrunonceandcreatesthebeginningofthecontainingparagraph,setsastrongtag,andcreatestheentryfornodepartmentselected(andselectsitifappropriate):

Department:—Select—Thenweloopthroughthelistofdepartments,creatinganoptionthathasavalueof"department."followedbytheidoftheentityprovidedasadepartment.Rememberthatearlierwesimplyusedalistofallentitiesfordepartments.Ifyouareinterestedintinkering,youcouldaddacheckboxtoindicatewhetheranentityshouldbeconsideredadepartmentorareports_tocandidate.(LocationsareawnloadfromWow!eBookdifferentdatatype,sonoparingshouldbeobviouslyhelpfulforthem.)oD{%fordepartmentindepartments%}{{department.name}}{%endfor%}Wethenclosetheselectandstrong,andmoveontothenextline.

[143] UsingjQueryUIAutocompleteinDjangoTemplatesThelocationandreports_toarehandledsimilarly:Location:Weaddthedefault,unselectedoption:—Select—Thenweloopthroughavailablelocationsandbuildtheiroptions.{%forlocationinlocations%}{{location.identifier}}{%endfor%}

AndlikewiseinReportsto:

Reportsto:—Select—{%forreports_toinreports_to_candidates%}{{reports_to.name}}{%endfor%}

[144] Chapter7Ourprofilepageismodifiedtoprovidemorevariablestothetemplate.Lateron,wemightidentifywhichentitiescanbeanentity'sdepartmentsandwhichentitiescanbereportedto,thusimprovingtheavailableoptionsbyparingawayirrelevantentities,butfornowwesimplyprovideallentities.@login_requireddefprofile(request,id):entity=directory.models.Entity.objects.get(pk=id)emails=directory.models.EntityEmail.objects.filter(entity__exact=id).all()all_entities=directory.models.Entity.objects.all()all_locations=directory.models.Location.objects.all()returnHttpResponse(get_template(u'profile.html').render(Context({u'entity':entity,u'emails':emails,u'departments':all_entities,u'reports_to_candidates':all_entities,u'locations':all_locations,})))Thisprovidesnice-lookingautocompletefunctionalitylike:[145] UsingjQueryUIAutocompleteinDjangoTemplatesHowever,there'sabitofaquirk.Whenweusethecodeasimplementedearlier,itdoesnotseemtosaveourchanges;theyarenotpersistentacrosspageviews.Areal-worldworkaroundInthecourseofwritingthisbook,adifficultywasencountered.Wewilltakeadvantageofthatopportunitytolookatfindingalternativeswhenthingsdonotwork.ForthoseinterestedinjQueryUIcompletespecifically,theStackOverflowquestionathttp://stackoverflow.com/questions/188442/whats-a-good-ajax-autocomplete-plugin-for-jqueryisrecommended,andaresponsewithnineupvotesasofthiswritingsaidautocompletedoesn'tworkasofroughlyjQueryUI1.6.http://pastie.org/362706isreportedasaworkingversion.However,let'slookataworkaroundwhenthingsdon'twork—becauseevenarecommendedpluginforagoodlibrarymaynotwork,ormaynotworkforus.However,wewillsucceedlateronworkingwiththeprovidedplugin.Thedifficultyinquestionisasfollows.jQueryUIincludesautocompletefunctionality.Asdownloadedinourcase,theautocompletefunctionalityappearedtoworkfromtheuserinterfaceperspective,butdidnotappeartobesavingdataontheserver.Acallbackfunctionhadbeencreatedand(attemptedly)registered,butitwasnotbeingcalled.Insertingalert()inthebeginningofthefunctiondidnottriggeranyalertboxes,andnoerrorappearedinthebrowserconsole.Consultingforumsathttp://forum.jquery.com/andhttp://stackoverflow.com/,whichareexcellentresources,didnotseemtoturnupresults.Abugwasfiledathttp://dev.jqueryui.com/,butthatdidnotgenerateanyresponsequickly.Supposingthatwehavedeadlineslooming,whatshouldwedoinacaselikethis?"Interest-basednegotiation":apowertoolforproblemsolvingwhenplanAdoesn'tworkAtthispointwemightdiscussatangentfrominterest-basednegotiation.FisherandUry'snegotiationclassicGettingtoYesdiscussestwobasickindsofnegotiation:hardandsoft.Hardnegotiationtriestobendaslittleaspossiblefromitsstatedposition;softnegotiation,suchasoftenoccursininformalandfriendlysettings,ismuchmoreflexible.ButtheybothsufferfromanAchillesheel:whenbothsidesstartbydefiningaposition,andtheonlyquestioniswhoisgoingtogivehowmuchfromtheirinitialpositions,theresultisalmostguaranteedtobesuboptimal.Ifyouplaythatgameinthefirstplace,youlosebecauseyouareplayingthewronggame.[146] Chapter7Thealternative,interest-basednegotiation,whichinvolvesfindingwhatinterestsexistonbothsidesanddoingcreativeproblemsolvingbasedonthoseinterests,ismuchmorelikelytoproduceawinnerforallinvolved.GettingtoYesdiscussesinterest-basednegotiationasapowertoolforhostilenegotiationswheretheothersidehastheupperhand,andperhapsitmaybe.Butsomeofthebestmileageyoucangetoutofinterest-basednegotiationsisinfriendlynegotiations.Onesuchsituationthatkeepscomingupatworkiswhensomeonehasbasicallyfiguredoutwhattheyconsiderasolutiontoaproblem,andthenasksforyoutoattendtotheimplementation.Forexample,amanagermighthavecometoasystemadministratorinthedayswhenpagerswerethehotnewthingandsaid,"Ourdiskfilleduplastnight!Inordertopreventthisfromhappeningagain,isthereanywayyoucansetupanautomaticprocesstosendtheoutputofadftoapagereveryfiveminutes?"Andalmosttheworstresponseinthatsituationis,"Yes,I'llgetrightonit."Incaseslikethese,ifthesolutionwasenvisioned,architected,anddesignedbysomeonenontechnical,andthetechnicalpersonisaskedonlytohandleimplementationdetails,thesolutionisalmostguaranteedtobewrong.Thecorrectsolutionisnottonegotiateonalevelofpositions,butofinterests.Thisparticularsolutionusesaprogramwhoseoutputisdesignedforafullterminalwindow,whichwouldbequitepainfultoscrollthroughonanearlypagerevenonce.Itisa"boywhocriedwolf"solutionthatmeanstothesystemadministrator,"Here'ssomespamyouhavetoscrollthrougheveryfiveminutestofindoutifthereisinterestingdata."Not,necessarily,thatitiswrongtosendsomethingtoapager.Itmightwellbeanappropriatesolutiontoperiodicallycheckandsendabriefmessagetothesystemadministratorsifthediskisfullerthansomethresholdpercentage,orifthediskisbeingfilledupbeyondsomethresholdpercentageperunitoftime.Butinmanycasesthecorrectresponseistopolitelyreceivethestatedpositionandthengetontoidentifyingtheinterestsinvolvedandtryingasbestyoucantocraftaposition.Inthiscase,weareapplyingtheprincipleofinterest-basednegotiationtonegotiationwiththecomputer.Ourinitialposition,"FollowthejQueryUIinstructions"hasnotproducedthedesiredresults,atleastnotyet.Sothenextthingwecandoisidentifyourinterests.Ourinteresthere,withoutdegradinganythingelse,istoprovideautocompletefunctionalitytotheuserinterface.Thisallowsatleastfourpotentialwaystogetpasttheobstacle:•Resolvetheproblemandcompletetheintendedsolution•Workaroundthebugusingthesameframework•FindanotherjQueryplugintohandleautocomplete•Useastandalonesolution,oranotherlibrary[147] UsingjQueryUIAutocompleteinDjangoTemplatesWedon'tneedtostaystuck;infact,wehaveseveraloptions.Thefirstoptioncannotberuledout,butrightnowwehavenotsucceeded.Let'ssaythat'snotanoptioninourcase.Thethirdandfourthpossibilitiesalmostcertainlyhavemultipleoptions,andmultipleliveoptions,butinthiscasewecanbendtherulesalittlebit,commentoutourevent-handlingcode,andgivealittlenudgetoletourjQueryUI-basedsolutionworkwithallofthenicetiesofusingjQueryUI.Wewillgowiththisworkaround,butwearenotjustaworkaroundawayfrombeingblockedbyabrickwall.Therearepresumablyseveralliveoptions,andthemorewethinkintermsofinterestsratherthanpositions,andidentifyintereststofeedintoproblemsolving,themoreabrickwallfadesintoacornucopiaofpossibilities.AfirstworkaroundTheworkaround,likemanyworkarounds,isitselfanexampleofinterest-basednegotiationwiththecomputer.Whatwewanttohappen,thatisn'thappeningyet,isforthedatatobesaved.TheobviouswayforustosavethedataisbyanXMLHttpRequestbasedcallbutitisnotstrictlyaninteresttosaythatwegothroughXMLHttpRequestorjQueryforthesubmission.Itwouldalsoworktohaveaformthatsubmittedthesamedatatoaniframe.Thisisnotafirstchoicesolution,butweshouldbemuchmorecautiousaboutrulingoutpositionsaltogetherthanidentifyingourinterests.Nowwedon'twantaspuriousiframeonthepage,butwecansetittodisplay:none;,andtreatitasabitbucket,oraUnix/dev/null.AndthegracefuldegradationsolutionprovidedbyjQueryUIusesaselect,sowecanspecifyanonchangefortheselectthatwillsubmittheformwhenevertheselectischanged,thatiswheneveranautocompletevalueisselected.jQueryUIallowsustodisplaytheselect,andwewilldothis,bothtoallowapersontoseeavailableoptions,andtoprovideachoiceaboutmeansofinput.Ourrevisedcodeisasfollowsinstatic/style.css:bitbucket,#bitbucket{display:none;}Inthetopofthebody_mainblockintemplates/profile.htmlweadd:[148] Chapter7Onedesignconsiderationfromourimplementationisthatitwrapseachoftheselecttags(thatpopulateanautocomplete)initsownformelement.Thismeansthatsemanticallywecannothaveallofthemindifferentbrseparatedlinesofthesamep,butthisgivesanopportunitytomakethedesignbetter.Ifweputeachfieldinitsownparagraph,itwillmakeamorereadableuseofwhitespace.Sowegiveeachfielditsownparagraph,wraptheparagraphswithselecttagsinformelementswhichsubmitviaPOSTtoourAjax-gearedURLandhaveatargetofbitbucket,addahiddenformelementwhichwillreceivespecialtreatmentontheserversideaswillbediscussedlater,andspecifyanonchangeformsubmissionforselect.Thisworkarounduseswhatisinformallycalled"DOMLevel0,"andisnotafirstchoice.Itdoes,however,allowustokeepa"closetojQuery"solution,withaworkaroundthatisreadilyreplacedbyamorepreferablesolutionifabugisfixed.Thepreviouscodeislargelythesame,butiswrappedinaformtag,andprecededbyahiddeninputdesignedtoensurethattheviewhasalltheinformationitneeds.

Department:Theselecthasanonchangesettosubmittheformwrappingit.Thiskindofapproach,informallyreferredtoasDOMLevel0scripting,isnotafirstchoicesolution;itisnotexactlysemanticmarkup.Butithasbeensaid,"Intheory,theoryandpracticearethesame.Inpractice,theoryandpracticearedifferent."Especiallyifyouhaveadeadline,givenachoicebetweenpuristsemanticmarkupthatdoesn'twork,andalesspuresolutionthatworks,choosethesolutionthatworks.(But,ofcourse,ifyouhaveachoicebetweenapuristsolutionthatworksandalesspuresolutionthatworks,choosethepuristsolutionthatworks,evenifitinvolvesmorelearningormoreworkupfront.)None{%fordepartmentindepartments%}{{department.name}}{%endfor%}

Theotherfieldsarechanged,ifslightly.Theyareintheirownparagraphs,whichissemanticallyatleastasgoodashavingthemoneperlineinalargeparagraph,andlendsitselftodisplaywithbetteruseofwhitespace—amajorbenefitifpeoplewillbeusingourdirectoryalot!Wedefinealinkforthehomepage:

Homepage:{%ifentity.homepage%}{%endif%}Withinthelinkwedefinestrong,whichismarkedupfor(right-click)editing,anddisplaytheURLinsidethelink:{{entity.homepage}}Thenweclosethelinkandparagraph:{%ifentity.homepage%}{%endif%}

[150] Chapter7Wedefinesimilar,butnotidentical,handlingforthee-mail:

Email:{%foremailinemails%}Weeliminatesomewhitespacesothattherewillnotbeaspacebetweenthelinkandtheseparatingcomma:{{email.email}}{%ifnotforloop.last%},{%endif%}{%endfor%}Clicktoaddemail.

Thelocationfields,asthereports_tofieldinthefollowingsnippet,followthesamepatternasthedepartmentpreviouslyseen.Wedefineaform:Wedefineahiddeninputsotheserver-sidecodehasaparsableidentifier:

Location:WedefineaselectwithaDOMLevel0submit:Webuildthefirstoptionforwhennothinghasbeenselected:None[151] UsingjQueryUIAutocompleteinDjangoTemplatesWepopulatetherestofthelist:{%forlocationinlocations%}{{location.identifier}}{%endfor%}Andweclosetagsthatneedtobeclosed:

ThePhonefieldisanin-placeeditfield.Itworksasaregularin-placeeditfield,notanautocomplete:

Phone:{%ifentity.phone%}{{entity.phone}}{%else%}Clicktochange.{%endif%}

Andfinally,wehaveourthirdandlastautocomplete,whichworkslikethefirsttwo:

Reportsto:None[152] Chapter7{%forreports_toinreports_to_candidates%}{{reports_to.name}}{%endfor%}

Startdate:{{entity.start_date}}

Thehandlerfollowsthesignatureforeventhandlersregistered,aswewishtoregisterthem.Whilewedonotusetheeventobject,wekeepitinthesignature.context.itemshouldbetheoptionthatwasselected.Itsvaluewilllooklikedepartment.1andismeanttocarryalltheinformationneededforthisfunction.Thedatasubmittedfollowsthesameid-valuestructureaswehavealreadyusedforin-placeeditinginthepreviouschapter,andsubmitsittothesameview.Wewillinfacthavedifferentserver-sidecodehandlingtheseselections.ThecodethatstorestextinafieldwillnottakeanIDandlookuptherightinstanceoftheintendedfield,atleastnotwithoutsignificantrefactoring.However,wearedevelopingwithananalogousinterfaceinmind:submitteddataintheid-valueformat,withtheIDfollowingtheModelName_fieldname_instanceIDnamingconvention.functionupdate_autocomplete(event,context){varsplit_value=context.item.value.split(".");if(split_value.length==2&&!isNaN(split_value[1])){varfield=split_value[0];varid=split_value[1];$.ajax({data:{id:"Entity_"+field+"_"+{{entity.id}},value:id,},[153] UsingjQueryUIAutocompleteinDjangoTemplatesurl:"/ajax/save",});};}BoilerplatecodefromjQueryUIdocumentationItiscommonplacewhenusingsoftwaretoincludeaddingboilerplatecode.Hereisanexample.WeinsertboilerplatecodefromthedocumentationpagesforjQueryUIathttp://jqueryui.com/demos/autocomplete/#combobox:(function($){$.widget("ui.combobox",{_create:function(){varself=this;varselect=this.element.hide();varinput=$("").insertAfter(select).autocomplete({source:function(request,response){varmatcher=newRegExp(request.term,"i");response(select.children("option").map(function(){vartext=$(this).text();if(this.value&&(!request.term||matcher.test(text)))return{id:this.value,label:text.replace(newRegExp("(?![^&;]+;)(?!<[^<>]*)("+$.ui.autocomplete.escapeRegex(request.term)+")(?![^<>]*>)(?![^&;]+;)","gi"),"$1"),value:text};}));},delay:0,change:function(event,ui){if(!ui.item){//removeinvalidvalue,//asitdidn'tmatchanything$(this).val("");returnfalse;[154] Chapter7}select.val(ui.item.id);self._trigger("selected",event,{item:select.find("[value='"+ui.item.id+"']")});},minLength:0}).addClass("ui-widgetui-widget-contentui-corner-left");$("").attr("tabIndex",-1).attr("title","ShowAllItems").insertAfter(input).button({icons:{primary:"ui-icon-triangle-1-s"},text:false}).removeClass("ui-corner-all").addClass("ui-corner-rightui-button-icon").click(function(){//closeifalreadyvisibleif(input.autocomplete("widget").is(":visible")){input.autocomplete("close");return;}//passemptystringasvaluetosearchfor,//displayingallresultsinput.autocomplete("search","");input.focus();});}});})(jQuery);[155] UsingjQueryUIAutocompleteinDjangoTemplatesTurningonAjaxbehavior(ortryingto)Wemakeautocompletesoutoftherelevantselects,andalsodisplaytheselects,whichthecombobox()callhidesbydefault.Theusernowhasachoicebetweentheselectandanautocompletebox.$(function(){$(".autocomplete").combobox();$(".autocomplete").toggle();Herewehavecodewhich,fromthedocumentation,mightbeexpectedtocalltheupdate_autocomplete()eventhandlerwhenaselectionorchangeismade.However,atthispointweencounterabendintheroad.Thebendintheroadisthis:thefollowingcommentedcode,whenuncommented,doesn'tseemtobeabletotriggerthehandlerbeingcalled.Whenitwasuncommented,andanalert()placedatthebeginningofupdate_autocomplete(),thealert()wasnottriggeredevenonce.Andtheusualsuspectsintermsofforumsandevenfilingabugdidnotsucceedingettingthehandlertobecalled.Thealert()wasstillnotcalled.Afterthiscode,let'slookatourupdatedcodeontheserverside,andthenseehowthisbendintheroadcanbeaddressed./*$(".autocomplete").autocomplete({select:update_autocomplete});$(".autocomplete").bind({"autocompleteselect":update_autocomplete});$(".autocomplete").bind({"autocompletechange":update_autocomplete});*/});Nowletusturnourattentiontotheserverside.CodeontheserversideHerewehavetheupdatedsave()viewwhichhasbeenexpandedtoaddressitsbroaderscope.WeaccepteitherGETorPOSTrequests,althoughrequeststhatalterdatashouldonlybemadebyPOSTforproductionpurposes,andsavethedictionaryforexploration.@ajax_login_requireddefsave(request):try:html_id=request.POST[u'id'][156] Chapter7dictionary=request.POSTexcept:html_id=request.GET[u'id']dictionary=request.GETIfwehaveoneoftheautocompletevalues,wehaveahiddenfieldnamedidwhichguaranteesthatanysubmissionwillhavethatfieldinitsdictionary,eitherrequest.POSTorrequest.GET.However,thedepartment,location,andreports_tofieldsarenotallnamedvalue,andwemanuallycheckforthemandusevalueasadefault:ifhtml_id.startswith(u'Entity_department_'):value=dictionary[u'department']elifhtml_id.startswith(u'Entity_location_'):value=dictionary[u'location']elifhtml_id.startswith(u'Entity_reports_to_'):value=dictionary[u'reports_to']else:value=dictionary[u'value']Weperformsomebasicvalidation.Ourcode'sHTMLIDshouldonlyconsistofwordcharacters:ifnotre.match(ur'^w+$',html_id):raiseException("InvalidHTMLid.")Thenwehandleseveralspecialcasesbeforethegeneral-purposecodethathandlesmost/ajax/saverequests.IfthereisanewEntityEmail,wecreateitandsaveit:match=re.match(ur'EntityEmail_new_(d+)',html_id)ifmatch:model=int(match.group(1))email=directory.models.EntityEmail(email=value,entity=directory.models.Entity.objects.get(pk=model))email.save()directory.functions.log_message(u'EntityEmailforEntity'+str(model)+u'addedby:'+request.user.username+u',value:'+value+u' ')returnHttpResponse(u''+value+u''+u'''Clicktoaddemail.'''%str(email.id))[157] UsingjQueryUIAutocompleteinDjangoTemplatesWehavetheaddedcodetolookuptheentity,lookuptheappropriatedepartment(ifany),assigntheupdateddepartment,andsavetheentity.Thistechniqueisrepeated,withslightvariation,forthelocationandreports_tofields.WestillneedtoreturnanHttpResponse,evenifthevalueisignored.Ontheclient-side,thefollowingcodewedevelopwillreportaDjangoerrorpagefordevelopmentpurposesbuteventhenwilldiscardthereportedvalueiftheupdaterunswithouterror.HerewehavethespecialcaseofanEntity'sdepartmentbeingset.Whilewecreatetheillusionontheclient-sidethatthisisjustthesamesortofsubmissionas(say)anEntity'sdescription,weneedtohandleafewthingsontheserversidetogivetheclient-sideasimpleappearanceof"Markituprightandsaveit,anditwillbesaved."elifhtml_id.startswith(u'Entity_department_'):entity_id=int(html_id[len(u'Entity_department_'):])department_id=int(value[len(u'department.'):])entity=directory.models.Entity.objects.get(pk=entity_id)ifdepartment_id==-1:entity.department=Noneelse:entity.department=directory.models.Entity.objects.get(pk=department_id)entity.save()returnHttpResponse(value)Thelocationcodeworksthesamewayasreports_toanddepartment:elifhtml_id.startswith(u'Entity_location_'):entity_id=int(html_id[len(u'Entity_location_'):])location_id=int(value[len(u'location.'):])iflocation_id==-1:entity.location=Noneelse:entity.location=directory.models.Location.objects.get(pk==location_id)entity.save()returnHttpResponse(value)elifhtml_id.startswith(u'Entity_reports_to_'):entity_id=int(html_id[len(u'Entity_reports_to'):])reports_to_id=int(value[len(u'reports_to.'):])entity=directory.models.Entity.object.get(pk=entity_id)ifreports_to_id==-1:entity.reports_to=Noneelse:[158] Chapter7entity.reports_to=directory.models.Entity.objects.get(pk==reports_to_id)entity.save()returnHttpResponse(value)Althoughitislast,thisisthemainstreamandmostgenerichandler,handlingtheusualcasesoftextfieldssupportingin-placeediting:else:match=re.match(ur'^(.*?)_(.*)_(d+)$',html_id)model=match.group(1)field=match.group(2)id=int(match.group(3))selected_model=get_model(u'directory',model)instance=selected_model.objects.get(pk=id)setattr(instance,field,value)instance.save()directory.functions.log_message(model+u'.'+field+u'('+str(id)+u')changedby:'+request.user.username+u'to:'+value+u' ')returnHttpResponse(escape(value))RefiningoursolutionfurtherThisapproachworks,butthereisroomtofurthercleanitup.Firstofall,wecansetthingsupinthebasetemplatesothat,fordevelopment,Django'sinformativeerrorpagesaredisplayed;wealsosetformsubmissionstoPOSTbydefault.WemakewnloadfromWow!eBookatargetdivforthenotifications:oD{%blockbody_site_announcements%}{%endblockbody_site_announcements%}{%blockbody_notifications%}{%endblockbody_notifications%}Thenweadd,tothefooter_javascript_siteblock,aslightlytweakedsend_notifications().Comparedtoourearliercode,insteadofdelayingfiveseconds,itdelaysfivesecondsplustwomillisecondspercharacterofthemessage.Thisdoesnothaveanoticeablydifferenteffectfornormal,shortnotifications,butitmeansthatifa60kDjangoerrorpageisservedup,youhavemoretimetoinspecttheerror.Wecouldtweakitfurthersothataboveathresholdlength,oronsomeotherconditions,thenotificationisonlydismissedbyexplicitlypressingabutton,butwewillstophere.functionsend_notification(message){[159] UsingjQueryUIAutocompleteinDjangoTemplates$("#notifications").html("

"+message+"

");setTimeout("$('#notifications').show('slow').delay("+(5000+message.length*2)+").hide('slow');",0);}Ournotificationsareahasseveraldifferentmessages,notallofwhichneedtobevisuallylabeledaserrors,sowemovefromared-basedstylingtoonethatissilverandgreyinstatic/css/style.css:#notifications{background-color:#c0c0c0;border:3pxsolid#808080;display:none;padding:20px;}Wecall,onpageload,$.ajaxSetup()tospecifyadefaulterrorhandler,andalsospecifyformsubmissionviaPOST.Thiswillneedtobechangedfordeployment,buttogetherthismeansthatavaluableDjangoerrorpageisdisplayedasanotificationwheneveranAjaxerroroccurs,whichisakindof"bestofbothworlds"solutionfordevelopment.$(function(){$.ajaxSetup({error:function(XMLHttpRequest,textStatus,errorThrown){send_notification(XMLHttpRequest.responseText);},type:"POST",});});Intheprofiletemplate,wewillremovethecontainingformelementsandthehiddeninputs,andreplacethecontentsoftheonchangeattributes.Wewillalsorenametheoriginalupdate_autocomplete()toupdate_autocomplete_handler(),leavingitavailableshouldtheoriginallyintendedapproachwork.Thenewupdate_autocomplete()willmaketheAjaxcall,submittingthesameinformationasthe(now)update_autocomplete_handler().Wewillremovethebitbucketiframe,althoughweleavetheCSSin,incaseabitbucketisdesiredlateron.Weareinapositiontechnicallytomakeonebigparagraphoutofseveralfieldsaswedidoriginally,butbreakingthemintotheirownparagraphswasafortunatechange,andwewillretainit.[160] Chapter7TheDepartmentparagraphnowlookslikeacrossbetweentheprevioustwoentries.Itisitsownparagraph,buttheformisgone.Thereisanonchangeattributeset,althoughitscontentsaredifferentfromearlier.Itcallsupdate_autocomplete()withanIDfollowingthenameconventionandthepresentvalueoftheselect.

Department:None{%fordepartmentindepartments%}{{department.name}}{%endfor%}

Thelocationandreports_toareasfollowsuit:

Location:None{%forlocationinlocations%}{{location.identifier}}{%endfor%}

[161] UsingjQueryUIAutocompleteinDjangoTemplatesTheReportstofieldalsofollowssuit:

Reportsto:None{%forreports_toinreports_to_candidates%}{{reports_to.name}}{%endfor%}

Andthat'sit.Wenowhaveaworking,andslightlymorepolishedinternally,implementationthatsupportsautocompleteandin-placeeditinglikethefollowing.Fortheautocomplete:[162] Chapter7Or,foranotherofseveralexamplesofuserinputthatwasallowed,hereisanin-placeeditusedtoaddanewemailaddress.SummaryAgain,wehavemovedintwodifferentdimensionsinthischapter.Thefirstdimensionistheobviousone:whatweneedontheclient-sideandserver-sidetogetautocompleteworkingwithjQueryUI.Theseconddimensionhastodowithcreativeproblemsolvingwhensomethinggoeswrong.WehavecoveredthenutsandboltsofjQueryUI'sautocomplete,andwherepluginscanbeobtained.Wecontinuewiththeconceptof"progressiveenhancement,"andaconcreteexample.Welookedatwhattoolswehaveontheserver-sideandclient-sidetodothis.WehavecontinuedtogetourhandsdirtywithDjangotemplatingtobuildthedesiredpages.[163] UsingjQueryUIAutocompleteinDjangoTemplatesWehavecoveredtheidealcaseofsettingupaneventlistenerthatwillcommunicatewiththeserverandkeepitupdatedwithpureAjax,lookedatDOMLevel0andiframe-basedalternativesontheclientside,andfurtherexpandedtheDjangoAjaxviewontheserversidesothatitwillaccommodateautocompleterequestsaswellastheoriginaledit-in-placerequests.Oncewehadaworkingsolution,welookedathowwecouldmakeitworkbetter.Wehavealsotakenacuefrombestpracticesinnegotiationtolookatgettingthebestfromacomputerwhenwecan'tgetwhatwewerefirstlookingfor.Inournextchapter,wewilllookatDjangoModelFormorhowtoeasilybuildformsfromDjangomodels.Wehaveagreatdealofpowerandcontrolifwearegoingtobuildinterfaces,butDjangoprovidessomebuilt-infeaturesthatcangetresultsquickly.Perhaps,forinstance,yourorganizationonlyhasafewlocationsanditisnotthebestuseofresourcestobuildafancyin-placeedit/autocompletepagefordatathatisupdatedmaybeonceamonth?Orperhapsyouhaveoneortwomainmodelsyouneedupdated,andseveralmorethatdon'tneedtobeupdatedthatoftenbutdoneedtobeavailableforediting?DjangoModelFormstotherescue![164] DjangoModelForm:aCSSMakeoverTheDjangoadmininterfaceprovidesaready-madesolutiontotheCreate,Read,Update,andDeleteoperations.TheideaisnotthattheDjangoadmininterfaceistheonlyrightwayforyourwebsitetohandlesuchoperations,butratherthatthechoreofcreatingthesamebasicfunctionalityisliftedoffyourshoulders.Youcancreatetheinterfaceyourselfforthekindofinterfacewebdevelopersfindinteresting,andleavetheremainingchoretoDjango'sadmininterface.AndDjango'sadmininterfaceshowcasesModelForm.WewillbelookingattheModelFormthatpowersDjango'sadmininterface,andathowwecanaddadmininterface-likefunctionalitywhereverwewant.Inthischapterwewillcover:•UsingModelFormtogeta"Hello,world!"ready-madeform•StylingModelFormandgivingapolishedpresentation•Goingunderthehoodandexploringadditionalcustomization•ModelForm'slimitations,whereitishelpful,andwhereitisnot•UsingCSStotakearawinitialappearanceandgiveavisualtransformationLet'sgetstarted!"Hello,world!"inModelFormOurapplicationasitstandsonlyallowsEntitiesandLocationstobecreatedfromtheDjangoadmininterface.Thismaynotbeaproblem.Forsomecompanies,itmaymakesenseforonepersonorgroupofpersonstoshoulderthatresponsibility,whileallowing"therestofus"tokeepthedetailsofpagesuptodate. DjangoModelForm:aCSSMakeoverHowever,someorganizationsmaynotwanttieredupdateabilities.IftheEntitymodelisusedforanythingemployeeswanttokeeptrackof,anorganizationmaydecide,"Secretariesdonotneedwrittenpre-authorizationfromtheCEOtoorderpencils."Withoutgivingfulladmininterfaceprivilegestoallusers,let'sexplorehowwecanuseModelFormtogivethesamekindofinterfacetoletusersaddEntitiesandLocations.Insomewaysthisinterfacemayactuallybenicerforcreatinganewuserfromscratch.Ourin-placeeditsystemworksverygracefullyforin-place,piecemealediting,butifyouwanttospecifymostavailablefields,anold-fashionedformmaybethebestsolution.WestartbymakingaModelFormthatintheDjangodocumentationissaidto"inherit"fromourmodel."Inherit"doesnotliterallymeanobject-orientedinheritance,butdescribessomethinganalogous.Inourmodels.pyfile,wedefinetwoadditionalclassesthatinobject-orientedfashioninheritfromModelForm,andintheModelFormsense"inherit"fromEntityandLocationrespectively.(Ifwehavenotdoneso,weaddtheappropriateimportatthetop.)importdjango.formsclassEntityForm(django.forms.ModelForm):classMeta:model=EntityclassLocationForm(django.forms.ModelForm):classMeta:model=LocationNextweaddviewsthatcheckifaformhasbeensubmitted,andifitisvalid,savesit.ThenitpopulatesaRequestContextandrendersittoaresponse:@login_requireddefmodelform_Entity(request):ifrequest.method==u'POST':form=directory.models.EntityForm(request.POST)ifform.is_valid():form.save()else:form=directory.models.EntityForm()variables=RequestContext(request,{u'form':form,u'title':u'Entity',})returnrender_to_response(u'modelform.html',variables)[166] Chapter8@login_requireddefmodelform_Location(request):ifrequest.method==u'POST':form=directory.models.LocationForm(request.POST)ifform.is_valid():form.save()else:form=directory.models.LocationForm()variables=RequestContext(request,{u'form':form,u'title':u'Location',})returnrender_to_response(u'modelform.html',variables)Weaddentriestotheurlconfinurls.pytocreatethemodels:(ur'^create/Entity',views.modelform_Entity),(ur'^create/Location',views.modelform_Location),Lastly,wecreateatemplate,modelform.html,whichwillwrapthetablecontentsprovided:{%extends"base.html"%}{%blockhead_title%}{{title}}{%endblockhead_title%}{%blockbody_header_title%}

{{title}}

{%endblockbody_header_title%}{%blockbody_main%}{{form}}
 
{%endblockbody_main%}[167] DjangoModelForm:aCSSMakeoverWecanvisithttp://127.0.0.1:8000/create/Entity;thiswillbringupthefollowingpage:Andnowalogged-inusercancreatenewEntitiesandLocations.Wehaveachieved"Hello,world!"levelsoffunctionality.ExpandingandcustomizingtheexampleAsitstandsnow,our"Hello,world!"functionalityworkstocreatenewEntitiesandLocations,butitdoesnotprovidetheabilitytolookupanalready-existingEntityorLocation.Let'sbeginbyupdatingtheurlpatternsinurls.py.Wechange"create"to"manage",becausepartof"bestpractices"istohavewell-chosennames,andwhile"create"accuratelydescribesthelinkabove,"manage"describesitsbroaderambitnow.Thoughthisisnotnecessaryonatestsystemthatnorealusershaveseen,weshouldalsoperformanyandallredirectssothatanyURLwehaveadvertisedendsupintherightplace.Usersshouldnevervisitalinkyouhavesentthemtobefore,andgetanerrorpage.[168] Chapter8First,weupdateurls.pyandreplacethecreate/Entityandcreate/Locationlines.Wechoosetobeforgivingofatrailingslashleftout,althoughaURLsuchashttp://example.com/manage/Entity/12wouldbeslightlybettertoadvertisethanaURLlikehttp://example.com/manage/Entity12.Weenclosesomeoftheinformationinparenthesessoitcanbesentasanargumenttotheviewsthatdealwiththem.(ur'^(create/Entity)',views.redirect),(ur'^(create/Location)',views.redirect),(ur'^manage/Entity/?(d*)',views.modelform_Entity),(ur'^manage/Location/?(d*)',views.modelform_Location),Inviews.py,weupdatetheincludesection:fromdjango.httpimportHttpResponse,HttpResponseRedirect,HttpResponsePermanentRedirectThenwecreatearedirectviewthatpermanentlyredirectsthetwoURLswewanttoredirect,andbydefaulttemporarilyredirectstothehomepageforanythingelse:defredirect(request,original_url):iforiginal_url==u'create/Entity':returnHttpResponsePermanentRedirect(u'/manage/Entity')eliforiginal_url==u'create/Location':returnHttpResponsePermanentRedirect(u'/manage/Location')else:returnHttpResponseRedirect(u'/')Weupdatemodelform_Entity()andmodelform_Location()totryandlookanitemupbyintegerprimarykey:defmodelform_Entity(request,id):ifrequest.method==u'POST':form=directory.models.EntityForm(request.POST)ifform.is_valid():form.save()else:try:form=directory.models.EntityForm(instance=directory.models.Entity.objects.get(pk=int(id)))except:form=directory.models.EntityForm()variables=RequestContext(request,{u'form':form,u'title':u'Entity',})returnrender_to_response(u'modelform.html',variables)[169] DjangoModelForm:aCSSMakeoverdefmodelform_Location(request,id):ifrequest.method==u'POST':form=directory.models.LocationForm(request.POST)ifform.is_valid():form.save()else:try:form=directory.models.LocationForm(instance=directory.models.Location.objects.get(pk=int(id)))except:form=directory.models.LocationForm()variables=RequestContext(request,{u'form':form,u'title':u'Location',})returnrender_to_response(u'modelform.html',variables)Wealsoclarifythetemplate'stitle:{%blockhead_title%}Manage{{title}}{%endblockhead_title%}CustomizingModelFormpages'appearanceInordertoobservethezero-one-infinityrule(eitheryoudon'tallowsomethingatall,oryouallowatmostone,oryouallowasmanyasavailableresourcessupport),wehaveusedTEXTbased,insteadofVARCHARbased,fields.WhenyouhaveaTEXTbasedfield,Djangoassumesthatyouwantthedatatobelarge,andsoitusesatextareainsteadofatextinput.Beforegoingfurther,Iwouldliketoaddressonequestion:whysomuchefforttocompensateforDjango'susingatextareahere?Theanswer,inshort,isthatwearelookingatapositiveskillthatisanassettoAjaxdevelopers.Saying"Thereisno'CSS'in'AsynchronousJavaScriptandXML,'"issomewhatlikesaying,"Thereisno'JSON'in'AsynchronousJavaScriptandXML.'"Itistrueinalegalisticsense,butanAjaxdeveloperwhoneedssomeoneelsetohandleJSONhasaliabilityandanAjaxdeveloperwhoneedssomeoneelsetohandleCSSisatamarkedprofessionaldisadvantage.WetakealemonservedtousbyDjango,namelythatwhenyouspecifyaTEXTdatabasefieldDjangohearsaTEXTAREAingeneratedHTML,andseehowfarwecangotomakelemonadefromthislemon.Theoveralltransformationismeanttobroadenthelistofbasescoveredandstrengthsthisbookdevelopedit,anditdoesnotstopatall,norshouldit,onceCSSrulesareinplace[170] Chapter8toaddressinitiallyunwieldyTEXTAREAheight.TheintentistoprovideaprogressivetransformationsuchasEdwardTufteoffersinhisnowclassicbookssuchasEnvisioningInformation,andmakethebestofagoodopportunity.DjangohearingTEXTAREAifyousayTEXTmeans,forourmodels,thattheformsuseupmoreverticalspacethantheyneedto.WewilluseCSStodomuchmorethanaddressjustthisissue.Thepageismarkedupsemanticallywhileleavinganycustomstylingtothedevelopers.CSSstylingdoesnothaveadirectwaytospecifyhowmanyrowsorcolumnsatextareahas—instead,widthandheightmaybeset.Thereareseveralunitsonecanuse;thebestpracticescalesupordownifpeoplechangethefontsize.Theemunitisonethatisproportionaltothefontsize,andifoneisspecifyingasize,useeminpreferencetopx/pt/in/cm.Also,dependingonwhatHTMLelementyouareusing,apercentagebasedoffthatelementmaybecalculated,browser-dependent,ontheelement'snativesizeoronsomethingelse.Styleanh2tobe80%tallandsomebrowserswillgive80%ofthebasesizeofanunstyledh2,butstilllargerthanunstyledtext,whileotherbrowserswillgive80%ofthesizeofunstyledtext,meaninganh2thatissmallerthanbothanunstyledh2andunstyledtext.Specifyingavalueinem,like1.5emor2em,bothavoidstheaccessibilityissuesofpx/pt/in/cm,andmitigatesagainstcertaincross-browserheadaches.Beforewestartstyling,ourpagelookslike:[171] DjangoModelForm:aCSSMakeoverLet'sstartstyling:textarea{height:1.5em;}Thisletsmuchmorefitonasinglepageasshowninthefollowing:Onalargemonitor,there'salotofscreenrealestatetotherightofthetextfieldsthatisn'tbeingused.Let'schangethat:{%blockhead_css_page%}{%endblockhead_css_page%}Insomebrowsers'defaultsettings,textareaelementshaveafixed-widthfont.Thebestpracticeonusabilityandaccessibilitygroundsistohaveaproportionalsans-seriffont;Verdanaisarecommendedchoice("Therefore,fromausabilityperspective,theclearwinnerisVerdana,"http://www.theinternetdigest.net/archive/websafefonts.html).Thisiscommonpracticebecauseitisabestpractice,andnotperceivedasabestpracticebecauseitisacommonpractice.Ifweupdatethetextareastyle:textarea{font-family:Verdana,Arial,sans;height:1.5em;width:100%;}ItwouldappeardifferentinFirefox,goingto:[173] DjangoModelForm:aCSSMakeoverWeaddmarginstothebodytoimprovetheeffectattherightofthescreeninparticular:body{padding-left:40px;padding-right:40px;}Andwespecifyatext-alignofright,insteadofcenter,forthethelementsDjangoModelFormusesforlabels.Atext-alignofleftwouldalsobeanimprovement,butnotquitethesame:th{text-align:right;}[174] Chapter8Wenowhaveapagethatlookslikethefollowing:Now,theremaybesomefieldsthatwewanttobeabitlonger:theDescription,unliketheIdentifierforinstance,maybemuchlongerthansomeotherfieldswillnormallybe.ThetextareaelementshaveHTMLIDslikeid_description,sowewnloadfromWow!eBookocanoverridethatelement'sheight:Dtextarea#id_description{height:10em;}[175] DjangoModelForm:aCSSMakeoverThisgivesmorescreenrealestatetotheDescription:Rightnowwehaveboldforvisualemphasis,butthisisinthewrongplace.Especiallyifpeoplearegoingtousetheformagainandagain,weshouldnotdrawtheireyestothelabels,wheretheboldemphasisisnow,buttothetextthatisinput.Boldlabelsarestandardpractice,butwhetherthisisthemostusablestandardpracticeiswhatisbeingdisputed.th{font-style:normal;font-weight:normal;text-align:right;}textarea{font-family:Verdana,Arial,sans;[176] Chapter8font-weight:bold;height:1.5em;width:100%;}There'sanoticeabledifference,butatthissizethetextinthetextareamaybehardtoread:Wecanaddressthisproblem,andmakethepageeasiertoread,byslightlyincreasingthefontsize:textarea{font-family:Verdana,Arial,sans;font-size:larger;font-weight:bold;height:1.5em;width:100%;}[177] DjangoModelForm:aCSSMakeoverThismakestextinthetextareaeasiertoread.Thebordersareslightlymoreforcefulthanweneed;alighttouchwouldhelp:textarea{border:1pxsolidsilver;font-family:Verdana,Arial,sans;font-size:larger;font-weight:bold;height:1.5em;width:100%;}Nowtheyarelessofadistraction:Nowwewillpayattentiontothetextinputelements,whichareasyetunstyled.ForthisformwecouldachievecomparablestylingwithCSS,asthepagesourceshowswhattheHTMLIDisforourtextinput,butthismeansthatthesolutionismorebrittle.RightnowwehaveonetemplatethatworksforbothEntitiesandLocations,andwewouldideallybuildopen-endedfunctionalitysothatoursolutionworkscorrectlyforfutureexpansion.[178] Chapter8jQuerycandothingsthatareCSS-likethatCSSitselfdoesnotdoverywell,orratherthatthetoptierofCSS3handlesperfectlybutwhichshouldnotbereliedonasavailableinCSSimplementationsforvisitors'browsers,suchasspecifystylesforallinputelementsoftypetext.Andso,insteadofhardcodingHTMLID'sinthepage-specificCSS,wewilldefineaCSSclasstoapplytoalltextinputs:input.text{border:1pxsolidsilver;font-size:larger;font-weight:bold;height:1.5em;width:100%;}ThenweusejQuerytoapplythatclasstoinputsoftypetext:Onenicetytohaveiszebrastriping.Slightvisualemphasishelpspreventrowsfromblendingandvisuallybleedingintoeachother.Wedefineevenandoddstyles,althoughevenisempty:.even{}.odd{background-color:#eeeeee;}Thenwerunintooneofthequirksofzero-basedandone-basedindicesrunningintoeachother,namely,jQuerycountsevenandoddrowsfromzeroratherthanone,sothefirstrowiseven,thesecondrowisodd,andsoon.Toremedythisbehavior,wedoacrossoverinregisteringthestylesintheJavaScript:$("tr:even").addClass("odd");$("tr:odd").addClass("even");[179] DjangoModelForm:aCSSMakeoverAndnowwehavethefollowingpage:Itwouldbenicetohavemutedzebrastripingforthetextareaelementsandtextinputelements.SowedefineemptyhooksforfurtherCSSdevelopmentsothatmaintainerswillseewhattheyneedtocustomizeandoverrideevenifwedonothingwithitbeforemaintainershavetodealwithit:.oddinput{}.oddtextarea{}.oddinput{background-color:#f4f4f4;}.oddtextarea{background-color:#f4f4f4;}[180] Chapter8Thereisaslightbreakvisuallybetweenthetablecells.Thissortofthingisnotenoughtonecessarilybenoticed,butsubtlydistractpeople.Wesetthetableelement'sborder-spacingpropertytozero,andslightlypadthandtd:table{border-spacing:0;width:100%;}td{padding:4px;width:100%;}th{padding:4px;font-style:normal;font-weight:normal;text-align:right;}Andthathastakenusfromaninitial,unstyledappearanceasshown:[181] DjangoModelForm:aCSSMakeoverToafinalresultthatcanbeseenandappreciated:TheinterestedreaderwhowantstoknowwheretheideascamefromisencouragedtoreadtheworksofEdwardTufte;EnvisioningInformationisasalientstartingpoint.Themakeoverhereislargelyanimitationofthekindofmakeoverhefleshesoutinhistexts.GoingunderModelForm'shoodDjangoModelFormisintendedtoallowaready-madeform,anditsmarkup,tobeeasilystyled.However,itisalsointendedtoallowmoreserver-sidecontrolifyouwant.WewillleaveourCSSandjQuerystylingastheyare,andgounderthehoodtocustomizethecontrol.[182] Chapter8Youmayormaynotwantallavailablefieldstoshowup.Djangosupportsboth"opt-out"and"opt-in"approaches.Theoretically,onecoulduseboth,althoughitisnotclearwhatkindofusecasewouldmakethatdesirable.OurEntitymodelhasactive,department,description,image,homepage,honorifics,name,post_nominals,phone,publish_externally,andreports_tofields.Ifwewishedtoallowanythingbutthehomepageandimagetobeset,wecould"opt-out"bysetting:classEntityForm(django.forms.ModelForm):classMeta:model=Entityexclude=(u'homepage',u'image')Orwecould"opt-in"bysetting:classEntityForm(django.forms.ModelForm):classMeta:model=Entityfields=(u'active',u'department',u'description',u'image',u'homepage',u'honorifics',u'name',u'post_nominals',u'phone',u'publish_externally',u'reports_to')Bydefault,ModelFormdisplaysfieldsintheordertheyweredeclaredonthemodel.Thisusuallyhasroomforimprovement.Thebestorderforprogrammerstoseeisforcodemaintainability,andthebestorderforuserstoseewillrarelybethesame.Wecanusethe"opt-in"approach,butspecifyanotherordertorearrangethefields:classEntityForm(django.forms.ModelForm):classMeta:model=Entityfields=(u'name',u'honorifics',u'post_nominals',u'description',u'homepage',u'phone',u'department',u'reports_to',u'active',u'publish_externally')Thisrearrangesthefieldsinawaythatmaymakemoresensethanouralphabetical-likeorder.TheDjangodocumentationtellsofanumberofotherthingsonecando,suchascustomizehowthedataiscleaned,effectivelyhavingseveralformsonthesamepage,andsoon.Butwewouldbestpausetolistentoa"stupid"question.[183] DjangoModelForm:aCSSMakeoverAnexcellent"stupid"question:where'sthee-mailslot?Youmayhavelookedatthescreenshotsandasked,"Aren'tEntitiesallowedtohavee-mailaddresses?Iseephonenumbersandaspaceforahomepage,butIdon'tseeane-mailaddressontheform."Andthatisapointworthpausingon.Thedesigndecisionwasmadetoallowunlimitede-mailaddresses,and,forpurposesofillustration,onlyonehomepageand/orphone.(Forproductionpurposes,onlylistingonephonenumberisadesignflaw.ItwouldbebettertoallowunlimitedURLsandphonenumbers,whichcanbedoneinthesamebasicwayweallowedunlimitede-mailaddresses.)Theimplementation,whichweworkedtoseamlesslyhideinourregulareditingpage,isthate-mailaddressesarestoredintheirownmodels,whichhaveaforeignkeytoanEntity.(Statusesarealsohandledthatway,althoughwehavenotpresentlyintegratedthemintotheuserinterface.)Logically,thee-mailsbelongtotheEntities,buttheimplementationissomethinglikealegalfiction.E-mailsarenotfieldsontheEntitiesandtheModelFormhasnowayofknowingwhatthee-mailaddresses(and,forproductionreadiness,URLsandphonenumbers,thelatterofwhichshouldbestoredwithadescription)arefromtheuser'sperspective.Wecouldchangethisbehaviorbyallowingatmostonee-mailaddress,whichwouldallowe-mailtobehandledgracefullybyModelForm.However,thiswouldbeabadcaseofthetailwaggingthedog.Thequestionweshouldbeansweringisnot"Whatcanwedotobestsupportimplementationconcerns?"but"Whatcanwedotobestsupportuserneeds?"(Aclean,easyimplementationisusuallypreferabletoahairyorinvolvedimplementation,butahairyorinvolvedimplementationthatservesuserneedswellisalwayspreferabletoaclean,easyimplementationthatservesuserneedsbadly.)AndthismeansthatModelFormmaynotbetherightsolutionforcreatingEntities.(ButthesolutionwehavedevelopedworksgracefullyandappropriatelyforLocations.)WhatweneedtodoforEntitiesissimplytocreateanAddNewlink.Let'saddoneatthetop-rightofboththeSearchandProfilepages.Todothat,weupdatethebeginningofprofile()inviews.py:@login_requireddefprofile(request,id):ifid=="new":entity=directory.models.Entity()entity.save()id=entity.id[184] Chapter8else:entity=directory.models.Entity.objects.get(pk=int(id))emails=directory.models.EntityEmail.objects.filter(entity__exact=id).all()Weinsert,justbeforethepreviousprofilelineinviews.pythefollowing:(ur'^profile/(new)$',views.profile),Inthisspecificcasewecouldplaceitanywhereonthelist,butinsomecasesordermattersbecausethefirstpatternthatsuccessfullymatchesarequestistheonethatiscalled.Nowweupdatethebase.htmltemplatetohaveahookforaddingthingsatthetopofthebodytag:{%blockbody%}{%blockbody_preamble_site%}{%endblockbody_preamble_site%}{%blockbody_preamble_section%}{%endblockbody_preamble_section%}{%blockbody_preamble_page%}{%endblockbody_preamble_page%}WewouldlikethisAddNewfeaturetobeavailablebothfromthemainsearchpageandfromprofilepages.Rightnowbothoftheseinheritdirectlyfrombase.html.Wewillcreateaninterveningtemplate,main.html,whichwillincludethelinkmain.htmlhas:{%extends"base.html"%}{%blockbody_preamble_section%}AddNew{%endblockbody_preamble_section%}Wechangesearch.htmlandprofile.htmltoextendmain.htmlratherthanbase.htmldirectly:{%extends"main.html"%}Wealsoaddstylingtostatic/css/style.css:div.standard_links{font-weight:bold;margin-right:15px;margin-top:15px;text-align:right;}[185] DjangoModelForm:aCSSMakeoverThisplacesasimple,nicelystyledlinkatthetop-rightoftheSearchandProfilepage.TheSearchpagenowlookslikethis:FollowingthislinkletstheusereditanewEntity,asshown:[186] Chapter8SummaryDjangoModelFormisveryeasytouse.Ifyouhaveamodelandwanttoletpeopleedititsfields,withoutdoingallthelegworkofimplementingthingsfromscratchyourself,ModelFormletsyou"cuttothechase,"developingyourowninterfacesforthereallyinterestingpartsofyourapplication,andfallingbacktoModelFormforthelessinterestingparts,particularlyifyouwanttoallowsomeoftheDjangoadmininterfacefunctionalitywithouthavingpeopleintheadmininterfaceitself.Themarkupitproducesisgood,semanticmarkup,anditiseasilystyledandcustomizedinappearance.Youcangounderthehoodandfurthercustomizewhathappensbeforeitoutputsthemarkup.However,itisalsolimited:itseemsnottoofferagracefulwaytohandlecaseslikeoure-mailaddresswherewehaveusedstandardtechniquestoassociateanunlimitednumberofe-mailaddresseswithoneEntity.ItwouldbepossibletomakeaModelFormpageforEntitiesandanotheroneforthee-mailaddressmodel,buttheresultingimplementationwouldbeconfusingtousers,whocanandshouldthinkofe-mailaddressesasjustonemorepieceofdatayoufilloutforapersonordepartment.Inthischapter,wehavecoveredallthebasicstogeta"Hello,world!"ModelFormupandrunning.WehavelookedathowtotakeadvantageofModelForm'sgoodmarkuptocustomizeandstyletheappearanceofthepagetobeeasierandmorepowerfultouse.Wehaveexploredthebeginningofpokingaroundunderthehoodtocustomizeappearance.WehaveseenModelForm'slimitations,whentouseit,andwhenitisnotthebestchoice(becauseitdoesnothandlesomelegalfictionsgracefully).AndinallthiswehavemadeavisualtransformationbyaCSSmakeover.Let'smoveontodatabaseandsearchhandling.Let'sgo![187] DatabaseandSearchHandlingOneearlyparadoxthatwasdiscoveredintheinitialWeb2.0buzzwasthattryingtobeproactiveandbemoreresponsivebybeingpreparedaheadoftimeendedupmakingapplicationslessresponsive,notmore.Theinitialexpectationthatpartialpageupdateswouldmakemoreresponsiveapplicationsmetwithasurprisewhenprogrammersmadeapplicationsmorelikeadesktopapplicationand,initiallyatleast,lessresponsive.Therecommendedbestpracticenowistobeaslazyaspossible.Inthischapter,wewill:•StartwithasolutionthatdemonstratestheeasyAHAH(AsynchronousHTTPAndHTML)technique,andplaystoDjango'sstrengths•Demonstrate"gracefuldegradation"thatallowsourapplicationtoserveupread-onlyaccesswithJavaScriptturnedoffSimplyput,wewillexploreasolutionwiththeresponsivenessofan"aslazyaspossible"solution.Behindthescenes,wewillshowhowthatsolutionshowcasesDjangostrengths.MovingforwardtoanAHAHsolutionLet'sstartwitha"fewermovingparts"solutionthatwillplaytoDjango'sstrengthsreasonablywell.Wewillmoveontoamorein-depthsolution,butsomehavesaid,"Everycomplexsystemthatworksisfoundtohaveevolvedfromasimplesystemthatworks,"andwecanmakearelativelysimplesystemthatworkswellbeforewestarttryingtopushtheenvelopeofwhatthesystemcando. DatabaseandSearchHandlingPartof"gracefuldegradation"meansmakingasolutionthatworkswithoutJavaScript.Inourcasewewillmakeamodestgoalof"read-only"functionalityaccommodatedwithoutJavaScript;thatis,wewillnotbeattemptinggracefuldegradationthatcanupdatethedatawithoutJavaScripton.ThatjobcanbedonewiththeDjangoadmininterface,byregisteringallrelevantmodelsaseditable,andaddingalink,possiblyenclosedin