Table of Contents - bedford-computing.co.ukbedford-computing.co.uk/learning/wp-content/... ·...

Post on 27-Feb-2020

6 views 0 download

Transcript of Table of Contents - bedford-computing.co.ukbedford-computing.co.uk/learning/wp-content/... ·...

1.1

1.2

1.3

1.4

1.5

1.6

1.7

1.8

1.9

1.10

1.11

1.12

1.13

1.14

1.15

1.16

1.17

1.18

1.19

1.20

1.21

1.22

1.23

1.24

TableofContentsIntroduction

Installation

HowtheInternetworks

Introductiontocommandline

Pythoninstallation

Codeeditor

IntroductiontoPython

WhatisDjango?

Djangoinstallation

YourfirstDjangoproject!

Djangomodels

Djangoadmin

Deploy!

Djangourls

Djangoviews-timetocreate!

IntroductiontoHTML

DjangoORM(Querysets)

Dynamicdataintemplates

Djangotemplates

CSS-makeitpretty

Templateextending

Extendyourapplication

DjangoForms

What'snext?

2

DjangoGirlsTutorial

ThisworkislicensedundertheCreativeCommonsAttribution-ShareAlike4.0InternationalLicense.Toviewacopyofthislicense,visithttps://creativecommons.org/licenses/by-sa/4.0/

IntroductionHaveyoueverfeltthattheworldismoreandmoreabouttechnologyandyouaresomehowleftbehind?Haveyoueverwonderedhowtocreateawebsitebuthaveneverhadenoughmotivationtostart?Haveyoueverthoughtthatthesoftwareworldistoocomplicatedforyoutoeventrydoingsomethingonyourown?

Well,wehavegoodnewsforyou!Programmingisnotashardasitseemsandwewanttoshowyouhowfunitcanbe.

Thistutorialwillnotmagicallyturnyouintoaprogrammer.Ifyouwanttobegoodatit,youneedmonthsorevenyearsoflearningandpractice.Butwewanttoshowyouthatprogrammingorcreatingwebsitesisnotascomplicatedasitseems.Wewilltrytoexplaindifferentbitsandpiecesaswellaswecan,soyouwillnotfeelintimidatedbytechnology.

Wehopethatwe'llbeabletomakeyoulovetechnologyasmuchaswedo!

Whatwillyoulearnduringthetutorial?Onceyou'vefinishedthetutorial,youwillhaveasimple,workingwebapplication:yourownblog.Wewillshowyouhowtoputitonline,sootherswillseeyourwork!

Itwill(moreorless)looklikethis:

Introduction

3

Ifyouworkwiththetutorialonyourownanddon'thaveacoachwhowillhelpyouincaseofanyproblem,wehavea

chatsystemforyou: .Weaskedourcoachesandpreviousattendeestobetherefromtimetotimeandhelpotherswiththetutorial!Don'tbeafraidtoaskyourquestionthere!

OK,let'sstartatthebeginning…

FollowingthetutorialathomeItisamazingtotakepartinaDjangoGirlsworkshop,butweareawarethatitisnotalwayspossibletoattendone.Thisiswhyweencourageyoutotryfollowingthistutorialathome.Forreadersathomewearecurrentlypreparingvideosthatwillmakeiteasiertofollowthetutorialonyourown.Itisstillaworkinprogress,butmoreandmorethingswillbecoveredsoonattheCodingisforgirlsYouTubechannel.

Ineverychapteralreadycovered,thereisalinkthatpointstothecorrectvideo.

AboutandcontributingThistutorialismaintainedbyDjangoGirls.Ifyoufindanymistakesorwanttoupdatethetutorialpleasefollowthecontributingguidelines.

Wouldyouliketohelpustranslatethetutorialtootherlanguages?

Introduction

4

Currently,translationsarebeingkeptoncrowdin.complatformat:

https://crowdin.com/project/django-girls-tutorial

Ifyourlanguageisnotlistedoncrowdin,pleaseopenanewissueinformingusofthelanguagesowecanaddit.

Introduction

5

Ifyou'redoingthetutorialathomeIfyou'redoingthetutorialathome,notatoneoftheDjangoGirlsevents,youcancompletelyskipthischapternowandgostraighttotheHowtheInternetworks?chapter.

Thisisbecausewecoverthesethingsinthewholetutorialanyway,andthisisjustanadditionalpagethatgathersalloftheinstallationinstructionsinoneplace.TheDjangoGirlseventincludesone"Installationevening"whereweinstalleverythingsowedon'tneedtobotherwithitduringtheworkshop,sothisisusefulforus.

Ifyoufindituseful,youcanfollowthroughthischaptertoo.Butifyouwanttostartlearningthingsbeforeinstallingabunchofstuffonyourcomputer,skipthischapterandwewillexplaintheinstallationparttoyoulateron.

Goodluck!

InstallationIntheworkshopyouwillbebuildingablog,andthereareafewsetuptasksinthetutorialwhichwouldbegoodtoworkthroughbeforehandsothatyouarereadytostartcodingontheday.

Chromebooksetup(ifyou'reusingone)Youcanskiprightoverthissectionifyou'renotusingaChromebook.Ifyouare,yourinstallationexperiencewillbealittledifferent.Youcanignoretherestoftheinstallationinstructions.

Cloud9

Cloud9isatoolthatgivesyouacodeeditorandaccesstoacomputerrunningontheinternetwhereyoucaninstall,write,andrunsoftware.Forthedurationofthetutorial,Cloud9willactasyourlocalmachine.You'llstillberunningcommandsinaterminalinterfacejustlikeyourclassmatesonOSX,Ubuntu,orWindows,butyourterminalwillbeconnectedtoacomputerrunningsomewhereelsethatCloud9setsupforyou.

1. InstallCloud9fromtheChromewebstore2. Gotoc9.io3. Signupforanaccount4. ClickCreateaNewWorkspace5. Nameitdjango-girls6. SelecttheBlank(secondfromtherightonthebottomrowwithorangelogo)

Nowyoushouldseeaninterfacewithasidebar,abigmainwindowwithsometext,andasmallwindowatthebottomthatlookssomethinglike:

Cloud9

yourusername:~/workspace$

Thisbottomareaisyourterminal,whereyouwillgivethecomputerCloud9haspreparedforyouinstructions.Youcanresizethatwindowtomakeitabitbigger.

VirtualEnvironment

Installation

6

Avirtualenvironment(alsocalledavirtualenv)islikeaprivateboxwecanstuffusefulcomputercodeintoforaprojectwe'reworkingon.Weusethemtokeepthevariousbitsofcodewewantforourvariousprojectsseparatesothingsdon'tgetmixedupbetweenprojects.

InyourterminalatthebottomoftheCloud9interface,runthefollowing:

Cloud9

sudoaptinstallpython3.5-venv

Ifthisstilldoesn'twork,askyourcoachforsomehelp.

Next,run:

Cloud9

mkdirdjangogirls

cddjangogirls

python3.5-mvenvmyvenv

sourcemyvenv/bin/activate

pipinstalldjango~=1.9.0

(notethatonthelastlineweuseatildefollowedbyanequalsign:~=).

Github

MakeaGithubaccount.

PythonAnywhere

TheDjangoGirlstutorialincludesasectiononwhatiscalledDeployment,whichistheprocessoftakingthecodethatpowersyournewwebapplicationandmovingittoapubliclyaccessiblecomputer(calledaserver)sootherpeoplecanseeyourwork.

ThispartisalittleoddwhendoingthetutorialonaChromebooksincewe'realreadyusingacomputerthatisontheinternet(asopposedto,say,alaptop).However,it'sstilluseful,aswecanthinkofourCloud9workspaceasaplaceorour"inprogress"workandPythonAnywhereasaplacetoshowoffourstuffasitbecomesmorecomplete.

Thus,signupforanewPythonAnywhereaccountatwww.pythonanywhere.com.

InstallPythonForreadersathome:thischapteriscoveredintheInstallingPython&CodeEditorvideo.

ThissectionisbasedonatutorialbyGeekGirlsCarrots(https://github.com/ggcarrots/django-carrots)

DjangoiswritteninPython.WeneedPythontodoanythinginDjango.Let'sstartbyinstallingit!WewantyoutoinstallPython3.5,soifyouhaveanyearlierversion,youwillneedtoupgradeit.

WindowsYoucandownloadPythonforWindowsfromthewebsitehttps://www.python.org/downloads/release/python-351/.Afterdownloadingthe*.msifile,youshouldrunit(double-clickonit)andfollowtheinstructionsthere.Itisimportanttorememberthepath(thedirectory)whereyouinstalledPython.Itwillbeneededlater!

Onethingtowatchoutfor:onthesecondscreenoftheinstallationwizard,marked"Customize",makesureyouscrolldowntothe"Addpython.exetothePath"optionandselect"Willbeinstalledonlocalharddrive",asshownhere:

Installation

7

Inupcomingsteps,you'llbeusingtheWindowsCommandLine(whichwe'llalsotellyouabout).Fornow,ifyouneedtotypeinsomecommands,gotoStartmenu→AllPrograms→Accessories→CommandPrompt.(OnnewerversionsofWindows,youmighthavetosearchfor"CommandPrompt"sinceit'ssometimeshidden.)

Linux

ItisverylikelythatyoualreadyhavePythoninstalledoutofthebox.Tocheckifyouhaveitinstalled(andwhichversionitis),openaconsoleandtypethefollowingcommand:

command-line

$python3--version

Python3.5.1

Ifyouhaveadifferent'microversion'ofPythoninstalled,e.g.3.5.0,thenyoudon'thavetoupgrade.Ifyoudon'thavePythoninstalled,orifyouwantadifferentversion,youcaninstallitasfollows:

DebianorUbuntu

Typethiscommandintoyourconsole:

command-line

$sudoapt-getinstallpython3.5

Fedora(upto21)

Usethiscommandinyourconsole:

command-line

Installation

8

$sudoyuminstallpython3

Fedora(22+)Usethiscommandinyourconsole:

command-line

$sudodnfinstallpython3

openSUSE

Usethiscommandinyourconsole:

command-line

$sudozypperinstallpython3

OSXBeforeyouinstallPythononOSX,youshouldensureyourMacsettingsallowinstallingpackagesthataren'tfromtheAppStore.GotoSystemPreferences(it'sintheApplicationsfolder),click"Security&Privacy,"andthenthe"General"tab.Ifyour"Allowappsdownloadedfrom:"issetto"MacAppStore,"changeitto"MacAppStoreandidentifieddevelopers."

Youneedtogotothewebsitehttps://www.python.org/downloads/release/python-351/anddownloadthePythoninstaller:

DownloadtheMacOSX64-bit/32-bitinstallerfile,Doubleclickpython-3.5.1-macosx10.6.pkgtoruntheinstaller.

VerifytheinstallationwassuccessfulbyopeningtheTerminalapplicationandrunningthepython3command:

command-line

$python3--version

Python3.5.1

Ifyouhaveanydoubts,orifsomethingwentwrongandyouhavenoideawhattodonext,pleaseaskyourcoach!Sometimesthingsdon'tgosmoothlyandit'sbettertoaskforhelpfromsomeonewithmoreexperience.

SetupvirtualenvandinstallDjangoPartofthissectionisbasedontutorialsbyGeekGirlsCarrots(https://github.com/ggcarrots/django-carrots).

Partofthissectionisbasedonthedjango-marcadortutoriallicensedundertheCreativeCommonsAttribution-ShareAlike4.0InternationalLicense.Thedjango-marcadortutorialiscopyrightedbyMarkusZapke-Gründemannetal.

Virtualenvironment

Installation

9

BeforeweinstallDjangowewillgetyoutoinstallanextremelyusefultooltohelpkeepyourcodingenvironmenttidyonyourcomputer.It'spossibletoskipthisstep,butit'shighlyrecommended.Startingwiththebestpossiblesetupwillsaveyoualotoftroubleinthefuture!

So,let'screateavirtualenvironment(alsocalledavirtualenv).VirtualenvwillisolateyourPython/Djangosetuponaper-projectbasis.Thismeansthatanychangesyoumaketoonewebsitewon'taffectanyothersyou'realsodeveloping.Neat,right?

Allyouneedtodoisfindadirectoryinwhichyouwanttocreatethevirtualenv;yourhomedirectory,forexample.OnWindowsitmightlooklikeC:\Users\Name\(whereNameisthenameofyourlogin).

NOTE:OnWindows,makesurethatthisdirectorydoesnotcontainaccentedorspecialcharacters;ifyourusernamecontainsaccentedcharacters,useadifferentdirectory,forexampleC:\djangogirls.

Forthistutorialwewillbeusinganewdirectorydjangogirlsfromyourhomedirectory:

command-line

$mkdirdjangogirls

$cddjangogirls

Wewillmakeavirtualenvcalledmyvenv.Thegeneralcommandwillbeintheformat:

command-line

$python3-mvenvmyvenv

Windows

Tocreateanewvirtualenv,youneedtoopentheconsole(wetoldyouaboutthatafewchaptersago–remember?)andrunC:\Python35\python-mvenvmyvenv.Itwilllooklikethis:

command-line

C:\Users\Name\djangogirls>C:\Python35\python-mvenvmyvenv

whereC:\Python35\pythonisthedirectoryinwhichyoupreviouslyinstalledPythonandmyvenvisthenameofyourvirtualenv.Youcanuseanyothername,butsticktolowercaseandusenospaces,accentsorspecialcharacters.Itisalsogoodideatokeepthenameshort–you'llbereferencingitalot!

LinuxandOSX

CreatingavirtualenvonbothLinuxandOSXisassimpleasrunningpython3-mvenvmyvenv.Itwilllooklikethis:

command-line

$python3-mvenvmyvenv

myvenvisthenameofyourvirtualenv.Youcanuseanyothername,butsticktolowercaseandusenospaces.Itisalsogoodideatokeepthenameshortasyou'llbereferencingitalot!

Installation

10

NOTE:OnsomeversionsofDebian/Ubuntuyoumayreceivethefollowingerror:

command-line

Thevirtualenvironmentwasnotcreatedsuccessfullybecauseensurepipisnotavailable.OnDebian/Ubuntusys

tems,youneedtoinstallthepython3-venvpackageusingthefollowingcommand.

apt-getinstallpython3-venv

Youmayneedtousesudowiththatcommand.Afterinstallingthepython3-venvpackage,recreateyourvirtual

environment.

Inthiscase,followtheinstructionsaboveandinstallthepython3-venvpackage:

command-line

$sudoapt-getinstallpython3-venv

NOTE:OnsomeversionsofDebian/Ubuntuinitiatingthevirtualenvironmentlikethiscurrentlygivesthefollowingerror:

command-line

Error:Command'['/home/eddie/Slask/tmp/venv/bin/python3','-Im','ensurepip','--upgrade','--default-pip']'

returnednon-zeroexitstatus1

Togetaroundthis,usethevirtualenvcommandinstead.

command-line

$sudoapt-getinstallpython-virtualenv

$virtualenv--python=python3.5myvenv

NOTE:Ifyougetanerrorlike

command-line

E:Unabletolocatepackagepython3-venv

theninsteadrun:

command-line

sudoaptinstallpython3.5-venv

WorkingwithvirtualenvThecommandabovewillcreateadirectorycalledmyvenv(orwhatevernameyouchose)thatcontainsourvirtualenvironment(basicallyabunchofdirectoryandfiles).

WindowsStartyourvirtualenvironmentbyrunning:

command-line

C:\Users\Name\djangogirls>myvenv\Scripts\activate

Installation

11

NOTE:onWindows10youmightgetanerrorintheWindowsPowerShellsaysexecutionofscriptsisdisabledonthissystem.InthosecasesopenanotherWindowsPowerShellandRunasAdministratortrydoingthisbeforecontinue:

command-line

C:\WINDOWS\system32>Set-ExecutionPolicy-ExecutionPolicyRemoteSigned

ExecutionPolicyChange

Theexecutionpolicyhelpsprotectyoufromscriptsthatyoudonottrust.Changingtheexecutionpolicym

ightexposeyoutothesecurityrisksdescribedintheabout_Execution_Policieshelptopicathttp://go.micros

oft.com/fwlink/?LinkID=135170.Doyouwanttochangetheexecutionpolicy?[Y]Yes[A]YestoAll[N]No[L

]NotoAll[S]Suspend[?]Help(defaultis"N"):A

LinuxandOSX

Startyourvirtualenvironmentbyrunning:

command-line

$sourcemyvenv/bin/activate

Remembertoreplacemyvenvwithyourchosenvirtualenvname!

NOTE:sometimessourcemightnotbeavailable.Inthosecasestrydoingthisinstead:

command-line

$.myvenv/bin/activate

Youwillknowthatyouhavevirtualenvstartedwhenyouseethatthepromptinyourconsoleisprefixedwith(myvenv).

Whenworkingwithinavirtualenvironment,pythonwillautomaticallyrefertothecorrectversionsoyoucanusepythoninsteadofpython3.

OK,wehaveallimportantdependenciesinplace.WecanfinallyinstallDjango!

InstallingDjangoNowthatyouhaveyourvirtualenvstarted,youcaninstallDjango.

Beforewedothat,weshouldmakesurewehavethelatestversionofpip,thesoftwarethatweusetoinstallDjango.Intheconsole,runpipinstall--upgradepip.

Thenrunpipinstalldjango~=1.9.0(notethatweuseatildefollowedbyanequalsign:~=)toinstallDjango.

command-line

(myvenv)~$pipinstalldjango~=1.9.0

Downloading/unpackingdjango==1.9

Installingcollectedpackages:django

Successfullyinstalleddjango

Cleaningup...

onWindows

Installation

12

IfyougetanerrorwhencallingpiponWindowsplatform,pleasecheckifyourprojectpathnamecontainsspaces,accentsorspecialcharacters(forexample,C:\Users\UserName\djangogirls).Ifitdoes,pleaseconsiderusinganotherplacewithoutspaces,accentsorspecialcharacters(suggestion:C:\djangogirls).Createanewvirtualenvinthenewdirectory,thendeletetheoldoneandtrytheabovecommandagain.(Movingthevirtualenvdirectorywon'tworksincevirtualenvusesabsolutepaths.)

onWindows8andWindows10

YourcommandlinemightfreezeafterwhenyoutrytoinstallDjango.Ifthishappens,insteadoftheabovecommanduse:

command-line

C:\Users\Name\djangogirls>python-mpipinstalldjango~=1.9.0

onLinux

IfyougetanerrorwhencallingpiponUbuntu12.04pleaserunpython-mpipinstall-U--force-reinstallpiptofixthepipinstallationinthevirtualenv.

That'sit!You'renow(finally)readytocreateaDjangoapplication!

InstallacodeeditorTherearealotofdifferenteditorsanditlargelyboilsdowntopersonalpreference.MostPythonprogrammersusecomplexbutextremelypowerfulIDEs(IntegratedDevelopmentEnvironments),suchasPyCharm.Asabeginner,however,that'sprobablylesssuitable;ourrecommendationsareequallypowerful,butalotsimpler.

Oursuggestionsarebelow,butfeelfreetoaskyourcoachwhattheirpreferencesare–it'llbeeasiertogethelpfromthem.

GeditGeditisanopen-source,freeeditor,availableforalloperatingsystems.

Downloadithere

SublimeText2SublimeTextisaverypopulareditorwithafreeevaluationperiod.It'seasytoinstallanduse,andit'savailableforalloperatingsystems.

Downloadithere

AtomAtomisanextremelynewcodeeditorcreatedbyGitHub.It'sfree,open-source,easytoinstallandeasytouse.It'savailableforWindows,OSXandLinux.

Downloadithere

Whyareweinstallingacodeeditor?Youmightbewonderingwhyweareinstallingthisspecialcodeeditorsoftware,ratherthanusingsomethinglikeWordorNotepad.

Installation

13

Thefirstreasonisthatcodeneedstobeplaintext,andtheproblemwithprogramslikeWordandTexteditisthattheydon'tactuallyproduceplaintext,theyproducerichtext(withfontsandformatting),usingcustomformatslikeRTF(RichTextFormat).

Thesecondreasonisthatcodeeditorsarespecialisedforeditingcode,sotheycanprovidehelpfulfeatureslikehighlightingcodewithcolouraccordingtoitsmeaning,orautomaticallyclosingquotesforyou.

We'llseeallthisinactionlater.Soon,you'llcometothinkofyourtrustyoldcodeeditorasoneofyourfavouritetools.:)

InstallGitWindows

YoucandownloadGitfromgit-scm.com.Youcanhit"next"onallstepsexceptforone;inthefifthstepentitled"AdjustingyourPATHenvironment",choose"UseGitandoptionalUnixtoolsfromtheWindowsCommandPrompt"(thebottomoption).Otherthanthat,thedefaultsarefine.CheckoutWindows-style,commitUnix-stylelineendingsisgood.

MacOS

DownloadGitfromgit-scm.comandjustfollowtheinstructions.

Linux

Ifitisn'tinstalledalready,gitshouldbeavailableviayourpackagemanager,sotry:

DebianorUbuntu

command-line

$sudoapt-getinstallgit

Fedora(upto21)

command-line

$sudoyuminstallgit

Fedora(22+)

command-line

$sudodnfinstallgit

openSUSE

command-line

$sudozypperinstallgit

CreateaGitHubaccountGotoGitHub.comandsignupforanew,freeuseraccount.

Installation

14

CreateaPythonAnywhereaccountNextit'stimetosignupforafree"Beginner"accountonPythonAnywhere.

www.pythonanywhere.com

Whenchoosingyourusernamehere,bearinmindthatyourblog'sURLwilltaketheformyourusername.pythonanywhere.com,sochooseeitheryourownnickname,oranameforwhatyourblogisallabout.

StartreadingCongratulations,youareallsetupandreadytogo!Ifyoustillhavesometimebeforetheworkshop,itwouldbeusefultostartreadingafewofthebeginningchapters:

Howtheinternetworks

Introductiontothecommandline

IntroductiontoPython

WhatisDjango?

Enjoytheworkshop!Whenyoubegintheworkshop,you'llbeabletogostraighttoYourfirstDjangoproject!becauseyoualreadycoveredthematerialintheearlierchapters.

Installation

15

HowtheInternetworksForreadersathome:thischapteriscoveredintheHowtheInternetWorksvideo.

Thischapterisinspiredbythetalk"HowtheInternetworks"byJessicaMcKellar(http://web.mit.edu/jesstess/www/).

WebetyouusetheInterneteveryday.Butdoyouactuallyknowwhathappenswhenyoutypeanaddresslikehttps://djangogirls.orgintoyourbrowserandpressenter?

Thefirstthingyouneedtounderstandisthatawebsiteisjustabunchoffilessavedonaharddisk.Justlikeyourmovies,music,orpictures.However,thereisonepartthatisuniqueforwebsites:theyincludecomputercodecalledHTML.

Ifyou'renotfamiliarwithprogrammingitcanbehardtograspHTMLatfirst,butyourwebbrowsers(likeChrome,Safari,Firefox,etc.)loveit.Webbrowsersaredesignedtounderstandthiscode,followitsinstructions,andpresentthesefilesthatyourwebsiteismadeof,exactlythewayyouwant.

Aswitheveryfile,weneedtostoreHTMLfilessomewhereonaharddisk.FortheInternet,weusespecial,powerfulcomputerscalledservers.Theydon'thaveascreen,mouseorakeyboard,becausetheirmainpurposeistostoredataandserveit.That'swhythey'recalledservers–becausetheyserveyoudata.

OK,butyouwanttoknowhowtheInternetlooks,right?

Wedrewyouapicture!Itlookslikethis:

Lookslikeamess,right?Infactitisanetworkofconnectedmachines(theabove-mentionedservers).Hundredsofthousandsofmachines!Many,manykilometersofcablesaroundtheworld!YoucanvisitaSubmarineCableMapwebsite(http://submarinecablemap.com)toseehowcomplicatedthenetis.Hereisascreenshotfromthewebsite:

HowtheInternetworks

16

Itisfascinating,isn'tit?Butobviously,itisnotpossibletohaveawirebetweeneverymachineconnectedtotheInternet.So,toreachamachine(forexample,theonewherehttps://djangogirls.orgissaved)weneedtopassarequestthroughmany,manydifferentmachines.

Itlookslikethis:

HowtheInternetworks

17

Imaginethatwhenyoutypehttps://djangogirls.org,yousendaletterthatsays:"DearDjangoGirls,Iwanttoseethedjangogirls.orgwebsite.Sendittome,please!"

Yourlettergoestothepostofficeclosesttoyou.Thenitgoestoanotherthatisabitnearertoyouraddressee,thentoanother,andanotheruntilitisdeliveredatitsdestination.Theonlyuniquethingisthatifyousendmanyletters(datapackets)tothesameplace,theycouldgothroughtotallydifferentpostoffices(routers).Thisdependsonhowtheyaredistributedateachoffice.

HowtheInternetworks

18

Yes,itisassimpleasthat.Yousendmessagesandyouexpectsomeresponse.Ofcourse,insteadofpaperandpenyouusebytesofdata,buttheideaisthesame!

Insteadofaddresseswithastreetname,city,zipcodeandcountryname,weuseIPaddresses.YourcomputerfirstaskstheDNS(DomainNameSystem)totranslatedjangogirls.orgintoanIPaddress.Itworksalittlebitlikeold-fashionedphonebookswhereyoucanlookupthenameofthepersonyouwanttocontactandfindtheirphonenumberandaddress.

Whenyousendaletter,itneedstohavecertainfeaturestobedeliveredcorrectly:anaddress,astamp,etc.Youalsousealanguagethatthereceiverunderstands,right?Thesameappliestothedatapacketsyousendtoseeawebsite.WeuseaprotocolcalledHTTP(HypertextTransferProtocol).

So,basically,whenyouhaveawebsite,youneedtohaveaserver(machine)whereitlives.Whentheserverreceivesanincomingrequest(inaletter),itsendsbackyourwebsite(inanotherletter).

SincethisisaDjangotutorial,youmightaskwhatDjangodoes.Whenyousendaresponse,youdon'talwayswanttosendthesamethingtoeverybody.Itissomuchbetterifyourlettersarepersonalized,especiallyforthepersonthathasjustwrittentoyou,right?Djangohelpsyouwithcreatingthesepersonalized,interestingletters.:)

Enoughtalk–timetocreate!

HowtheInternetworks

19

Introductiontothecommand-lineinterfaceForreadersathome:thischapteriscoveredintheYournewfriend:CommandLinevideo.

It'sexciting,right?!You'llwriteyourfirstlineofcodeinjustafewminutes!:)

Letusintroduceyoutoyourfirstnewfriend:thecommandline!

Thefollowingstepswillshowyouhowtousetheblackwindowallhackersuse.Itmightlookabitscaryatfirstbutreallyit'sjustapromptwaitingforcommandsfromyou.

Pleasenotethatthroughoutthisbookweusetheterms'directory'and'folder'interchangeablybuttheyareoneandthesamething.

Whatisthecommandline?Thewindow,whichisusuallycalledthecommandlineorcommand-lineinterface,isatext-basedapplicationforviewing,handling,andmanipulatingfilesonyourcomputer.It'smuchlikeWindowsExplorerorFinderontheMac,butwithoutthegraphicalinterface.Othernamesforthecommandlineare:cmd,CLI,prompt,consoleorterminal.

Openthecommand-lineinterfaceTostartsomeexperimentsweneedtoopenourcommand-lineinterfacefirst.

Windows

GotoStartmenu→AllPrograms→Accessories→CommandPrompt.

MacOSX

Applications→Utilities→Terminal.

Linux

It'sprobablyunderApplications→Accessories→Terminal,butthatmaydependonyoursystem.Ifit'snotthere,justGoogleit.:)

PromptYounowshouldseeawhiteorblackwindowthatiswaitingforyourcommands.

Ifyou'reonMacorLinux,youprobablysee$,justlikethis:

command-line

$

OnWindows,it'sa>sign,likethis:

command-line

>

Introductiontocommandline

20

Eachcommandwillbeprependedbythissignandonespace,butyoudon'thavetotypeit.Yourcomputerwilldoitforyou.:)

Justasmallnote:inyourcasetheremaybesomethinglikeC:\Users\ola>orOlas-MacBook-Air:~ola$beforethepromptsign,andthisis100%OK.

Thepartuptoandincludingthe$orthe>iscalledthecommandlineprompt,orpromptforshort.Itpromptsyoutoinputsomethingthere.

Inthetutorial,whenwewantyoutotypeinacommand,wewillincludethe$or>,andoccasionallymoretotheleft.Youcanignoretheleftpartandjusttypeinthecommandwhichstartsaftertheprompt.

Yourfirstcommand(YAY!)Let'sstartwithsomethingsimple.Typethiscommand:

command-line

$whoami

or

command-line

>whoami

Andthenhitenter.Thisisourresult:

command-line

$whoami

olasitarska

Asyoucansee,thecomputerhasjustprintedyourusername.Neat,huh?:)

Trytotypeeachcommand;donotcopy-paste.You'llremembermorethisway!

BasicsEachoperatingsystemhasaslightlydifferentsetofcommandsforthecommandline,somakesuretofollowinstructionsforyouroperatingsystem.Let'strythis,shallwe?

CurrentdirectoryIt'dbenicetoknowwherearewenow,right?Let'ssee.Typethiscommandandhitenter:

command-line

$pwd

/Users/olasitarska

Ifyou'reonWindows:

command-line

Introductiontocommandline

21

>cd

C:\Users\olasitarska

You'llprobablyseesomethingsimilaronyourmachine.Onceyouopenthecommandlineyouusuallystartatyouruser'shomedirectory.

Note:'pwd'standsfor'printworkingdirectory'.

Listfilesanddirectories

Sowhat'sinit?It'dbecooltofindout.Let'ssee:

command-line

$ls

Applications

Desktop

Downloads

Music

...

Windows:

command-line

>dir

DirectoryofC:\Users\olasitarska

05/08/201407:28PM<DIR>Applications

05/08/201407:28PM<DIR>Desktop

05/08/201407:28PM<DIR>Downloads

05/08/201407:28PM<DIR>Music

...

ChangecurrentdirectoryNow,let'sgotoourDesktopdirectory:

command-line

$cdDesktop

Windows:

command-line

>cdDesktop

Checkifit'sreallychanged:

command-line

$pwd

/Users/olasitarska/Desktop

Windows:

command-line

Introductiontocommandline

22

>cd

C:\Users\olasitarska\Desktop

Hereitis!

PROtip:ifyoutypecdDandthenhittabonyourkeyboard,thecommandlinewillautomaticallyfillintherestofthenamesoyoucannavigatefaster.Ifthereismorethanonefolderstartingwith"D",hitthetabbuttontwicetogetalistofoptions.

Createdirectory

Howaboutcreatingapracticedirectoryonyourdesktop?Youcandoitthisway:

command-line

$mkdirpractice

Windows:

command-line

>mkdirpractice

Thislittlecommandwillcreateafolderwiththenamepracticeonyourdesktop.Youcancheckifit'stherejustbylookingonyourDesktoporbyrunningalsordircommand!Tryit.:)

PROtip:Ifyoudon'twanttotypethesamecommandsoverandover,trypressingtheuparrowanddownarrowonyourkeyboardtocyclethroughrecentlyusedcommands.

Exercise!Asmallchallengeforyou:inyournewlycreatedpracticedirectory,createadirectorycalledtest.(Usethecdandmkdircommands.)

Solution:command-line

$cdpractice

$mkdirtest

$ls

test

Windows:

command-line

>cdpractice

>mkdirtest

>dir

05/08/201407:28PM<DIR>test

Congrats!:)

Introductiontocommandline

23

Cleanup

Wedon'twanttoleaveamess,solet'sremoveeverythingwediduntilthatpoint.

First,weneedtogetbacktoDesktop:

command-line

$cd..

Windows:

command-line

>cd..

Using..withthecdcommandwillchangeyourcurrentdirectorytotheparentdirectory(thatis,thedirectorythatcontainsyourcurrentdirectory).

Checkwhereyouare:

command-line

$pwd

/Users/olasitarska/Desktop

Windows:

command-line

>cd

C:\Users\olasitarska\Desktop

Nowtimetodeletethepracticedirectory:

Attention:Deletingfilesusingdel,rmdirorrmisirrecoverable,meaningthedeletedfileswillbegoneforever!Sobeverycarefulwiththiscommand.

command-line

$rm-rpractice

Windows:

command-line

>rmdir/Spractice

practice,Areyousure<Y/N>?Y

Done!Tobesureit'sactuallydeleted,let'scheckit:

command-line

$ls

Windows:

command-line

Introductiontocommandline

24

>dir

ExitThat'sitfornow!Youcansafelyclosethecommandlinenow.Let'sdoitthehackerway,alright?:)

command-line

$exit

Windows:

command-line

>exit

Cool,huh?:)

SummaryHereisasummaryofsomeusefulcommands:

Command(Windows)

Command(MacOS/Linux) Description Example

exit exit closethewindow exit

cd cd changedirectory cdtest

dir ls listdirectories/files dir

copy cp copyfile copyc:\test\test.txtc:\windows\test.txt

move mv movefile movec:\test\test.txtc:\windows\test.txt

mkdir mkdir createanewdirectory mkdirtestdirectory

rmdir(ordel) rm deleteafile delc:\test\test.txt

rmdir\S rm-r deleteadirectory rm-rtestdirectory

Thesearejustaveryfewofthecommandsyoucanruninyourcommandline,butyou'renotgoingtouseanythingmorethanthattoday.

Ifyou'recurious,ss64.comcontainsacompletereferenceofcommandsforalloperatingsystems.

Ready?Let'sdiveintoPython!

Introductiontocommandline

25

Let’sstartwithPythonWe'refinallyhere!

Butfirst,letustellyouwhatPythonis.Pythonisaverypopularprogramminglanguagethatcanbeusedforcreatingwebsites,games,scientificsoftware,graphics,andmuch,muchmore.

Pythonoriginatedinthelate1980sanditsmaingoalistobereadablebyhumanbeings(notonlymachines!).Thisiswhyitlooksmuchsimplerthanotherprogramminglanguages.Thismakesiteasytolearn,butdon'tworry–Pythonisalsoreallypowerful!

PythoninstallationIfyou'reusingaChromebook,skipthischapterandmakesureyoufollowtheChromebookSetupinstructions.

NoteIfyoualreadyworkedthroughtheInstallationsteps,there'snoneedtodothisagain–youcanskipstraightaheadtothenextchapter!

Forreadersathome:thischapteriscoveredintheInstallingPython&CodeEditorvideo.

ThissectionisbasedonatutorialbyGeekGirlsCarrots(https://github.com/ggcarrots/django-carrots)

DjangoiswritteninPython.WeneedPythontodoanythinginDjango.Let'sstartbyinstallingit!WewantyoutoinstallPython3.5,soifyouhaveanyearlierversion,youwillneedtoupgradeit.

WindowsYoucandownloadPythonforWindowsfromthewebsitehttps://www.python.org/downloads/release/python-351/.Afterdownloadingthe*.msifile,youshouldrunit(double-clickonit)andfollowtheinstructionsthere.Itisimportanttorememberthepath(thedirectory)whereyouinstalledPython.Itwillbeneededlater!

Onethingtowatchoutfor:onthesecondscreenoftheinstallationwizard,marked"Customize",makesureyouscrolldowntothe"Addpython.exetothePath"optionandselect"Willbeinstalledonlocalharddrive",asshownhere:

Pythoninstallation

26

Inupcomingsteps,you'llbeusingtheWindowsCommandLine(whichwe'llalsotellyouabout).Fornow,ifyouneedtotypeinsomecommands,gotoStartmenu→AllPrograms→Accessories→CommandPrompt.(OnnewerversionsofWindows,youmighthavetosearchfor"CommandPrompt"sinceit'ssometimeshidden.)

Linux

ItisverylikelythatyoualreadyhavePythoninstalledoutofthebox.Tocheckifyouhaveitinstalled(andwhichversionitis),openaconsoleandtypethefollowingcommand:

command-line

$python3--version

Python3.5.1

Ifyouhaveadifferent'microversion'ofPythoninstalled,e.g.3.5.0,thenyoudon'thavetoupgrade.Ifyoudon'thavePythoninstalled,orifyouwantadifferentversion,youcaninstallitasfollows:

DebianorUbuntu

Typethiscommandintoyourconsole:

command-line

$sudoapt-getinstallpython3.5

Fedora(upto21)

Usethiscommandinyourconsole:

command-line

Pythoninstallation

27

$sudoyuminstallpython3

Fedora(22+)Usethiscommandinyourconsole:

command-line

$sudodnfinstallpython3

openSUSE

Usethiscommandinyourconsole:

command-line

$sudozypperinstallpython3

OSXBeforeyouinstallPythononOSX,youshouldensureyourMacsettingsallowinstallingpackagesthataren'tfromtheAppStore.GotoSystemPreferences(it'sintheApplicationsfolder),click"Security&Privacy,"andthenthe"General"tab.Ifyour"Allowappsdownloadedfrom:"issetto"MacAppStore,"changeitto"MacAppStoreandidentifieddevelopers."

Youneedtogotothewebsitehttps://www.python.org/downloads/release/python-351/anddownloadthePythoninstaller:

DownloadtheMacOSX64-bit/32-bitinstallerfile,Doubleclickpython-3.5.1-macosx10.6.pkgtoruntheinstaller.

VerifytheinstallationwassuccessfulbyopeningtheTerminalapplicationandrunningthepython3command:

command-line

$python3--version

Python3.5.1

Ifyouhaveanydoubts,orifsomethingwentwrongandyouhavenoideawhattodonext,pleaseaskyourcoach!Sometimesthingsdon'tgosmoothlyandit'sbettertoaskforhelpfromsomeonewithmoreexperience.

Pythoninstallation

28

CodeeditorForreadersathome:thischapteriscoveredintheInstallingPython&CodeEditorvideo.

You'reabouttowriteyourfirstlineofcode,soit'stimetodownloadacodeeditor!

Ifyou'reusingaChromebook,skipthischapterandmakesureyoufollowtheChromebookSetupinstructions.

NoteYoumighthavedonethisearlierintheInstallationchapter–ifso,youcanskiprightaheadtothenextchapter!

Therearealotofdifferenteditorsanditlargelyboilsdowntopersonalpreference.MostPythonprogrammersusecomplexbutextremelypowerfulIDEs(IntegratedDevelopmentEnvironments),suchasPyCharm.Asabeginner,however,that'sprobablylesssuitable;ourrecommendationsareequallypowerful,butalotsimpler.

Oursuggestionsarebelow,butfeelfreetoaskyourcoachwhattheirpreferencesare–it'llbeeasiertogethelpfromthem.

GeditGeditisanopen-source,freeeditor,availableforalloperatingsystems.

Downloadithere

SublimeText2SublimeTextisaverypopulareditorwithafreeevaluationperiod.It'seasytoinstallanduse,andit'savailableforalloperatingsystems.

Downloadithere

AtomAtomisanextremelynewcodeeditorcreatedbyGitHub.It'sfree,open-source,easytoinstallandeasytouse.It'savailableforWindows,OSXandLinux.

Downloadithere

Whyareweinstallingacodeeditor?Youmightbewonderingwhyweareinstallingthisspecialcodeeditorsoftware,ratherthanusingsomethinglikeWordorNotepad.

Thefirstreasonisthatcodeneedstobeplaintext,andtheproblemwithprogramslikeWordandTexteditisthattheydon'tactuallyproduceplaintext,theyproducerichtext(withfontsandformatting),usingcustomformatslikeRTF(RichTextFormat).

Thesecondreasonisthatcodeeditorsarespecialisedforeditingcode,sotheycanprovidehelpfulfeatureslikehighlightingcodewithcolouraccordingtoitsmeaning,orautomaticallyclosingquotesforyou.

We'llseeallthisinactionlater.Soon,you'llcometothinkofyourtrustyoldcodeeditorasoneofyourfavouritetools.:)

Codeeditor

29

Codeeditor

30

IntroductiontoPythonPartofthischapterisbasedontutorialsbyGeekGirlsCarrots(https://github.com/ggcarrots/django-carrots).

Let'swritesomecode!

PythonpromptForreadersathome:thispartiscoveredinthePythonBasics:Integers,Strings,Lists,VariablesandErrorsvideo.

TostartplayingwithPython,weneedtoopenupacommandlineonyourcomputer.Youshouldalreadyknowhowtodothat–youlearneditintheIntrotoCommandLinechapter.

Onceyou'reready,followtheinstructionsbelow.

WewanttoopenupaPythonconsole,sotypeinpythononWindowsorpython3onMacOS/Linuxandhitenter.

command-line

$python3

Python3.5.1(...)

Type"help","copyright","credits"or"license"formoreinformation.

>>>

YourfirstPythoncommand!AfterrunningthePythoncommand,thepromptchangedto>>>.ForusthismeansthatfornowwemayonlyusecommandsinthePythonlanguage.Youdon'thavetotypein>>>–Pythonwilldothatforyou.

IfyouwanttoexitthePythonconsoleatanypoint,justtypeexit()orusetheshortcutCtrl+ZforWindowsandCtrl+DforMac/Linux.Thenyouwon'tsee>>>anylonger.

Fornow,wedon'twanttoexitthePythonconsole.Wewanttolearnmoreaboutit.Let'sstartwithsomethingreallysimple.Forexample,trytypingsomemath,like2+3andhitenter.

command-line

>>>2+3

5

Nice!Seehowtheanswerpoppedout?Pythonknowsmath!Youcouldtryothercommandslike:

4*5

5-1

40/2

Havefunwiththisforalittlewhileandthengetbackhere.:)

Asyoucansee,Pythonisagreatcalculator.Ifyou'rewonderingwhatelseyoucando…

StringsHowaboutyourname?Typeyourfirstnameinquoteslikethis:

command-line

IntroductiontoPython

31

>>>"Ola"

'Ola'

You'venowcreatedyourfirststring!It'sasequenceofcharactersthatcanbeprocessedbyacomputer.Thestringmustalwaysbeginandendwiththesamecharacter.Thismaybesingle(')ordouble(")quotes(thereisnodifference!)ThequotestellPythonthatwhat'sinsideofthemisastring.

Stringscanbestrungtogether.Trythis:

command-line

>>>"Hithere"+"Ola"

'HithereOla'

Youcanalsomultiplystringswithanumber:

command-line

>>>"Ola"*3

'OlaOlaOla'

Ifyouneedtoputanapostropheinsideyourstring,youhavetwowaystodoit.

Usingdoublequotes:

command-line

>>>"Runnin'downthehill"

"Runnin'downthehill"

orescapingtheapostrophewithabackslash(\):

command-line

>>>'Runnin\'downthehill'

"Runnin'downthehill"

Nice,huh?Toseeyournameinuppercaseletters,simplytype:

command-line

>>>"Ola".upper()

'OLA'

Youjustusedtheuppermethodonyourstring!Amethod(likeupper())isasequenceofinstructionsthatPythonhastoperformonagivenobject("Ola")onceyoucallit.

Ifyouwanttoknowthenumberofletterscontainedinyourname,thereisafunctionforthattoo!

command-line

>>>len("Ola")

3

Wonderwhysometimesyoucallfunctionswitha.attheendofastring(like"Ola".upper())andsometimesyoufirstcallafunctionandplacethestringinparentheses?Well,insomecases,functionsbelongtoobjects,likeupper(),whichcanonlybeperformedonstrings.Inthiscase,wecallthefunctionamethod.Othertimes,functionsdon'tbelongtoanything

IntroductiontoPython

32

specificandcanbeusedondifferenttypesofobjects,justlikelen().That'swhywe'regiving"Ola"asaparametertothelenfunction.

Summary

OK,enoughofstrings.Sofaryou'velearnedabout:

theprompt–typingcommands(code)intothePythonpromptresultsinanswersinPythonnumbersandstrings–inPythonnumbersareusedformathandstringsfortextobjectsoperators–like+and\*,combinevaluestoproduceanewonefunctions–likeupper()andlen(),performactionsonobjects.

Thesearethebasicsofeveryprogramminglanguageyoulearn.Readyforsomethingharder?Webetyouare!

ErrorsLet'strysomethingnew.Canwegetthelengthofanumberthesamewaywecouldfindoutthelengthofourname?Typeinlen(304023)andhitenter:

command-line

>>>len(304023)

Traceback(mostrecentcalllast):

File"<stdin>",line1,in<module>

TypeError:objectoftype'int'hasnolen()

Wegotourfirsterror!Itsaysthatobjectsoftype"int"(integers,wholenumbers)havenolength.Sowhatcanwedonow?Maybewecanwriteournumberasastring?Stringshavealength,right?

command-line

>>>len(str(304023))

6

Itworked!Weusedthestrfunctioninsideofthelenfunction.str()convertseverythingtostrings.

ThestrfunctionconvertsthingsintostringsTheintfunctionconvertsthingsintointegers

Important:wecanconvertnumbersintotext,butwecan'tnecessarilyconverttextintonumbers–whatwouldint('hello')beanyway?

VariablesAnimportantconceptinprogrammingisvariables.Avariableisnothingmorethananameforsomethingsoyoucanuseitlater.Programmersusethesevariablestostoredata,maketheircodemorereadableandsotheydon'thavetokeeprememberingwhatthingsare.

Let'ssaywewanttocreateanewvariablecalledname:

command-line

>>>name="Ola"

Yousee?It'seasy!It'ssimply:nameequalsOla.

IntroductiontoPython

33

Asyou'venoticed,yourprogramdidn'treturnanythinglikeitdidbefore.Sohowdoweknowthatthevariableactuallyexists?Simplyenternameandhitenter:

command-line

>>>name

'Ola'

Yippee!Yourfirstvariable!:)Youcanalwayschangewhatitrefersto:

command-line

>>>name="Sonja"

>>>name

'Sonja'

Youcanuseitinfunctionstoo:

command-line

>>>len(name)

5

Awesome,right?Ofcourse,variablescanbeanything–numberstoo!Trythis:

command-line

>>>a=4

>>>b=6

>>>a*b

24

Butwhatifweusedthewrongname?Canyouguesswhatwouldhappen?Let'stry!

command-line

>>>city="Tokyo"

>>>ctiy

Traceback(mostrecentcalllast):

File"<stdin>",line1,in<module>

NameError:name'ctiy'isnotdefined

Anerror!Asyoucansee,PythonhasdifferenttypesoferrorsandthisoneiscalledaNameError.Pythonwillgiveyouthiserrorifyoutrytouseavariablethathasn'tbeendefinedyet.Ifyouencounterthiserrorlater,checkyourcodetoseeifyou'vemistypedanynames.

Playwiththisforawhileandseewhatyoucando!

TheprintfunctionTrythis:

command-line

>>>name='Maria'

>>>name

'Maria'

>>>print(name)

Maria

IntroductiontoPython

34

Whenyoujusttypename,thePythoninterpreterrespondswiththestringrepresentationofthevariable'name',whichisthelettersM-a-r-i-a,surroundedbysinglequotes,''.Whenyousayprint(name),Pythonwill"print"thecontentsofthevariabletothescreen,withoutthequotes,whichisneater.

Aswe'llseelater,print()isalsousefulwhenwewanttoprintthingsfrominsidefunctions,orwhenwewanttoprintthingsonmultiplelines.

ListsBesidestringsandintegers,Pythonhasallsortsofdifferenttypesofobjects.Nowwe'regoingtointroduceonecalledlist.Listsareexactlywhatyouthinktheyare:objectswhicharelistsofotherobjects.:)

Goaheadandcreatealist:

command-line

>>>[]

[]

Yes,thislistisempty.Notveryuseful,right?Let'screatealistoflotterynumbers.Wedon'twanttorepeatourselvesallthetime,sowewillputitinavariable,too:

command-line

>>>lottery=[3,42,12,19,30,59]

Allright,wehavealist!Whatcanwedowithit?Let'sseehowmanylotterynumbersthereareinalist.Doyouhaveanyideawhichfunctionyoushoulduseforthat?Youknowthisalready!

command-line

>>>len(lottery)

6

Yes!len()cangiveyouanumberofobjectsinalist.Handy,right?Maybewewillsortitnow:

command-line

>>>lottery.sort()

Thisdoesn'treturnanything,itjustchangedtheorderinwhichthenumbersappearinthelist.Let'sprintitoutagainandseewhathappened:

command-line

>>>print(lottery)

[3,12,19,30,42,59]

Asyoucansee,thenumbersinyourlistarenowsortedfromthelowesttohighestvalue.Congrats!

Maybewewanttoreversethatorder?Let'sdothat!

command-line

>>>lottery.reverse()

>>>print(lottery)

[59,42,30,19,12,3]

IntroductiontoPython

35

Easy,right?Ifyouwanttoaddsomethingtoyourlist,youcandothisbytypingthiscommand:

command-line

>>>lottery.append(199)

>>>print(lottery)

[59,42,30,19,12,3,199]

Ifyouwanttoshowonlythefirstnumber,youcandothisbyusingindexes.Anindexisthenumberthatsayswhereinalistanitemoccurs.Programmersprefertostartcountingat0,sothefirstobjectinyourlistisatindex0,thenextoneisat1,andsoon.Trythis:

command-line

>>>print(lottery[0])

59

>>>print(lottery[1])

42

Asyoucansee,youcanaccessdifferentobjectsinyourlistbyusingthelist'snameandtheobject'sindexinsideofsquarebrackets.

Todeletesomethingfromyourlistyouwillneedtouseindexesaswelearntaboveandthepop()method.Let'stryanexampleandreinforcewhatwelearntpreviously;wewillbedeletingthefirstnumberofourlist.

command-line

>>>print(lottery)

[59,42,30,19,12,3,199]

>>>print(lottery[0])

59

>>>lottery.pop(0)

>>>print(lottery)

[42,30,19,12,3,199]

Thatworkedlikeacharm!

Forextrafun,trysomeotherindexes:6,7,1000,-1,-6or-1000.Seeifyoucanpredicttheresultbeforetryingthecommand.Dotheresultsmakesense?

YoucanfindalistofallavailablelistmethodsinthischapterofthePythondocumentation:https://docs.python.org/3/tutorial/datastructures.html

DictionariesForreadersathome:thispartiscoveredinthePythonBasics:Dictionariesvideo.

Adictionaryissimilartoalist,butyouaccessvaluesbylookingupakeyinsteadofanumericindex.Akeycanbeanystringornumber.Thesyntaxtodefineanemptydictionaryis:

command-line

>>>{}

{}

Thisshowsthatyoujustcreatedanemptydictionary.Hurray!

Now,trywritingthefollowingcommand(trysubstitutingyourowninformation,too):

command-line

IntroductiontoPython

36

>>>participant={'name':'Ola','country':'Poland','favorite_numbers':[7,42,92]}

Withthiscommand,youjustcreatedavariablenamedparticipantwiththreekey–valuepairs:

Thekeynamepointstothevalue'Ola'(astringobject),countrypointsto'Poland'(anotherstring),andfavorite_numberspointsto[7,42,92](alistwiththreenumbersinit).

Youcancheckthecontentofindividualkeyswiththissyntax:

command-line

>>>print(participant['name'])

Ola

See,it'ssimilartoalist.Butyoudon'tneedtoremembertheindex–justthename.

WhathappensifweaskPythonthevalueofakeythatdoesn'texist?Canyouguess?Let'stryitandsee!

command-line

>>>participant['age']

Traceback(mostrecentcalllast):

File"<stdin>",line1,in<module>

KeyError:'age'

Look,anothererror!ThisoneisaKeyError.Pythonishelpfulandtellsyouthatthekey'age'doesn'texistinthisdictionary.

Whenshouldyouuseadictionaryoralist?Well,that'sagoodpointtoponder.Justhaveasolutioninmindbeforelookingattheanswerinthenextline.

Doyoujustneedanorderedsequenceofitems?Goforalist.Doyouneedtoassociatevalueswithkeys,soyoucanlookthemupefficiently(bykey)lateron?Useadictionary.

Dictionaries,likelists,aremutable,meaningthattheycanbechangedaftertheyarecreated.Youcanaddnewkey–valuepairstoadictionaryafteritiscreated,likethis:

command-line

>>>participant['favorite_language']='Python'

Likelists,usingthelen()methodonthedictionariesreturnsthenumberofkey–valuepairsinthedictionary.Goaheadandtypeinthiscommand:

command-line

>>>len(participant)

4

Ihopeitmakessenseuptonow.:)Readyforsomemorefunwithdictionaries?Readonforsomeamazingthings.

Youcanusethepop()methodtodeleteaniteminthedictionary.Say,ifyouwanttodeletetheentrycorrespondingtothekey'favorite_numbers',justtypeinthefollowingcommand:

command-line

>>>participant.pop('favorite_numbers')

>>>participant

{'country':'Poland','favorite_language':'Python','name':'Ola'}

IntroductiontoPython

37

Asyoucanseefromtheoutput,thekey–valuepaircorrespondingtothe'favorite_numbers'keyhasbeendeleted.

Aswellasthis,youcanalsochangeavalueassociatedwithanalready-createdkeyinthedictionary.Typethis:

command-line

>>>participant['country']='Germany'

>>>participant

{'country':'Germany','favorite_language':'Python','name':'Ola'}

Asyoucansee,thevalueofthekey'country'hasbeenalteredfrom'Poland'to'Germany'.:)Exciting?Hurrah!Youjustlearntanotheramazingthing.

Summary

Awesome!Youknowalotaboutprogrammingnow.Inthislastpartyoulearnedabout:

errors–younowknowhowtoreadandunderstanderrorsthatshowupifPythondoesn'tunderstandacommandyou'vegivenitvariables–namesforobjectsthatallowyoutocodemoreeasilyandtomakeyourcodemorereadablelists–listsofobjectsstoredinaparticularorderdictionaries–objectsstoredaskey–valuepairs

Excitedforthenextpart?:)

ComparethingsForreadersathome:thispartiscoveredinthePythonBasics:Comparisonsvideo.

Abigpartofprogramminginvolvescomparingthings.What'stheeasiestthingtocompare?Numbers,ofcourse.Let'sseehowthatworks:

command-line

>>>5>2

True

>>>3<1

False

>>>5>2*2

True

>>>1==1

True

>>>5!=2

True

WegavePythonsomenumberstocompare.Asyoucansee,notonlycanPythoncomparenumbers,butitcanalsocomparemethodresults.Nice,huh?

Doyouwonderwhyweputtwoequalsigns==nexttoeachothertocompareifnumbersareequal?Weuseasingle=forassigningvaluestovariables.Youalways,alwaysneedtoputtwoofthem–==–ifyouwanttocheckifthingsareequaltoeachother.Wecanalsostatethatthingsareunequaltoeachother.Forthat,weusethesymbol!=,asshownintheexampleabove.

GivePythontwomoretasks:

command-line

IntroductiontoPython

38

>>>6>=12/2

True

>>>3<=2

False

>and<areeasy,butwhatdo>=and<=mean?Readthemlikethis:

x>ymeans:xisgreaterthanyx<ymeans:xislessthanyx<=ymeans:xislessthanorequaltoyx>=ymeans:xisgreaterthanorequaltoy

Awesome!Wannadoonemore?Trythis:

command-line

>>>6>2and2<3

True

>>>3>2and2<1

False

>>>3>2or2<1

True

YoucangivePythonasmanynumberstocompareasyouwant,anditwillgiveyouananswer!Prettysmart,right?

and–ifyouusetheandoperator,bothcomparisonshavetobeTrueinorderforthewholecommandtobeTrueor–ifyouusetheoroperator,onlyoneofthecomparisonshastobeTrueinorderforthewholecommandtobeTrue

Haveyouheardoftheexpression"comparingapplestooranges"?Let'strythePythonequivalent:

command-line

>>>1>'django'

Traceback(mostrecentcalllast):

File"<stdin>",line1,in<module>

TypeError:unorderabletypes:int()>str()

Hereyouseethatjustlikeintheexpression,Pythonisnotabletocompareanumber(int)andastring(str).Instead,itshowsaTypeErrorandtellsusthetwotypescan'tbecomparedtogether.

BooleanIncidentally,youjustlearnedaboutanewtypeofobjectinPython.It'scalledBoolean,anditisprobablytheeasiesttypethereis.

ThereareonlytwoBooleanobjects:

TrueFalse

ButforPythontounderstandthis,youneedtoalwayswriteitas'True'(firstletteruppercased,withtherestoftheletterlowercased).true,TRUE,andtRUEwon'twork–onlyTrueiscorrect.(Thesameappliesto'False'aswell,ofcourse.)

Booleanscanbevariables,too!Seehere:

command-line

IntroductiontoPython

39

>>>a=True

>>>a

True

Youcanalsodoitthisway:

command-line

>>>a=2>5

>>>a

False

PracticeandhavefunwithBooleansbytryingtorunthefollowingcommands:

TrueandTrue

FalseandTrue

Trueor1==1

1!=2

Congrats!Booleansareoneofthecoolestfeaturesinprogramming,andyoujustlearnedhowtousethem!

Saveit!Forreadersathome:thispartiscoveredinthePythonBasics:Savingfilesand"If"statementvideo.

Sofarwe'vebeenwritingallourpythoncodeintheinterpreter,whichlimitsustoenteringonelineofcodeatatime.Normalprogramsaresavedinfilesandexecutedbyourprogramminglanguageinterpreterorcompiler.Sofarwe'vebeenrunningourprogramsonelineatatimeinthePythoninterpreter.We'regoingtoneedmorethanonelineofcodeforthenextfewtasks,sowe'llquicklyneedto:

ExitthePythoninterpreterOpenupourcodeeditorofchoiceSavesomecodeintoanewpythonfileRunit!

ToexitfromthePythoninterpreterthatwe'vebeenusing,simplytypetheexit()function

command-line

>>>exit()

$

Thiswillputyoubackintothecommandprompt.

Earlier,wepickedoutacodeeditorfromthecodeeditorsection.We'llneedtoopentheeditornowandwritesomecodeintoanewfile:

editor

print('Hello,Djangogirls!')

Obviously,you'reaprettyseasonedPythondevelopernow,sofeelfreetowritesomecodethatyou'velearnedtoday.

Nowweneedtosavethefileandgiveitadescriptivename.Let'scallthefilepython_intro.pyandsaveittoyourdesktop.Wecannamethefileanythingwewant,buttheimportantparthereistomakesurethefileendsin.py.The.pyextensiontellsouroperatingsystemthatthisisaPythonexecutablefileandPythoncanrunit.

IntroductiontoPython

40

Youshouldnoticeoneofthecoolestthingaboutcodeeditors:colours!InthePythonconsole,everythingwasthesamecolour;nowyoushouldseethattheprintfunctionisadifferentcolourfromthestring.Thisiscalled"syntaxhighlighting",andit'sareallyusefulfeaturewhencoding.Thecolourofthingswillgiveyouhints,suchasunclosedstringsoratypoinakeywordname(likethedefinafunction,whichwe'llseebelow).Thisisoneofthereasonsweuseacodeeditor.:)

Withthefilesaved,it'stimetorunit!Usingtheskillsyou'velearnedinthecommandlinesection,usetheterminaltochangedirectoriestothedesktop.(Note:Replace<your_name>includingthe<and>withyourusername.)

OnaMac,thecommandwilllooksomethinglikethis:

command-line

$cd/Users/<your_name>/Desktop

OnLinux,itwillbelikethis(theword"Desktop"mightbetranslatedtoyourlocallanguage):

command-line

$cd/home/<your_name>/Desktop

AndonWindows,itwillbelikethis:

command-line

>cdC:\Users\<your_name>\Desktop

Ifyougetstuck,justaskforhelp.

NowusePythontoexecutethecodeinthefilelikethis:

command-line

$python3python_intro.py

Hello,Djangogirls!

Note:onWindows'python3'isnotrecognizedasacommand.Instead,use'python'toexecutethefile:

command-line

>pythonpython_intro.py

Alright!YoujustranyourfirstPythonprogramthatwassavedtoafile.Feelawesome?

Youcannowmoveontoanessentialtoolinprogramming:

If…elif…elseLotsofthingsincodeshouldbeexecutedonlywhengivenconditionsaremet.That'swhyPythonhassomethingcalledifstatements.

Replacethecodeinyourpython_intro.pyfilewiththis:

python_intro.py

if3>2:

IntroductiontoPython

41

Ifweweretosaveandrunthis,we'dseeanerrorlikethis:

command-line

$python3python_intro.py

File"python_intro.py",line2

^

SyntaxError:unexpectedEOFwhileparsing

Pythonexpectsustogivefurtherinstructionstoitwhichareexecutedifthecondition3>2turnsouttobetrue(orTrueforthatmatter).Let’strytomakePythonprint“Itworks!”.Changeyourcodeinyourpython_intro.pyfiletothis:

python_intro.py

if3>2:

print('Itworks!')

Noticehowwe'veindentedthenextlineofcodeby4spaces?WeneedtodothissoPythonknowswhatcodetoruniftheresultistrue.Youcandoonespace,butnearlyallPythonprogrammersdo4tomakethingslookneat.Asingletabwillalsocountas4spaces.

Saveitandgiveitanotherrun:

command-line

$python3python_intro.py

Itworks!

Note:RememberthatonWindows,'python3'isnotrecognizedasacommand.Fromnowon,replace'python3'with'python'toexecutethefile.

Whatifaconditionisn'tTrue?Inpreviousexamples,codewasexecutedonlywhentheconditionswereTrue.ButPythonalsohaselifandelsestatements:

python_intro.py

if5>2:

print('5isindeedgreaterthan2')

else:

print('5isnotgreaterthan2')

Whenthisisrunitwillprintout:

command-line

$python3python_intro.py

5isindeedgreaterthan2

If2wereagreaternumberthan5,thenthesecondcommandwouldbeexecuted.Easy,right?Let'sseehowelifworks:

python_intro.py

IntroductiontoPython

42

name='Sonja'

ifname=='Ola':

print('HeyOla!')

elifname=='Sonja':

print('HeySonja!')

else:

print('Heyanonymous!')

andexecuted:

command-line

$python3python_intro.py

HeySonja!

Seewhathappenedthere?elifletsyouaddextraconditionsthatrunifthepreviousconditionsfail.

Youcanaddasmanyelifstatementsasyoulikeafteryourinitialifstatement.Forexample:

python_intro.py

volume=57

ifvolume<20:

print("It'skindaquiet.")

elif20<=volume<40:

print("It'sniceforbackgroundmusic")

elif40<=volume<60:

print("Perfect,Icanhearallthedetails")

elif60<=volume<80:

print("Niceforparties")

elif80<=volume<100:

print("Abitloud!")

else:

print("Myearsarehurting!:(")

Pythonrunsthrougheachtestinsequenceandprints:

command-line

$python3python_intro.py

Perfect,Icanhearallthedetails

CommentsCommentsarelinesbeginningwith#.Youcanwritewhateveryouwantafterthe#andPythonwillignoreit.Commentscanmakeyourcodeeasierforotherpeopletounderstand.

Let'sseehowthatlooks:

python_intro.py

#Changethevolumeifit'stooloudortooquiet

ifvolume<20orvolume>80:

volume=50

print("That'sbetter!")

Youdon'tneedtowriteacommentforeverylineofcode,buttheyareusefulforexplainingwhyyourcodeisdoingsomething,orprovidingasummarywhenit'sdoingsomethingcomplex.

Summary

IntroductiontoPython

43

Inthelastfewexercisesyoulearnedabout:

comparingthings–inPythonyoucancomparethingsbyusing>,>=,==,<=,<andtheand,oroperatorsBoolean–atypeofobjectthatcanonlyhaveoneoftwovalues:TrueorFalseSavingfiles–storingcodeinfilessoyoucanexecutelargerprograms.if…elif…else–statementsthatallowyoutoexecutecodeonlywhencertainconditionsaremet.comments-linesthatPythonwon'trunwhichletyoudocumentyourcode

Timeforthelastpartofthischapter!

Yourownfunctions!Forreadersathome:thispartiscoveredinthePythonBasics:Functionsvideo.

Rememberfunctionslikelen()thatyoucanexecuteinPython?Well,goodnews–youwilllearnhowtowriteyourownfunctionsnow!

AfunctionisasequenceofinstructionsthatPythonshouldexecute.EachfunctioninPythonstartswiththekeyworddef,isgivenaname,andcanhavesomeparameters.Let'sstartwithaneasyone.Replacethecodeinpython_intro.pywiththefollowing:

python_intro.py

defhi():

print('Hithere!')

print('Howareyou?')

hi()

Okay,ourfirstfunctionisready!

Youmaywonderwhywe'vewrittenthenameofthefunctionatthebottomofthefile.ThisisbecausePythonreadsthefileandexecutesitfromtoptobottom.Soinordertouseourfunction,wehavetore-writeitatthebottom.

Let'srunthisnowandseewhathappens:

command-line

$python3python_intro.py

Hithere!

Howareyou?

Thatwaseasy!Let'sbuildourfirstfunctionwithparameters.Wewillusethepreviousexample–afunctionthatsays'hi'tothepersonrunningit–withaname:

python_intro.py

defhi(name):

Asyoucansee,wenowgaveourfunctionaparameterthatwecalledname:

python_intro.py

IntroductiontoPython

44

defhi(name):

ifname=='Ola':

print('HiOla!')

elifname=='Sonja':

print('HiSonja!')

else:

print('Hianonymous!')

hi()

Remember:Theprintfunctionisindentedfourspaceswithintheifstatement.Thisisbecausethefunctionrunswhentheconditionismet.Let'sseehowitworksnow:

command-line

$python3python_intro.py

Traceback(mostrecentcalllast):

File"python_intro.py",line10,in<module>

hi()

TypeError:hi()missing1requiredpositionalargument:'name'

Oops,anerror.Luckily,Pythongivesusaprettyusefulerrormessage.Ittellsusthatthefunctionhi()(theonewedefined)hasonerequiredargument(calledname)andthatweforgottopassitwhencallingthefunction.Let'sfixitatthebottomofthefile:

python_intro.py

hi("Ola")

Andrunitagain:

command-line

$python3python_intro.py

HiOla!

Andifwechangethename?

python_intro.py

hi("Sonja")

Andrunit:

command-line

$python3python_intro.py

HiSonja!

Now,whatdoyouthinkwillhappenifyouwriteanothernameinthere?(NotOlaorSonja.)Giveitatryandseeifyou'reright.Itshouldprintoutthis:

command-line

Hianonymous!

Thisisawesome,right?Thiswayyoudon'thavetorepeatyourselfeverytimeyouwanttochangethenameofthepersonthefunctionissupposedtogreet.Andthat'sexactlywhyweneedfunctions–youneverwanttorepeatyourcode!

IntroductiontoPython

45

Let'sdosomethingsmarter–therearemorenamesthantwo,andwritingaconditionforeachwouldbehard,right?

python_intro.py

defhi(name):

print('Hi'+name+'!')

hi("Rachel")

Let'scallthecodenow:

command-line

$python3python_intro.py

HiRachel!

Congratulations!Youjustlearnedhowtowritefunctions!:)

LoopsForreadersathome:thispartiscoveredinthePythonBasics:ForLoopvideo.

Thisisthelastpartalready.Thatwasquick,right?:)

Programmersdon'tliketorepeatthemselves.Programmingisallaboutautomatingthings,sowedon'twanttogreeteverypersonbytheirnamemanually,right?That'swhereloopscomeinhandy.

Stillrememberlists?Let'sdoalistofgirls:

python_intro.py

girls=['Rachel','Monica','Phoebe','Ola','You']

Wewanttogreetallofthembytheirname.Wehavethehifunctiontodothat,solet'suseitinaloop:

python_intro.py

fornameingirls:

Theforstatementbehavessimilarlytotheifstatement;codebelowbothoftheseneedtobeindentedfourspaces.

Hereisthefullcodethatwillbeinthefile:

python_intro.py

defhi(name):

print('Hi'+name+'!')

girls=['Rachel','Monica','Phoebe','Ola','You']

fornameingirls:

hi(name)

print('Nextgirl')

Andwhenwerunit:

command-line

IntroductiontoPython

46

$python3python_intro.py

HiRachel!

Nextgirl

HiMonica!

Nextgirl

HiPhoebe!

Nextgirl

HiOla!

Nextgirl

HiYou!

Nextgirl

Asyoucansee,everythingyouputinsideaforstatementwithanindentwillberepeatedforeveryelementofthelistgirls.

Youcanalsouseforonnumbersusingtherangefunction:

python_intro.py

foriinrange(1,6):

print(i)

Whichwouldprint:

command-line

1

2

3

4

5

rangeisafunctionthatcreatesalistofnumbersfollowingoneaftertheother(thesenumbersareprovidedbyyouasparameters).

NotethatthesecondofthesetwonumbersisnotincludedinthelistthatisoutputbyPython(meaningrange(1,6)countsfrom1to5,butdoesnotincludethenumber6).Thatisbecause"range"ishalf-open,andbythatwemeanitincludesthefirstvalue,butnotthelast.

SummaryThat'sit.Youtotallyrock!Thiswasatrickychapter,soyoushouldfeelproudofyourself.We'redefinitelyproudofyouformakingitthisfar!

Youmightwanttobrieflydosomethingelse–stretch,walkaroundforabit,restyoureyes–beforegoingontothenextchapter.:)

IntroductiontoPython

47

IntroductiontoPython

48

WhatisDjango?Django(/ˈdʒæŋɡoʊ/jang-goh)isafreeandopensourcewebapplicationframework,writteninPython.Awebframeworkisasetofcomponentsthathelpsyoutodevelopwebsitesfasterandeasier.

Whenyou'rebuildingawebsite,youalwaysneedasimilarsetofcomponents:awaytohandleuserauthentication(signingup,signingin,signingout),amanagementpanelforyourwebsite,forms,awaytouploadfiles,etc.

Luckilyforyouotherpeoplelongagonoticedthatwebdevelopersfacesimilarproblemswhenbuildinganewsite,sotheyteamedupandcreatedframeworks(Djangoisoneofthem)thatgiveyouready-madecomponentsyoucanuse.

Frameworksexisttosaveyoufromhavingtoreinventthewheelandhelpalleviatesomeoftheoverheadwhenyou’rebuildinganewsite.

Whydoyouneedaframework?TounderstandwhatDjangoactuallyisfor,weneedtotakeacloserlookattheservers.Thefirstthingisthattheserverneedstoknowthatyouwantittoserveyouawebpage.

Imagineamailbox(port)whichismonitoredforincomingletters(requests).Thisisdonebyawebserver.Thewebserverreadstheletter,andsendsaresponsewithawebpage.Butwhenyouwanttosendsomething,youneedtohavesomecontent.AndDjangoissomethingthathelpsyoucreatethecontent.

Whathappenswhensomeonerequestsawebsitefromyourserver?Whenarequestcomestoawebserverit'spassedtoDjangowhichtriestofigureoutwhatactuallyisrequested.Ittakesawebpageaddressfirstandtriestofigureoutwhattodo.ThispartisdonebyDjango'surlresolver(notethatawebsiteaddressiscalledaURL-UniformResourceLocator-sothenameurlresolvermakessense).Itisnotverysmart-ittakesalistofpatternsandtriestomatchtheURL.DjangocheckspatternsfromtoptothebottomandifsomethingismatchedthenDjangopassestherequesttotheassociatedfunction(whichiscalledview).

Imagineamailcarrierwithaletter.Sheiswalkingdownthestreetandcheckseachhousenumberagainsttheoneontheletter.Ifitmatches,sheputstheletterthere.Thisishowtheurlresolverworks!

Intheviewfunctionalltheinterestingthingsaredone:wecanlookatadatabasetolookforsomeinformation.Maybetheuseraskedtochangesomethinginthedata?Likealettersaying"Pleasechangethedescriptionofmyjob."Theviewcancheckifyouareallowedtodothat,thenupdatethejobdescriptionforyouandsendbackamessage:"Done!".ThentheviewgeneratesaresponseandDjangocansendittotheuser'swebbrowser.

Ofcourse,thedescriptionaboveisalittlebitsimplified,butyoudon'tneedtoknowallthetechnicalthingsyet.Havingageneralideaisenough.

Soinsteadofdivingtoomuchintodetails,wewillsimplystartcreatingsomethingwithDjangoandwewilllearnalltheimportantpartsalongtheway!

WhatisDjango?

49

DjangoinstallationIfyou'reusingaChromebook,skipthischapterandmakesureyoufollowtheChromebookSetupinstructions.

NoteIfyoualreadyworkedthroughtheInstallationstepsthenyou'vealreadydonethis-youcangostraighttothenextchapter!

PartofthissectionisbasedontutorialsbyGeekGirlsCarrots(https://github.com/ggcarrots/django-carrots).

Partofthissectionisbasedonthedjango-marcadortutoriallicensedundertheCreativeCommonsAttribution-ShareAlike4.0InternationalLicense.Thedjango-marcadortutorialiscopyrightedbyMarkusZapke-Gründemannetal.

VirtualenvironmentBeforeweinstallDjangowewillgetyoutoinstallanextremelyusefultooltohelpkeepyourcodingenvironmenttidyonyourcomputer.It'spossibletoskipthisstep,butit'shighlyrecommended.Startingwiththebestpossiblesetupwillsaveyoualotoftroubleinthefuture!

So,let'screateavirtualenvironment(alsocalledavirtualenv).VirtualenvwillisolateyourPython/Djangosetuponaper-projectbasis.Thismeansthatanychangesyoumaketoonewebsitewon'taffectanyothersyou'realsodeveloping.Neat,right?

Allyouneedtodoisfindadirectoryinwhichyouwanttocreatethevirtualenv;yourhomedirectory,forexample.OnWindowsitmightlooklikeC:\Users\Name\(whereNameisthenameofyourlogin).

NOTE:OnWindows,makesurethatthisdirectorydoesnotcontainaccentedorspecialcharacters;ifyourusernamecontainsaccentedcharacters,useadifferentdirectory,forexampleC:\djangogirls.

Forthistutorialwewillbeusinganewdirectorydjangogirlsfromyourhomedirectory:

command-line

$mkdirdjangogirls

$cddjangogirls

Wewillmakeavirtualenvcalledmyvenv.Thegeneralcommandwillbeintheformat:

command-line

$python3-mvenvmyvenv

WindowsTocreateanewvirtualenv,youneedtoopentheconsole(wetoldyouaboutthatafewchaptersago–remember?)andrunC:\Python35\python-mvenvmyvenv.Itwilllooklikethis:

command-line

C:\Users\Name\djangogirls>C:\Python35\python-mvenvmyvenv

whereC:\Python35\pythonisthedirectoryinwhichyoupreviouslyinstalledPythonandmyvenvisthenameofyourvirtualenv.Youcanuseanyothername,butsticktolowercaseandusenospaces,accentsorspecialcharacters.Itisalsogoodideatokeepthenameshort–you'llbereferencingitalot!

Djangoinstallation

50

LinuxandOSX

CreatingavirtualenvonbothLinuxandOSXisassimpleasrunningpython3-mvenvmyvenv.Itwilllooklikethis:

command-line

$python3-mvenvmyvenv

myvenvisthenameofyourvirtualenv.Youcanuseanyothername,butsticktolowercaseandusenospaces.Itisalsogoodideatokeepthenameshortasyou'llbereferencingitalot!

NOTE:OnsomeversionsofDebian/Ubuntuyoumayreceivethefollowingerror:

command-line

Thevirtualenvironmentwasnotcreatedsuccessfullybecauseensurepipisnotavailable.OnDebian/Ubuntusys

tems,youneedtoinstallthepython3-venvpackageusingthefollowingcommand.

apt-getinstallpython3-venv

Youmayneedtousesudowiththatcommand.Afterinstallingthepython3-venvpackage,recreateyourvirtual

environment.

Inthiscase,followtheinstructionsaboveandinstallthepython3-venvpackage:

command-line

$sudoapt-getinstallpython3-venv

NOTE:OnsomeversionsofDebian/Ubuntuinitiatingthevirtualenvironmentlikethiscurrentlygivesthefollowingerror:

command-line

Error:Command'['/home/eddie/Slask/tmp/venv/bin/python3','-Im','ensurepip','--upgrade','--default-pip']'

returnednon-zeroexitstatus1

Togetaroundthis,usethevirtualenvcommandinstead.

command-line

$sudoapt-getinstallpython-virtualenv

$virtualenv--python=python3.5myvenv

NOTE:Ifyougetanerrorlike

command-line

E:Unabletolocatepackagepython3-venv

theninsteadrun:

command-line

sudoaptinstallpython3.5-venv

Workingwithvirtualenv

Djangoinstallation

51

Thecommandabovewillcreateadirectorycalledmyvenv(orwhatevernameyouchose)thatcontainsourvirtualenvironment(basicallyabunchofdirectoryandfiles).

Windows

Startyourvirtualenvironmentbyrunning:

command-line

C:\Users\Name\djangogirls>myvenv\Scripts\activate

NOTE:onWindows10youmightgetanerrorintheWindowsPowerShellsaysexecutionofscriptsisdisabledonthissystem.InthosecasesopenanotherWindowsPowerShellandRunasAdministratortrydoingthisbeforecontinue:

command-line

C:\WINDOWS\system32>Set-ExecutionPolicy-ExecutionPolicyRemoteSigned

ExecutionPolicyChange

Theexecutionpolicyhelpsprotectyoufromscriptsthatyoudonottrust.Changingtheexecutionpolicym

ightexposeyoutothesecurityrisksdescribedintheabout_Execution_Policieshelptopicathttp://go.micros

oft.com/fwlink/?LinkID=135170.Doyouwanttochangetheexecutionpolicy?[Y]Yes[A]YestoAll[N]No[L

]NotoAll[S]Suspend[?]Help(defaultis"N"):A

LinuxandOSXStartyourvirtualenvironmentbyrunning:

command-line

$sourcemyvenv/bin/activate

Remembertoreplacemyvenvwithyourchosenvirtualenvname!

NOTE:sometimessourcemightnotbeavailable.Inthosecasestrydoingthisinstead:

command-line

$.myvenv/bin/activate

Youwillknowthatyouhavevirtualenvstartedwhenyouseethatthepromptinyourconsoleisprefixedwith(myvenv).

Whenworkingwithinavirtualenvironment,pythonwillautomaticallyrefertothecorrectversionsoyoucanusepythoninsteadofpython3.

OK,wehaveallimportantdependenciesinplace.WecanfinallyinstallDjango!

InstallingDjangoNowthatyouhaveyourvirtualenvstarted,youcaninstallDjango.

Beforewedothat,weshouldmakesurewehavethelatestversionofpip,thesoftwarethatweusetoinstallDjango.Intheconsole,runpipinstall--upgradepip.

Thenrunpipinstalldjango~=1.9.0(notethatweuseatildefollowedbyanequalsign:~=)toinstallDjango.

command-line

Djangoinstallation

52

(myvenv)~$pipinstalldjango~=1.9.0

Downloading/unpackingdjango==1.9

Installingcollectedpackages:django

Successfullyinstalleddjango

Cleaningup...

onWindows

IfyougetanerrorwhencallingpiponWindowsplatform,pleasecheckifyourprojectpathnamecontainsspaces,accentsorspecialcharacters(forexample,C:\Users\UserName\djangogirls).Ifitdoes,pleaseconsiderusinganotherplacewithoutspaces,accentsorspecialcharacters(suggestion:C:\djangogirls).Createanewvirtualenvinthenewdirectory,thendeletetheoldoneandtrytheabovecommandagain.(Movingthevirtualenvdirectorywon'tworksincevirtualenvusesabsolutepaths.)

onWindows8andWindows10

YourcommandlinemightfreezeafterwhenyoutrytoinstallDjango.Ifthishappens,insteadoftheabovecommanduse:

command-line

C:\Users\Name\djangogirls>python-mpipinstalldjango~=1.9.0

onLinux

IfyougetanerrorwhencallingpiponUbuntu12.04pleaserunpython-mpipinstall-U--force-reinstallpiptofixthepipinstallationinthevirtualenv.

That'sit!You'renow(finally)readytocreateaDjangoapplication!

Djangoinstallation

53

YourfirstDjangoproject!PartofthischapterisbasedontutorialsbyGeekGirlsCarrots(https://github.com/ggcarrots/django-carrots).

Partsofthischapterarebasedonthedjango-marcadortutoriallicensedunderCreativeCommonsAttribution-ShareAlike4.0InternationalLicense.Thedjango-marcadortutorialiscopyrightedbyMarkusZapke-Gründemannetal.

We'regoingtocreateasimpleblog!

ThefirststepistostartanewDjangoproject.Basically,thismeansthatwe'llrunsomescriptsprovidedbyDjangothatwillcreatetheskeletonofaDjangoprojectforus.Thisisjustabunchofdirectoriesandfilesthatwewilluselater.

ThenamesofsomefilesanddirectoriesareveryimportantforDjango.Youshouldnotrenamethefilesthatweareabouttocreate.Movingthemtoadifferentplaceisalsonotagoodidea.Djangoneedstomaintainacertainstructuretobeabletofindimportantthings.

Remembertoruneverythinginthevirtualenv.Ifyoudon'tseeaprefix(myvenv)inyourconsoleyouneedtoactivateyourvirtualenv.WeexplainedhowtodothatintheDjangoinstallationchapterintheWorkingwithvirtualenvpart.Typingmyvenv\Scripts\activateonWindowsorsourcemyvenv/bin/activateonMacOS/Linuxwilldothisforyou.

InyourMacOSorLinuxconsoleyoushouldrunthefollowingcommand;don'tforgettoaddtheperiod(ordot).attheend:

command-line

(myvenv)~/djangogirls$django-adminstartprojectmysite.

OnWindows;don'tforgettoaddtheperiod(ordot).attheend:

command-line

(myvenv)C:\Users\Name\djangogirls>django-admin.pystartprojectmysite.

Theperiod.iscrucialbecauseittellsthescripttoinstallDjangoinyourcurrentdirectory(forwhichtheperiod.isashort-handreference)

NoteWhentypingthecommandsabove,rememberthatyouonlytypethepartwhichstartsdjango-adminordjango-admin.py.The(myvenv)~/djangogirls$and(myvenv)C:\Users\Name\djangogirls>partsshownherearejustexamplesofthepromptthatwillbeinvitingyourinputonyourcommandline.

django-admin.pyisascriptthatwillcreatethedirectoriesandfilesforyou.Youshouldnowhaveadirectorystructurewhichlookslikethis:

djangogirls

├───manage.py

└───mysite

settings.py

urls.py

wsgi.py

__init__.py

manage.pyisascriptthathelpswithmanagementofthesite.Withitwewillbeabletostartawebserveronourcomputerwithoutinstallinganythingelse,amongstotherthings.

Thesettings.pyfilecontainstheconfigurationofyourwebsite.

YourfirstDjangoproject!

54

Rememberwhenwetalkedaboutamailcarriercheckingwheretodeliveraletter?urls.pyfilecontainsalistofpatternsusedbyurlresolver.

Let'signoretheotherfilesfornowaswewon'tchangethem.Theonlythingtorememberisnottodeletethembyaccident!

ChangingsettingsLet'smakesomechangesinmysite/settings.py.Openthefileusingthecodeeditoryouinstalledearlier.

Itwouldbenicetohavethecorrecttimeonourwebsite.Gotothewikipediatimezoneslistandcopyyourrelevanttimezone(TZ).(eg.Europe/Berlin)

Insettings.py,findthelinethatcontainsTIME_ZONEandmodifyittochooseyourowntimezone:

mysite/settings.py

TIME_ZONE='Europe/Berlin'

Modifying"Europe/Berlin"asappropriate

We'llalsoneedtoaddapathforstaticfiles(we'llfindoutallaboutstaticfilesandCSSlaterinthetutorial).Godowntotheendofthefile,andjustunderneaththeSTATIC_URLentry,addanewonecalledSTATIC_ROOT:

mysite/settings.py

STATIC_URL='/static/'

STATIC_ROOT=os.path.join(BASE_DIR,'static')

:Ifyou'reusingaChromebook,addthislineatthebottomofyoursettings.pyfile:MESSAGE_STORAGE='django.contrib.messages.storage.session.SessionStorage'

SetupadatabaseThere'salotofdifferentdatabasesoftwarethatcanstoredataforyoursite.We'llusethedefaultone,sqlite3.

Thisisalreadysetupinthispartofyourmysite/settings.pyfile:

mysite/settings.py

DATABASES={

'default':{

'ENGINE':'django.db.backends.sqlite3',

'NAME':os.path.join(BASE_DIR,'db.sqlite3'),

}

}

Tocreateadatabaseforourblog,let'srunthefollowingintheconsole:pythonmanage.pymigrate(weneedtobeinthedjangogirlsdirectorythatcontainsthemanage.pyfile).Ifthatgoeswell,youshouldseesomethinglikethis:

command-line

YourfirstDjangoproject!

55

(myvenv)~/djangogirls$pythonmanage.pymigrate

Operationstoperform:

Applyallmigrations:auth,admin,contenttypes,sessions

Runningmigrations:

Renderingmodelstates...DONE

Applyingcontenttypes.0001_initial...OK

Applyingauth.0001_initial...OK

Applyingadmin.0001_initial...OK

Applyingadmin.0002_logentry_remove_auto_add...OK

Applyingcontenttypes.0002_remove_content_type_name...OK

Applyingauth.0002_alter_permission_name_max_length...OK

Applyingauth.0003_alter_user_email_max_length...OK

Applyingauth.0004_alter_user_username_opts...OK

Applyingauth.0005_alter_user_last_login_null...OK

Applyingauth.0006_require_contenttypes_0002...OK

Applyingauth.0007_alter_validators_add_error_messages...OK

Applyingsessions.0001_initial...OK

Andwe'redone!Timetostartthewebserverandseeifourwebsiteisworking!

StartingthewebserverYouneedtobeinthedirectorythatcontainsthemanage.pyfile(thedjangogirlsdirectory).Intheconsole,wecanstartthewebserverbyrunningpythonmanage.pyrunserver:

command-line

(myvenv)~/djangogirls$pythonmanage.pyrunserver

IfyouareonaChromebook,usethiscommandinstead:

Cloud9

(myvenv)~/djangogirls$pythonmanage.pyrunserver0.0.0.0:8080

IfyouareonWindowsandthisfailswithUnicodeDecodeError,usethiscommandinstead:

command-line

(myvenv)~/djangogirls$pythonmanage.pyrunserver0:8000

Nowallyouneedtodoischeckthatyourwebsiteisrunning.Openyourbrowser(Firefox,Chrome,Safari,InternetExplorerorwhateveryouuse)andentertheaddress:

browser

http://127.0.0.1:8000/

Ifyou'reusingaChromebook,you'llalwaysvisityourtestserverbyaccessing:

browser

https://django-girls-<yourcloud9username>.c9users.io

Congratulations!You'vejustcreatedyourfirstwebsiteandrunitusingawebserver!Isn'tthatawesome?

YourfirstDjangoproject!

56

Whilethewebserverisrunning,youwon'tseeanewcommandlineprompttoenteradditonalcommands.Theterminalwillacceptnewtextbutwillnotexecutenewcommands.Thisisbecausethewebservercontinuouslyrunsinordertolistenforincomingrequests.

WereviewedhowwebserversworkintheHowtheInternetworkschapter.

Totypeadditionalcommandswhilethewebserverisrunning,openanewterminalwindowandactivateyourvirtualenv.Tostopthewebserver,switchbacktothewindowinwhichit'srunningandpressCTRL+C-ControlandCbuttonstogether(onWindows,youmighthavetopressCtrl+Break).

Readyforthenextstep?It'stimetocreatesomecontent!

YourfirstDjangoproject!

57

DjangomodelsWhatwewanttocreatenowissomethingthatwillstoreallthepostsinourblog.Buttobeabletodothatweneedtotalkalittlebitaboutthingscalledobjects.

ObjectsThereisaconceptinprogrammingcalledObject-orientedprogramming.Theideaisthatinsteadofwritingeverythingasaboringsequenceofprogramminginstructionswecanmodelthingsanddefinehowtheyinteractwitheachother.

Sowhatisanobject?Itisacollectionofpropertiesandactions.Itsoundsweird,butwewillgiveyouanexample.

IfwewanttomodelacatwewillcreateanobjectCatthathassomepropertiessuchas:color,age,mood(likegood,bad,orsleepy;)),andowner(thatisaPersonobjectormaybe,incaseofastraycat,thispropertyisempty).

ThentheCathassomeactions:purr,scratch,orfeed(inwhichcase,wewillgivethecatsomeCatFood,whichcouldbeaseparateobjectwithproperties,liketaste).

Cat

--------

color

age

mood

owner

purr()

scratch()

feed(cat_food)

CatFood

--------

taste

Sobasicallytheideaistodescriberealthingsincodewithproperties(calledobjectproperties)andactions(calledmethods).

Howwillwemodelblogpoststhen?Wewanttobuildablog,right?

Weneedtoanswerthequestion:Whatisablogpost?Whatpropertiesshouldithave?

Well,forsureourblogpostneedssometextwithitscontentandatitle,right?Itwouldbealsonicetoknowwhowroteit-soweneedanauthor.Finally,wewanttoknowwhenthepostwascreatedandpublished.

Post

--------

title

text

author

created_date

published_date

Whatkindofthingscouldbedonewithablogpost?Itwouldbenicetohavesomemethodthatpublishesthepost,right?

Sowewillneedapublishmethod.

Sincewealreadyknowwhatwewanttoachieve,let'sstartmodelingitinDjango!

Djangomodel

Djangomodels

58

Knowingwhatanobjectis,wecancreateaDjangomodelforourblogpost.

AmodelinDjangoisaspecialkindofobject-itissavedinthedatabase.Adatabaseisacollectionofdata.Thisisaplaceinwhichyouwillstoreinformationaboutusers,yourblogposts,etc.WewillbeusingaSQLitedatabasetostoreourdata.ThisisthedefaultDjangodatabaseadapter--it'llbeenoughforusrightnow.

Youcanthinkofamodelinthedatabaseasaspreadsheetwithcolumns(fields)androws(data).

CreatinganapplicationTokeepeverythingtidy,wewillcreateaseparateapplicationinsideourproject.Itisverynicetohaveeverythingorganizedfromtheverybeginning.Tocreateanapplicationweneedtorunthefollowingcommandintheconsole(fromdjangogirlsdirectorywheremanage.pyfileis):

command-line

(myvenv)~/djangogirls$pythonmanage.pystartappblog

Youwillnoticethatanewblogdirectoryiscreatedanditcontainsanumberoffilesnow.Ourdirectoriesandfilesinourprojectshouldlooklikethis:

djangogirls

├──blog

│├──__init__.py

│├──admin.py

│├──apps.py

│├──migrations

││└──__init__.py

│├──models.py

│├──tests.py

│└──views.py

├──db.sqlite3

├──manage.py

└──mysite

├──__init__.py

├──settings.py

├──urls.py

└──wsgi.py

AftercreatinganapplicationwealsoneedtotellDjangothatitshoulduseit.Wedothatinthefilemysite/settings.py.WeneedtofindINSTALLED_APPSandaddalinecontaining'blog',justabove).Sothefinalproductshouldlooklikethis:

mysite/settings.py

INSTALLED_APPS=(

'django.contrib.admin',

'django.contrib.auth',

'django.contrib.contenttypes',

'django.contrib.sessions',

'django.contrib.messages',

'django.contrib.staticfiles',

'blog',

)

Creatingablogpostmodel

Intheblog/models.pyfilewedefineallobjectscalledModels-thisisaplaceinwhichwewilldefineourblogpost.

Let'sopenblog/models.py,removeeverythingfromitandwritecodelikethis:

blog/models.py

Djangomodels

59

fromdjango.dbimportmodels

fromdjango.utilsimporttimezone

classPost(models.Model):

author=models.ForeignKey('auth.User')

title=models.CharField(max_length=200)

text=models.TextField()

created_date=models.DateTimeField(

default=timezone.now)

published_date=models.DateTimeField(

blank=True,null=True)

defpublish(self):

self.published_date=timezone.now()

self.save()

def__str__(self):

returnself.title

Double-checkthatyouusetwounderscorecharacters(_)oneachsideofstr.ThisconventionisusedfrequentlyinPythonandsometimeswealsocallthem"dunder"(shortfor"double-underscore").

Itlooksscary,right?Butnoworrieswewillexplainwhattheselinesmean!

Alllinesstartingwithfromorimportarelinesthataddsomebitsfromotherfiles.Soinsteadofcopyingandpastingthesamethingsineveryfile,wecanincludesomepartswithfrom...import....

classPost(models.Model):-thislinedefinesourmodel(itisanobject).

classisaspecialkeywordthatindicatesthatwearedefininganobject.Postisthenameofourmodel.Wecangiveitadifferentname(butwemustavoidspecialcharactersandwhitespaces).Alwaysstartaclassnamewithanuppercaseletter.models.ModelmeansthatthePostisaDjangoModel,soDjangoknowsthatitshouldbesavedinthedatabase.

Nowwedefinethepropertiesweweretalkingabout:title,text,created_date,published_dateandauthor.Todothatweneedtodefineatypeofeachfield(Isittext?Anumber?Adate?Arelationtoanotherobject,likeaUser?).

models.CharField-thisishowyoudefinetextwithalimitednumberofcharacters.models.TextField-thisisforlongtextwithoutalimit.Soundsidealforblogpostcontent,right?models.DateTimeField-thisisadateandtime.models.ForeignKey-thisisalinktoanothermodel.

Wewillnotexplaineverybitofcodeheresinceitwouldtaketoomuchtime.YoushouldtakealookatDjango'sdocumentationifyouwanttoknowmoreaboutModelfieldsandhowtodefinethingsotherthanthosedescribedabove(https://docs.djangoproject.com/en/1.9/ref/models/fields/#field-types).

Whataboutdefpublish(self):?Itisexactlythepublishmethodweweretalkingaboutbefore.defmeansthatthisisafunction/methodandpublishisthenameofthemethod.Youcanchangethenameofthemethod,ifyouwant.Thenamingruleisthatweuselowercaseandunderscoresinsteadofwhitespaces.Forexample,amethodthatcalculatesaveragepricecouldbecalledcalculate_average_price.

Methodsoftenreturnsomething.Thereisanexampleofthatinthe__str__method.Inthisscenario,whenwecall__str__()wewillgetatext(string)withaPosttitle.

Alsonoticethatbothdefpublish(self):,anddef__str__(self):areindentedinsideourclass.BecausePythonissensitivetowhitespace,weneedtoindentourmethodsinsidetheclass.Otherwise,themethodswon'tbelongtotheclass,andyoucangetsomeunexpectedbehavior.

Ifsomethingisstillnotclearaboutmodels,feelfreetoaskyourcoach!Weknowitiscomplicated,especiallywhenyoulearnwhatobjectsandfunctionsareatthesametime.Buthopefullyitlooksslightlylessmagicforyounow!

Djangomodels

60

Createtablesformodelsinyourdatabase

Thelaststephereistoaddournewmodeltoourdatabase.FirstwehavetomakeDjangoknowthatwehavesomechangesinourmodel(wehavejustcreatedit!).Gotoyourconsolewindowandtypepythonmanage.pymakemigrationsblog.Itwilllooklikethis:

command-line

(myvenv)~/djangogirls$pythonmanage.pymakemigrationsblog

Migrationsfor'blog':

0001_initial.py:

-CreatemodelPost

Djangopreparedforusamigrationfilethatwehavetoapplynowtoourdatabase.Typepythonmanage.pymigrateblogandtheoutputshouldbe:

command-line

(myvenv)~/djangogirls$pythonmanage.pymigrateblog

Operationstoperform:

Applyallmigrations:blog

Runningmigrations:

Renderingmodelstates...DONE

Applyingblog.0001_initial...OK

Hurray!OurPostmodelisnowinourdatabase!Itwouldbenicetoseeit,right?JumptothenextchaptertoseewhatyourPostlookslike!

Djangomodels

61

DjangoadminToadd,editanddeletepostswe'vejustmodeled,wewilluseDjangoadmin.

Let'sopentheblog/admin.pyfileandreplaceitscontentwiththis:

blog/admin.py

fromdjango.contribimportadmin

from.modelsimportPost

admin.site.register(Post)

Asyoucansee,weimport(include)thePostmodeldefinedinthepreviouschapter.Tomakeourmodelvisibleontheadminpage,weneedtoregisterthemodelwithadmin.site.register(Post).

OK,timetolookatourPostmodel.Remembertorunpythonmanage.pyrunserverintheconsoletorunthewebserver.Gotothebrowserandtypetheaddresshttp://127.0.0.1:8000/admin/Youwillseealoginpagelikethis:

Tologin,youneedtocreateasuperuser-auserwhichhascontrolovereverythingonthesite.Gobacktothecommand-lineandtypepythonmanage.pycreatesuperuser,andpressenter.

Remember,towritenewcommandswhilethewebserverisrunning,openanewterminalwindowandactivateyourvirtualenv.WereviewedhowtowritenewcommandsintheYourfirstDjangoproject!chapter,intheStartingthewebserversection.

Whenprompted,typeyourusername(lowercase,nospaces),emailaddress,andpassword.Don'tworrythatyoucan'tseethepasswordyou'retypingin-that'showit'ssupposedtobe.Justtypeitinandpressentertocontinue.Theoutputshouldlooklikethis(whereusernameandemailshouldbeyourownones):

command-line

(myvenv)~/djangogirls$pythonmanage.pycreatesuperuser

Username:admin

Emailaddress:admin@admin.com

Password:

Password(again):

Superusercreatedsuccessfully.

Djangoadmin

62

Returntoyourbrowser.Loginwiththesuperuser'scredentialsyouchose;youshouldseetheDjangoadmindashboard.

GotoPostsandexperimentalittlebitwithit.Addfiveorsixblogposts.Don'tworryaboutthecontent-youcansimplycopy-pastesometextfromthistutorialtosavetime:).

Makesurethatatleasttwoorthreeposts(butnotall)havethepublishdateset.Itwillbehelpfullater.

IfyouwanttoknowmoreaboutDjangoadmin,youshouldcheckDjango'sdocumentation:https://docs.djangoproject.com/en/1.9/ref/contrib/admin/

Thisisprobablyagoodmomenttograbacoffee(ortea)orsomethingtoeattore-energiseyourself.YoucreatedyourfirstDjangomodel-youdeservealittletimeout!

Djangoadmin

63

Deploy!Thefollowingchaptercanbesometimesabithardtogetthrough.Persistandfinishit;deploymentisanimportantpartofthewebsitedevelopmentprocess.Thischapterisplacedinthemiddleofthetutorialsothatyourmentorcanhelpwiththeslightlytrickierprocessofgettingyourwebsiteonline.Thismeansyoucanstillfinishthetutorialonyourownifyourunoutoftime.

Untilnow,yourwebsitewasonlyavailableonyourcomputer.Nowyouwilllearnhowtodeployit!DeployingistheprocessofpublishingyourapplicationontheInternetsopeoplecanfinallygoandseeyourapp:).

Asyoulearned,awebsitehastobelocatedonaserver.Therearealotofserverprovidersavailableontheinternet.Wewilluseonethathasarelativelysimpledeploymentprocess:PythonAnywhere.PythonAnywhereisfreeforsmallapplicationsthatdon'thavetoomanyvisitorssoit'lldefinitelybeenoughforyounow.

Theotherexternalservicewe'llbeusingisGitHub,whichisacodehostingservice.Thereareothersoutthere,butalmostallprogrammershaveaGitHubaccountthesedays,andnowsowillyou!

Thesethreeplaceswillbeimportanttoyou.Yourlocalcomputerwillbetheplacewhereyoudodevelopmentandtesting.Whenyou'rehappywiththechanges,youwillplaceacopyofyourprogramonGitHub.YourwebsitewillbeonPythonAnywhereandyouwillupdateitbygettinganewcopyofyourcodefromGitHub.

GitGitisa"versioncontrolsystem"usedbyalotofprogrammers.Thissoftwarecantrackchangestofilesovertimesothatyoucanrecallspecificversionslater.Abitlikethe"trackchanges"featureinMicrosoftWord,butmuchmorepowerful.

InstallingGitIfyoualreadydidtheInstallationsteps,noneedtodothisagain-youcanskiptothenextsectionandstartcreatingyourGitrepository.

Windows

YoucandownloadGitfromgit-scm.com.Youcanhit"next"onallstepsexceptforone;inthefifthstepentitled"AdjustingyourPATHenvironment",choose"UseGitandoptionalUnixtoolsfromtheWindowsCommandPrompt"(thebottomoption).Otherthanthat,thedefaultsarefine.CheckoutWindows-style,commitUnix-stylelineendingsisgood.

MacOSDownloadGitfromgit-scm.comandjustfollowtheinstructions.

LinuxIfitisn'tinstalledalready,gitshouldbeavailableviayourpackagemanager,sotry:

DebianorUbuntucommand-line

$sudoapt-getinstallgit

Deploy!

64

Fedora(upto21)

command-line

$sudoyuminstallgit

Fedora(22+)

command-line

$sudodnfinstallgit

openSUSE

command-line

$sudozypperinstallgit

StartingourGitrepositoryGittrackschangestoaparticularsetoffilesinwhat'scalledacoderepository(or"repo"forshort).Let'sstartoneforourproject.Openupyourconsoleandrunthesecommands,inthedjangogirlsdirectory:

Checkyourcurrentworkingdirectorywithapwd(OSX/Linux)orcd(Windows)commandbeforeinitializingtherepository.Youshouldbeinthedjangogirlsfolder.

command-line

$gitinit

InitializedemptyGitrepositoryin~/djangogirls/.git/

$gitconfig--globaluser.name"YourName"

$gitconfig--globaluser.emailyou@example.com

Initializingthegitrepositoryissomethingweonlyneedtodoonceperproject(andyouwon'thavetore-entertheusernameandemailagainever).

Gitwilltrackchangestoallthefilesandfoldersinthisdirectory,buttherearesomefileswewantittoignore.Wedothisbycreatingafilecalled.gitignoreinthebasedirectory.Openupyoureditorandcreateanewfilewiththefollowingcontents:

.gitignore

*.pyc

__pycache__

myvenv

db.sqlite3

/static

.DS_Store

Andsaveitas.gitignoreinthe"djangogirls"folder.

Deploy!

65

Thedotatthebeginningofthefilenameisimportant!Ifyou'rehavinganydifficultycreatingit(Macsdon'tlikeyoutocreatefilesthatbeginwithadotviatheFinder,forexample),thenusethe"SaveAs"featureinyoureditor,it'sbulletproof.

NoteOneofthefilesyouspecifiedinyour.gitignorefileisdb.sqlite3.Thatfileisyourlocaldatabase,wherealloryourpostsarestored.Wedon'twanttoaddthistoyourrepository,becauseyourwebsiteonPythonAnywhereisgoingtobeusingadifferentdatabase.ThatdatabasecouldbeSQLite,likeyourdevelopmentmachine,butusually,youwilluseonecalledMySQLwhichcandealwithalotmoresitevisitorsthanSQLite.Eitherway,byignoringyourSQLitedatabasefortheGitHubcopy,itmeansthatallofthepostsyoucreatedsofararegoingtostayandonlybeavailablelocally,butyou'regonnahavetoaddthemagainonproduction.Youshouldthinkofyourlocaldatabaseasagoodplaygroundwhereyoucantestdifferentthingsandnotbeafraidthatyou'regoingtodeleteyourrealpostsfromyourblog.

It'sagoodideatouseagitstatuscommandbeforegitaddorwheneveryoufindyourselfunsureofwhathaschanged.Thiswillhelpstopanysurprisesfromhappening,suchaswrongfilesbeingaddedorcommited.Thegitstatuscommandreturnsinformationaboutanyuntracked/modifed/stagedfiles,branchstatus,andmuchmore.Theoutputshouldbesimilarto:

command-line

$gitstatus

Onbranchmaster

Initialcommit

Untrackedfiles:

(use"gitadd<file>..."toincludeinwhatwillbecommitted)

.gitignore

blog/

manage.py

mysite/

nothingaddedtocommitbutuntrackedfilespresent(use"gitadd"totrack)

Andfinallywesaveourchanges.Gotoyourconsoleandrunthesecommands:

command-line

$gitadd--all.

$gitcommit-m"MyDjangoGirlsapp,firstcommit"

[...]

13fileschanged,200insertions(+)

createmode100644.gitignore

[...]

createmode100644mysite/wsgi.py

PushingourcodetoGitHubGotoGitHub.comandsignupforanew,freeuseraccount.(Ifyoualreadydidthatintheworkshopprep,thatisgreat!)

Then,createanewrepository,givingitthename"my-first-blog".Leavethe"initialisewithaREADME"tickboxun-checked,leavethe.gitignoreoptionblank(we'vedonethatmanually)andleavetheLicenseasNone.

Deploy!

66

Thenamemy-first-blogisimportant--youcouldchoosesomethingelse,butit'sgoingtooccurlotsoftimesintheinstructionsbelow,andyou'dhavetosubstituteiteachtime.It'sprobablyeasiertojuststickwiththenamemy-first-blog.

Onthenextscreen,you'llbeshownyourrepo'scloneURL.Choosethe"HTTPS"version,copyit,andwe'llpasteitintotheterminalshortly:

NowweneedtohookuptheGitrepositoryonyourcomputertotheoneuponGitHub.

Typethefollowingintoyourconsole(Replace<your-github-username>withtheusernameyouenteredwhenyoucreatedyourGitHubaccount,butwithouttheangle-brackets):

command-line

Deploy!

67

$gitremoteaddoriginhttps://github.com/<your-github-username>/my-first-blog.git

$gitpush-uoriginmaster

EnteryourGitHubusernameandpasswordandyoushouldseesomethinglikethis:

command-line

Usernamefor'https://github.com':hjwp

Passwordfor'https://hjwp@github.com':

Countingobjects:6,done.

Writingobjects:100%(6/6),200bytes|0bytes/s,done.

Total3(delta0),reused0(delta0)

Tohttps://github.com/hjwp/my-first-blog.git

*[newbranch]master->master

Branchmastersetuptotrackremotebranchmasterfromorigin.

YourcodeisnowonGitHub.Goandcheckitout!You'llfindit'sinfinecompany-Django,theDjangoGirlsTutorial,andmanyothergreatopensourcesoftwareprojectsalsohosttheircodeonGitHub:)

SettingupourblogonPythonAnywhereYoumighthavealreadycreatedaPythonAnywhereaccountearlierduringtheinstallsteps-ifso,noneedtodoitagain.

Nextit'stimetosignupforafree"Beginner"accountonPythonAnywhere.

www.pythonanywhere.com

Whenchoosingyourusernamehere,bearinmindthatyourblog'sURLwilltaketheformyourusername.pythonanywhere.com,sochooseeitheryourownnickname,oranameforwhatyourblogisallabout.

PullingourcodedownonPythonAnywhereWhenyou'vesignedupforPythonAnywhere,you'llbetakentoyourdashboardor"Consoles"page.Choosetheoptiontostarta"Bash"console--that'sthePythonAnywhereversionofaconsole,justliketheoneonyourcomputer.

PythonAnywhereisbasedonLinux,soifyou'reonWindows,theconsolewilllookalittledifferentfromtheoneonyourcomputer.

Let'spulldownourcodefromGitHubandontoPythonAnywherebycreatinga"clone"ofourrepo.TypethefollowingintotheconsoleonPythonAnywhere(don'tforgettouseyourGitHubusernameinplaceof<your-github-username>):

PythonAnywherecommand-line

$gitclonehttps://github.com/<your-github-username>/my-first-blog.git

ThiswillpulldownacopyofyourcodeontoPythonAnywhere.Checkitoutbytypingtreemy-first-blog:

PythonAnywherecommand-line

Deploy!

68

$treemy-first-blog

my-first-blog/

├──blog

│├──__init__.py

│├──admin.py

│├──migrations

││├──0001_initial.py

││└──__init__.py

│├──models.py

│├──tests.py

│└──views.py

├──manage.py

└──mysite

├──__init__.py

├──settings.py

├──urls.py

└──wsgi.py

CreatingavirtualenvonPythonAnywhereJustlikeyoudidonyourowncomputer,youcancreateavirtualenvonPythonAnywhere.IntheBashconsole,type:

PythonAnywherecommand-line

$cdmy-first-blog

$virtualenv--python=python3.5myvenv

Runningvirtualenvwithinterpreter/usr/bin/python3.5

[...]

Installingsetuptools,pip...done.

$sourcemyvenv/bin/activate

(myvenv)$pipinstalldjango~=1.9.0

Collectingdjango

[...]

Successfullyinstalleddjango-1.9

Thepipinstallstepcantakeacoupleofminutes.Patience,patience!Butifittakesmorethan5minutes,somethingiswrong.Askyourcoach.

CreatingthedatabaseonPythonAnywhere

Here'sanotherthingthat'sdifferentbetweenyourowncomputerandtheserver:itusesadifferentdatabase.Sotheuseraccountsandpostscanbedifferentontheserverandonyourcomputer.

Wecaninitialisethedatabaseontheserverjustlikewedidtheoneonyourowncomputer,withmigrateandcreatesuperuser:

PythonAnywherecommand-line

(mvenv)$pythonmanage.pymigrate

Operationstoperform:

[...]

Applyingsessions.0001_initial...OK

(mvenv)$pythonmanage.pycreatesuperuser

Publishingourblogasawebapp

Deploy!

69

NowourcodeisonPythonAnywhere,ourvirtualenvisready,andthedatabaseisinitialised.We'rereadytopublishitasawebapp!

ClickbacktothePythonAnywheredashboardbyclickingonitslogo,andgoclickontheWebtab.Finally,hitAddanewwebapp.

Afterconfirmingyourdomainname,choosemanualconfiguration(NBnotthe"Django"option)inthedialog.NextchoosePython3.5,andclickNexttofinishthewizard.

Makesureyouchoosethe"Manualconfiguration"option,notthe"Django"one.We'retoocoolforthedefaultPythonAnywhereDjangosetup;-)

SettingthevirtualenvYou'llbetakentothePythonAnywhereconfigscreenforyourwebapp,whichiswhereyou'llneedtogowheneveryouwanttomakechangestotheappontheserver.

Inthe"Virtualenv"section,clicktheredtextthatsays"Enterthepathtoavirtualenv",andenter:/home/<your-PythonAnywhere-username>/my-first-blog/myvenv/.Clicktheblueboxwiththecheckmarktosavethepathbeforemovingon.

SubstituteyourownPythonAnywhereusernameasappropriate.Ifyoumakeamistake,PythonAnywherewillshowyoualittlewarning.

ConfiguringtheWSGIfile

Djangoworksusingthe"WSGIprotocol",astandardforservingwebsitesusingPython,whichPythonAnywheresupports.ThewayweconfigurePythonAnywheretorecogniseourDjangoblogisbyeditingaWSGIconfigurationfile.

Clickonthe"WSGIconfigurationfile"link(inthe"Code"sectionnearthetopofthepage--it'llbenamedsomethinglike/var/www/<your-PythonAnywhere-username>_pythonanywhere_com_wsgi.py),andyou'llbetakentoaneditor.

Deleteallthecontentsandreplacethemwithsomethinglikethis:

<your-username>_pythonanywhere_com_wsgi.py

Deploy!

70

importos

importsys

path='/home/<your-PythonAnywhere-username>/my-first-blog'#useyourownPythonAnywhereusernamehere

ifpathnotinsys.path:

sys.path.append(path)

os.environ['DJANGO_SETTINGS_MODULE']='mysite.settings'

fromdjango.core.wsgiimportget_wsgi_application

fromdjango.contrib.staticfiles.handlersimportStaticFilesHandler

application=StaticFilesHandler(get_wsgi_application())

Don'tforgettosubstituteinyourownPythonAnywhereusernamewhereitsays<your-PythonAnywhere-username>NoteInlinefour,wemakesurePythonanywhereknowshowtofindourapplication.Itisveryimportantthatthispathnameiscorrect,andespeciallythattherearenoextraspaceshere.Otherwiseyouwillseean"ImportError"intheerrorlog.

Thisfile'sjobistotellPythonAnywherewhereourwebapplivesandwhattheDjangosettingsfile'snameis.

TheStaticFilesHandlerisfordealingwithourCSS.Thisistakencareofautomaticallyforyouduringlocaldevelopmentbytherunservercommand.We'llfindoutabitmoreaboutstaticfileslaterinthetutorial,whenweedittheCSSforoursite.

HitSaveandthengobacktotheWebtab.

We'realldone!HitthebiggreenReloadbuttonandyou'llbeabletogoviewyourapplication.You'llfindalinktoitatthetopofthepage.

DebuggingtipsIfyouseeanerrorwhenyoutrytovisityoursite,thefirstplacetolookforsomedebugginginfoisinyourerrorlog.You'llfindalinktothisonthePythonAnywhereWebtab.Seeifthereareanyerrormessagesinthere;themostrecentonesareatthebottom.Commonproblemsinclude:

Forgettingoneofthestepswedidintheconsole:creatingthevirtualenv,activatingit,installingDjangointoit,migratingthedatabase.

MakingamistakeinthevirtualenvpathontheWebtab--therewillusuallybealittlerederrormessageonthere,ifthereisaproblem.

MakingamistakeintheWSGIconfigurationfile--didyougetthepathtoyourmy-first-blogfolderright?

DidyoupickthesameversionofPythonforyourvirtualenvasyoudidforyourwebapp?Bothshouldbe3.5.

TherearesomegeneraldebuggingtipsonthePythonAnywherewiki.

Andremember,yourcoachisheretohelp!

Youarelive!Thedefaultpageforyoursiteshouldsay"WelcometoDjango",justlikeitdoesonyourlocalcomputer.Tryadding/admin/totheendoftheURL,andyou'llbetakentotheadminsite.Loginwiththeusernameandpassword,andyou'llseeyoucanaddnewPostsontheserver.

Onceyouhaveafewpostscreated,youcangobacktoyourlocalsetup(notPythonAnywhere).Fromhereyoushouldworkonyourlocalsetuptomakechanges.ThisisacommonworkflowinWebdevelopment(makechangeslocally,pushthosechangestoGitHub,pullyourchangesdowntoyourliveWebserver).Thisallowsyoutoworkandexperimentwithout

Deploy!

71

breakingyourliveWebsite.Prettycool,huh?

GiveyourselfaHUGEpatontheback!Serverdeploymentsareoneofthetrickiestpartsofwebdevelopmentanditoftentakespeopleseveraldaysbeforetheygetthemworking.Butyou'vegotyoursitelive,ontherealInternet,justlikethat!

Deploy!

72

DjangoURLsWe'reabouttobuildourfirstwebpage:ahomepageforyourblog!Butfirst,let'slearnalittlebitaboutDjangourls.

WhatisaURL?AURLissimplyawebaddress.YoucanseeaURLeverytimeyouvisitawebsite-itisvisibleinyourbrowser'saddressbar(yes!127.0.0.1:8000isaURL!Andhttps://djangogirls.orgisalsoaURL):

EverypageontheInternetneedsitsownURL.ThiswayyourapplicationknowswhatitshouldshowtoauserwhoopensaURL.InDjangoweusesomethingcalledURLconf(URLconfiguration).URLconfisasetofpatternsthatDjangowilltrytomatchwiththereceivedURLtofindthecorrectview.

HowdoURLsworkinDjango?Let'sopenupthemysite/urls.pyfileinyourcodeeditorofchoiceandseewhatitlookslike:

mysite/urls.py

"""mysiteURLConfiguration

[...]

"""

fromdjango.conf.urlsimporturl

fromdjango.contribimportadmin

urlpatterns=[

url(r'^admin/',admin.site.urls),

]

Asyoucansee,Djangoalreadyputsomethinghereforus.

Linesbetweentriplequotes('''or""")arecalleddocstrings-youcanwritethematthetopofafile,classormethodtodescribewhatitdoes.Theywon'tberunbyPython.

TheadminURL,whichyouvisitedinpreviouschapterisalreadyhere:

mysite/urls.py

url(r'^admin/',admin.site.urls),

ItmeansthatforeveryURLthatstartswithadmin/Djangowillfindacorrespondingview.Inthiscasewe'reincludingalotofadminURLssoitisn'tallpackedintothissmallfile--it'smorereadableandcleaner.

Djangourls

73

RegexDoyouwonderhowDjangomatchesURLstoviews?Well,thispartistricky.Djangousesregex,shortfor"regularexpressions".Regexhasalot(alot!)ofrulesthatformasearchpattern.Sinceregexesareanadvancedtopic,wewillnotgoindetailoverhowtheywork.

Ifyoustillwishtounderstandhowwecreatedthepatterns,hereisanexampleoftheprocess-wewillonlyneedalimitedsubsetoftherulestoexpressthepatternwearelookingfor,namely:

^forbeginningofthetext

$forendoftext

\dforadigit

+toindicatethatthepreviousitemshouldberepeatedatleastonce

()tocapturepartofthepattern

Anythingelseintheurldefinitionwillbetakenliterally.

Nowimagineyouhaveawebsitewiththeaddresslikethat:http://www.mysite.com/post/12345/,where12345isthenumberofyourpost.

Writingseparateviewsforallthepostnumberswouldbereallyannoying.Withregularexpressionwecancreateapatternthatwillmatchtheurlandextractthenumberforus: ̂ post/(\d+)/$.Let'sbreakitdownpiecebypiecetoseewhatwearedoinghere:

^post/istellingDjangototakeanythingthathaspost/atthebeginningoftheurl(rightafter ̂ )(\d+)meansthattherewillbeanumber(oneormoredigits)andthatwewantthenumbercapturedandextracted/tellsdjangothatanother/charactershouldfollow$thenindicatestheendoftheURLmeaningthatonlystringsendingwiththe/willmatchthispattern

YourfirstDjangourl!TimetocreateourfirstURL!Wewant'http://127.0.0.1:8000/'tobeahomepageofourbloganddisplayalistofposts.

Wealsowanttokeepthemysite/urls.pyfileclean,sowewillimporturlsfromourblogapplicationtothemainmysite/urls.pyfile.

Goahead,addalinethatwillimportblog.urlsintothemainurl('').Notethatweareusingtheincludefunctionheresoyouwillneedtoaddthattotheimportonthefirstlineofthefile.

Yourmysite/urls.pyfileshouldnowlooklikethis:

mysite/urls.py

fromdjango.conf.urlsimportinclude,url

fromdjango.contribimportadmin

urlpatterns=[

url(r'^admin/',admin.site.urls),

url(r'',include('blog.urls')),

]

Djangowillnowredirecteverythingthatcomesinto'http://127.0.0.1:8000/'toblog.urlsandlookforfurtherinstructionsthere.

WhenwritingregularexpressionsinPythonitisalwaysdonewithrinfrontofthestring.ThisisahelpfulhintforPythonthatthestringmaycontainspecialcharactersthatarenotmeantforPythonitself,butfortheregularexpressioninstead.

blog.urls

Djangourls

74

Createanewblog/urls.pyemptyfile.Allright!Addthesetwofirstlines:

blog/urls.py

fromdjango.conf.urlsimporturl

from.importviews

Herewe'reimportingDjango'sfunctionurlandallofourviewsfromblogapplication(wedon'thaveanyyet,butwewillgettothatinaminute!)

Afterthat,wecanaddourfirstURLpattern:

blog/urls.py

urlpatterns=[

url(r'^$',views.post_list,name='post_list'),

]

Asyoucansee,we'renowassigningaviewcalledpost_listto ̂ $URL.Thisregularexpressionwillmatch ̂ (abeginning)followedby$(anend)-soonlyanemptystringwillmatch.That'scorrect,becauseinDjangoURLresolvers,'http://127.0.0.1:8000/'isnotapartoftheURL.ThispatternwilltellDjangothatviews.post_lististherightplacetogoifsomeoneentersyourwebsiteatthe'http://127.0.0.1:8000/'address.

Thelastpartname='post_list'isthenameoftheURLthatwillbeusedtoidentifytheview.Thiscanbethesameasthenameoftheviewbutitcanalsobesomethingcompletelydifferent.WewillbeusingthenamedURLslaterintheprojectsoitisimportanttonameeachURLintheapp.WeshouldalsotrytokeepthenamesofURLsuniqueandeasytoremember.

Ifyoutrytovisithttp://127.0.0.1:8000/now,thenyou'llfindsomesortof'webpagenotavailable'message.Thisisbecausetheserver(remembertypingrunserver?)isnolongerrunning.Takealookatyourserverconsolewindowtofindoutwhy.

Yourconsoleisshowinganerrorbutdon'tworry—they'reactuallyprettyuseful:

It'stellingyouthatthereisnoattribute'post_list'.That'sthenameoftheviewthatDjangoistryingtofindanduse,butwehaven'tcreatedityet.Atthisstageyour/admin/willalsonotwork.Noworries,wewillgetthere.

IfyouwanttoknowmoreaboutDjangoURLconfs,lookattheofficialdocumentation:https://docs.djangoproject.com/en/1.9/topics/http/urls/

Djangourls

75

Djangoviews-timetocreate!Timetogetridofthebugwecreatedinthelastchapter:)

Aviewisaplacewhereweputthe"logic"ofourapplication.Itwillrequestinformationfromthemodelyoucreatedbeforeandpassittoatemplate.We'llcreateatemplateinthenextchapter.ViewsarejustPythonfunctionsthatarealittlebitmorecomplicatedthantheoneswewroteintheIntroductiontoPythonchapter.

Viewsareplacedintheviews.pyfile.Wewilladdourviewstotheblog/views.pyfile.

blog/views.pyOK,let'sopenupthisfileandseewhat'sinthere:

blog/views.py

fromdjango.shortcutsimportrender

#Createyourviewshere.

Nottoomuchstuffhereyet.

Rememberthatlinesstartingwith#arecommentsandthoselineswon'tberunbyPython.

Thesimplestviewcanlooklikethis.

blog/views.py

defpost_list(request):

returnrender(request,'blog/post_list.html',{})

Asyoucansee,wecreatedafunction(def)calledpost_listthattakesrequestandreturnafunctionrenderthatwillrender(puttogether)ourtemplateblog/post_list.html.

Savethefile,gotohttp://127.0.0.1:8000/andseewhatwehavegot.

Anothererror!Readwhat'sgoingonnow:

Thisshowsthattheserverisrunningagain,atleast,butitstilldoesn'tlookright,doesit?Don'tworry,it'sjustanerrorpage,nothingtobescaredof!Justliketheerrormessagesintheconsole,theseareactuallyprettyuseful.YoucanreadthattheTemplateDoesNotExist.Let'sfixthisbugandcreateatemplateinthenextchapter!

Djangoviews-timetocreate!

76

LearnmoreaboutDjangoviewsbyreadingtheofficialdocumentation:https://docs.djangoproject.com/en/1.9/topics/http/views/

Djangoviews-timetocreate!

77

IntroductiontoHTMLWhat'satemplate,youmayask?

Atemplateisafilethatwecanre-usetopresentdifferentinformationinaconsistentformat-forexample,youcoulduseatemplatetohelpyouwritealetter,becausealthougheachlettermightcontainadifferentmessageandbeaddressedtoadifferentperson,theywillsharethesameformat.

ADjangotemplate'sformatisdescribedinalanguagecalledHTML(that'stheHTMLwementionedinthefirstchapterHowtheInternetworks).

WhatisHTML?HTMLisasimplecodethatisinterpretedbyyourwebbrowser-suchasChrome,FirefoxorSafari-todisplayawebpagefortheuser.

HTMLstandsfor"HyperTextMarkupLanguage".HyperTextmeansit'satypeoftextthatsupportshyperlinksbetweenpages.Markupmeanswehavetakenadocumentandmarkeditupwithcodetotellsomething(inthiscase,abrowser)howtointerpretthepage.HTMLcodeisbuiltwithtags,eachonestartingwith<andendingwith>.Thesetagsrepresentmarkupelements.

Yourfirsttemplate!Creatingatemplatemeanscreatingatemplatefile.Everythingisafile,right?Youhaveprobablynoticedthisalready.

Templatesaresavedinblog/templates/blogdirectory.Sofirstcreateadirectorycalledtemplatesinsideyourblogdirectory.Thencreateanotherdirectorycalledbloginsideyourtemplatesdirectory:

blog

└───templates

└───blog

(Youmightwonderwhyweneedtwodirectoriesbothcalledblog-asyouwilldiscoverlater,thisissimplyausefulnamingconventionthatmakeslifeeasierwhenthingsstarttogetmorecomplicated.)

Andnowcreateapost_list.htmlfile(justleaveitblankfornow)insidetheblog/templates/blogdirectory.

Seehowyourwebsitelooksnow:http://127.0.0.1:8000/

IfyoustillhaveanerrorTemplateDoesNotExist,trytorestartyourserver.Gointocommandline,stoptheserverbypressingCtrl+C(ControlandCbuttonstogether)andstartitagainbyrunningapythonmanage.pyrunservercommand.

IntroductiontoHTML

78

Noerroranymore!Congratulations:)However,yourwebsiteisn'tactuallypublishinganythingexceptanemptypage,becauseyourtemplateisemptytoo.Weneedtofixthat.

Addthefollowingtoyourtemplatefile:

blog/templates/blog/post_list.html

<html>

<p>Hithere!</p>

<p>Itworks!</p>

</html>

Sohowdoesyourwebsitelooknow?Clicktofindout:http://127.0.0.1:8000/

Itworked!Niceworkthere:)

Themostbasictag,<html>,isalwaysthebeginningofanywebpageand</html>isalwaystheend.Asyoucansee,thewholecontentofthewebsitegoesbetweenthebeginningtag<html>andclosingtag</html><p>isatagforparagraphelements;</p>closeseachparagraph

Head&bodyEachHTMLpageisalsodividedintotwoelements:headandbody.

headisanelementthatcontainsinformationaboutthedocumentthatisnotdisplayedonthescreen.

bodyisanelementthatcontainseverythingelsethatisdisplayedaspartofthewebpage.

Weuse<head>totellthebrowserabouttheconfigurationofthepage,and<body>totellitwhat'sactuallyonthepage.

Forexample,youcanputawebpagetitleelementinsidethe<head>,likethis:

blog/templates/blog/post_list.html

IntroductiontoHTML

79

<html>

<head>

<title>Ola'sblog</title>

</head>

<body>

<p>Hithere!</p>

<p>Itworks!</p>

</body>

</html>

Savethefileandrefreshyourpage.

Noticehowthebrowserhasunderstoodthat"Ola'sblog"isthetitleofyourpage?Ithasinterpreted<title>Ola'sblog</title>andplacedthetextinthetitlebarofyourbrowser(itwillalsobeusedforbookmarksandsoon).

Probablyyouhavealsonoticedthateachopeningtagismatchedbyaclosingtag,witha/,andthatelementsarenested(i.e.youcan'tcloseaparticulartaguntilalltheonesthatwereinsideithavebeenclosedtoo).

It'slikeputtingthingsintoboxes.Youhaveonebigbox,<html></html>;insideitthereis<body></body>,andthatcontainsstillsmallerboxes:<p></p>.

Youneedtofollowtheserulesofclosingtags,andofnestingelements-ifyoudon't,thebrowsermaynotbeabletointerpretthemproperlyandyourpagewilldisplayincorrectly.

CustomizeyourtemplateYoucannowhavealittlefunandtrytocustomizeyourtemplate!Hereareafewusefultagsforthat:

<h1>Aheading</h1>-foryourmostimportantheading<h2>Asub-heading</h2>foraheadingatthenextlevel<h3>Asub-sub-heading</h3>...andsoon,upto<h6><p>Aparagraphoftext</p>

<em>text</em>emphasizesyourtext<strong>text</strong>stronglyemphasizesyourtext<br/>goestoanotherline(youcan'tputanythinginsidebr)<ahref="https://djangogirls.org">link</a>createsalink<ul><li>firstitem</li><li>seconditem</li></ul>makesalist,justlikethisone!<div></div>definesasectionofthepage

Here'sanexampleofafulltemplate:

blog/templates/blog/post_list.html

IntroductiontoHTML

80

<html>

<head>

<title>DjangoGirlsblog</title>

</head>

<body>

<div>

<h1><ahref="">DjangoGirlsBlog</a></h1>

</div>

<div>

<p>published:14.06.2014,12:14</p>

<h2><ahref="">Myfirstpost</a></h2>

<p>Aeneaneuleoquam.Pellentesqueornaresemlaciniaquamvenenatisvestibulum.Donecidelitnonmipo

rtagravidaategetmetus.Fuscedapibus,tellusaccursuscommodo,tortormauriscondimentumnibh,utfermentummass

ajustositametrisus.</p>

</div>

<div>

<p>published:14.06.2014,12:14</p>

<h2><ahref="">Mysecondpost</a></h2>

<p>Aeneaneuleoquam.Pellentesqueornaresemlaciniaquamvenenatisvestibulum.Donecidelitnonmipo

rtagravidaategetmetus.Fuscedapibus,tellusaccursuscommodo,tortormauriscondimentumnibh,utf.</p>

</div>

</body>

</html>

We'vecreatedthreedivsectionshere.

Thefirstdivelementcontainsthetitleofourblog-it'saheadingandalinkAnothertwodivelementscontainourblogpostswithapublisheddate,h2withaposttitlethatisclickableandtwops(paragraph)oftext,oneforthedateandoneforourblogpost.

Itgivesusthiseffect:

Yaaay!Butsofar,ourtemplateonlyeverdisplaysexactlythesameinformation-whereasearlierweweretalkingabouttemplatesasallowingustodisplaydifferentinformationinthesameformat.

IntroductiontoHTML

81

WhatwereallywanttodoisdisplayrealpostsaddedinourDjangoadmin-andthat'swherewe'regoingnext.

Onemorething:deploy!It'dbegoodtoseeallthisoutandliveontheInternet,right?Let'sdoanotherPythonAnywheredeploy:

Commit,andpushyourcodeuptoGithub

Firstoff,let'sseewhatfileshavechangedsincewelastdeployed(runthesecommandslocally,notonPythonAnywhere):

command-line

$gitstatus

Makesureyou'reinthedjangogirlsdirectoryandlet'stellgittoincludeallthechangeswithinthisdirectory:

command-line

$gitadd--all.

--allmeansthatgitwillalsorecognizeifyou'vedeletedfiles(bydefault,itonlyrecognizesnew/modifiedfiles).Alsoremember(fromchapter3)that.meansthecurrentdirectory.

Beforeweuploadallthefiles,let'scheckwhatgitwillbeuploading(allthefilesthatgitwilluploadshouldnowappearingreen):

command-line

$gitstatus

We'realmostthere,nowit'stimetotellittosavethischangeinitshistory.We'regoingtogiveita"commitmessage"wherewedescribewhatwe'vechanged.Youcantypeanythingyou'dlikeatthisstage,butit'shelpfultotypesomethingdescriptivesothatyoucanrememberwhatyou'vedoneinthefuture.

command-line

$gitcommit-m"ChangedtheHTMLforthesite."

Makesureyouusedoublequotesaroundthecommitmessage.

Oncewe'vedonethat,weupload(push)ourchangesuptoGithub:

command-line

$gitpush

PullyournewcodedowntoPythonAnywhere,andreloadyourwebapp

OpenupthePythonAnywhereconsolespageandgotoyourBashconsole(orstartanewone).Then,run:

command-line

$cd~/my-first-blog

$gitpull

[...]

IntroductiontoHTML

82

Andwatchyourcodegetdownloaded.Ifyouwanttocheckthatit'sarrived,youcanhopovertotheFilestabandviewyourcodeonPythonAnywhere.

Finally,hoponovertotheWebtabandhitReloadonyourwebapp.

Yourupdateshouldbelive!Goaheadandrefreshyourwebsiteinthebrowser.Changesshouldbevisible:)

IntroductiontoHTML

83

DjangoORMandQuerySetsInthischapteryou'lllearnhowDjangoconnectstothedatabaseandstoresdatainit.Let'sdivein!

WhatisaQuerySet?AQuerySetis,inessence,alistofobjectsofagivenModel.QuerySetallowsyoutoreadthedatafromthedatabase,filteritandorderit.

It'seasiesttolearnbyexample.Let'strythis,shallwe?

DjangoshellOpenupyourlocalconsole(notonPythonAnywhere)andtypethiscommand:

command-line

(myvenv)~/djangogirls$pythonmanage.pyshell

Theeffectshouldbelikethis:

command-line

(InteractiveConsole)

>>>

You'renowinDjango'sinteractiveconsole.It'sjustlikePythonpromptbutwithsomeadditionalDjangomagic:).YoucanuseallthePythoncommandsheretoo,ofcourse.

AllobjectsLet'strytodisplayallofourpostsfirst.Youcandothatwiththefollowingcommand:

command-line

>>>Post.objects.all()

Traceback(mostrecentcalllast):

File"<console>",line1,in<module>

NameError:name'Post'isnotdefined

Oops!Anerrorshowedup.IttellsusthatthereisnoPost.It'scorrect--weforgottoimportitfirst!

command-line

>>>fromblog.modelsimportPost

Thisissimple:weimportmodelPostfromblog.models.Let'strydisplayingallpostsagain:

command-line

>>>Post.objects.all()

[<Post:myposttitle>,<Post:anotherposttitle>]

DjangoORM(Querysets)

84

It'salistofthepostswecreatedearlier!WecreatedthesepostsusingtheDjangoadmininterface.But,nowwewanttocreatenewpostsusingPython,sohowdowedothat?

Createobject

ThisishowyoucreateanewPostobjectindatabase:

command-line

>>>Post.objects.create(author=me,title='Sampletitle',text='Test')

Butwehaveonemissingingredienthere:me.WeneedtopassaninstanceofUsermodelasanauthor.Howtodothat?

Let'simportUsermodelfirst:

command-line

>>>fromdjango.contrib.auth.modelsimportUser

Whatusersdowehaveinourdatabase?Trythis:

command-line

>>>User.objects.all()

[<User:ola>]

It'sthesuperuserwecreatedearlier!Let'sgetaninstanceoftheusernow:

command-line

me=User.objects.get(username='ola')

Asyoucansee,wenowgetaUserwithausernamethatequalsto'ola'.Neat!Ofcourse,youhavetoadjustittoyourusername.

Nowwecanfinallycreateourpost:

command-line

>>>Post.objects.create(author=me,title='Sampletitle',text='Test')

Hurray!Wannacheckifitworked?

command-line

>>>Post.objects.all()

[<Post:myposttitle>,<Post:anotherposttitle>,<Post:Sampletitle>]

Thereitis,onemorepostinthelist!

Addmoreposts

Youcannowhavealittlefunandaddmorepoststoseehowitworks.Add2-3moreandgoaheadtothenextpart.

Filterobjects

DjangoORM(Querysets)

85

AbigpartofQuerySetsisanabilitytofilterthem.Let'ssay,wewanttofindallpostsUserolaauthored.WewillusefilterinsteadofallinPost.objects.all().Inparentheseswewillstatewhatcondition(s)ablogpostneedstomeettoendupinourqueryset.Inoursituationitisauthorthatisequaltome.ThewaytowriteitinDjangois:author=me.Nowourpieceofcodelookslikethis:

command-line

>>>Post.objects.filter(author=me)

[<Post:Sampletitle>,<Post:Postnumber2>,<Post:My3rdpost!>,<Post:4thtitleofpost>]

Ormaybewewanttoseeallthepoststhatcontainaword'title'inthetitlefield?

command-line

>>>Post.objects.filter(title__contains='title')

[<Post:Sampletitle>,<Post:4thtitleofpost>]

Therearetwounderscorecharacters(_)betweentitleandcontains.Django'sORMusesthisruletoseparatefieldnames("title")andoperationsorfilters("contains").Ifyouonlyuseoneunderscore,you'llgetanerrorlike"FieldError:Cannotresolvekeywordtitle_contains".

Youcanalsogetalistofallpublishedposts.Wedoitbyfilteringallthepoststhathavepublished_datesetinthepast:

command-line

>>>fromdjango.utilsimporttimezone

>>>Post.objects.filter(published_date__lte=timezone.now())

[]

Unfortunately,thepostweaddedfromthePythonconsoleisnotpublishedyet.Wecanchangethat!Firstgetaninstanceofapostwewanttopublish:

command-line

>>>post=Post.objects.get(title="Sampletitle")

Andthenpublishitwithourpublishmethod!

command-line

>>>post.publish()

Nowtrytogetlistofpublishedpostsagain(presstheuparrowbutton3timesandhitenter):

command-line

>>>Post.objects.filter(published_date__lte=timezone.now())

[<Post:Sampletitle>]

Orderingobjects

QuerySetsalsoallowyoutoorderthelistofobjects.Let'strytoorderthembycreated_datefield:

command-line

>>>Post.objects.order_by('created_date')

[<Post:Sampletitle>,<Post:Postnumber2>,<Post:My3rdpost!>,<Post:4thtitleofpost>]

DjangoORM(Querysets)

86

Wecanalsoreversetheorderingbyadding-atthebeginning:

command-line

>>>Post.objects.order_by('-created_date')

[<Post:4thtitleofpost>,<Post:My3rdpost!>,<Post:Postnumber2>,<Post:Sampletitle>]

ChainingQuerySets

YoucanalsocombineQuerySetsbychainingthemtogether:

>>>Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')

Thisisreallypowerfulandletsyouwritequitecomplexqueries.

Cool!You'renowreadyforthenextpart!Toclosetheshell,typethis:

command-line

>>>exit()

$

DjangoORM(Querysets)

87

DynamicdataintemplatesWehavedifferentpiecesinplace:thePostmodelisdefinedinmodels.py,wehavepost_listinviews.pyandthetemplateadded.ButhowwillweactuallymakeourpostsappearinourHTMLtemplate?Becausethatiswhatwewanttodo.Takesomecontent(modelssavedinthedatabase)anddisplayitnicelyinourtemplate,right?

Thisisexactlywhatviewsaresupposedtodo:connectmodelsandtemplates.Inourpost_listviewwewillneedtotakemodelswewanttodisplayandpassthemtothetemplate.Inaviewwedecidewhat(model)willbedisplayedinatemplate.

OK,sohowwillweachieveit?

Weneedtoopenourblog/views.py.Sofarpost_listviewlookslikethis:

blog/views.py

fromdjango.shortcutsimportrender

defpost_list(request):

returnrender(request,'blog/post_list.html',{})

Rememberwhenwetalkedaboutincludingcodewrittenindifferentfiles?Nowitisthemomentwhenwehavetoincludethemodelwehavewritteninmodels.py.Wewilladdthislinefrom.modelsimportPostlikethis:

blog/views.py

fromdjango.shortcutsimportrender

from.modelsimportPost

Thedotbeforemodelsmeanscurrentdirectoryorcurrentapplication.Bothviews.pyandmodels.pyareinthesamedirectory.Thismeanswecanuse.andthenameofthefile(without.py).Thenweimportthenameofthemodel(Post).

Butwhat'snext?TotakeactualblogpostsfromPostmodelweneedsomethingcalledQuerySet.

QuerySetYoushouldalreadybefamiliarwithhowQuerySetswork.WetalkedaboutitinDjangoORM(QuerySets)chapter.

Sonowwewantpublishedblogpostssortedbypublished_date,right?WealreadydidthatinQuerySetschapter!

blog/views.py

Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')

Nowweputthispieceofcodeinsidetheblog/views.pyfilebyaddingittothefunctiondefpost_list(request):

blog/views.py

fromdjango.shortcutsimportrender

fromdjango.utilsimporttimezone

from.modelsimportPost

defpost_list(request):

posts=Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')

returnrender(request,'blog/post_list.html',{})

Dynamicdataintemplates

88

PleasenotethatwecreateavariableforourQuerySet:posts.TreatthisasthenameofourQuerySet.Fromnowonwecanrefertoitbythisname.

Also,thecodeusesthetimezone.now()function,soweneedtoaddanimportfortimezone.

ThelastmissingpartispassingthepostsQuerySettothetemplate.Don'tworrywewillcoverhowtodisplayitinanextchapter.

Intherenderfunctionwealreadyhaveparameterwithrequest(soeverythingwereceivefromtheuserviatheInternet)andatemplatefile'blog/post_list.html'.Thelastparameter,whichlookslikethis:{}isaplaceinwhichwecanaddsomethingsforthetemplatetouse.Weneedtogivethemnames(wewillstickto'posts'rightnow:)).Itshouldlooklikethis:{'posts':posts}.Pleasenotethatthepartbefore:isastring;youneedtowrapitwithquotes''.

Sofinallyourblog/views.pyfileshouldlooklikethis:

blog/views.py

fromdjango.shortcutsimportrender

fromdjango.utilsimporttimezone

from.modelsimportPost

defpost_list(request):

posts=Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')

returnrender(request,'blog/post_list.html',{'posts':posts})

That'sit!TimetogobacktoourtemplateanddisplaythisQuerySet!

WanttoreadalittlebitmoreaboutQuerySetsinDjango?Youshouldlookhere:https://docs.djangoproject.com/en/1.9/ref/models/querysets/

Dynamicdataintemplates

89

DjangotemplatesTimetodisplaysomedata!Djangogivesussomehelpfulbuilt-intemplatetagsforthat.

Whataretemplatetags?Yousee,inHTML,youcan'treallywritePythoncode,becausebrowsersdon'tunderstandit.TheyonlyknowHTML.WeknowthatHTMLisratherstatic,whilePythonismuchmoredynamic.

DjangotemplatetagsallowustotransferPython-likethingsintoHTML,soyoucanbuilddynamicwebsitesfasterandeasier.Yikes!

DisplaypostlisttemplateInthepreviouschapterwegaveourtemplatealistofpostsinthepostsvariable.NowwewilldisplayitinHTML.

ToprintavariableinDjangotemplates,weusedoublecurlybracketswiththevariable'snameinside,likethis:

blog/templates/blog/post_list.html

{{posts}}

Trythisinyourblog/templates/blog/post_list.htmltemplate.Replaceeverythingfromthesecond<div>tothethird</div>with{{posts}}.Savethefile,andrefreshthepagetoseetheresults:

Asyoucansee,allwe'vegotisthis:

blog/templates/blog/post_list.html

[<Post:Mysecondpost>,<Post:Myfirstpost>]

ThismeansthatDjangounderstandsitasalistofobjects.RememberfromIntroductiontoPythonhowwecandisplaylists?Yes,withforloops!InaDjangotemplateyoudothemlikethis:

blog/templates/blog/post_list.html

{%forpostinposts%}

{{post}}

{%endfor%}

Trythisinyourtemplate.

Djangotemplates

90

Itworks!ButwewantthemtobedisplayedlikethestaticpostswecreatedearlierintheIntroductiontoHTMLchapter.YoucanmixHTMLandtemplatetags.Ourbodywilllooklikethis:

blog/templates/blog/post_list.html

<div>

<h1><ahref="/">DjangoGirlsBlog</a></h1>

</div>

{%forpostinposts%}

<div>

<p>published:{{post.published_date}}</p>

<h1><ahref="">{{post.title}}</a></h1>

<p>{{post.text|linebreaksbr}}</p>

</div>

{%endfor%}

Everythingyouputbetween{%for%}and{%endfor%}willberepeatedforeachobjectinthelist.Refreshyourpage:

Haveyounoticedthatweusedaslightlydifferentnotationthistime{{post.title}}or{{post.text}}?WeareaccessingdataineachofthefieldsdefinedinourPostmodel.Alsothe|linebreaksbrispipingtheposts'textthroughafiltertoconvertline-breaksintoparagraphs.

Djangotemplates

91

OnemorethingIt'dbegoodtoseeifyourwebsitewillstillbeworkingonthepublicInternet,right?Let'strydeployingtoPythonAnywhereagain.Here'sarecapofthesteps...

First,pushyourcodetoGithub

command-line

$gitstatus

[...]

$gitadd--all.

$gitstatus

[...]

$gitcommit-m"Modifiedtemplatestodisplaypostsfromdatabase."

[...]

$gitpush

Then,logbackintoPythonAnywhereandgotoyourBashconsole(orstartanewone),andrun:

command-line

$cdmy-first-blog

$gitpull

[...]

Finally,hoponovertotheWebtabandhitReloadonyourwebapp.Yourupdateshouldbelive!IftheblogpostsonyourPythonAnywheresitedon'tmatchthepostsappearingonthebloghostedonyourlocalserver-that'sOK.ThedatabasesonyourlocalcomputerandPythonAnywheredon'tsyncwiththerestofyourfiles.

Congrats!NowgoaheadandtryaddinganewpostinyourDjangoadmin(remembertoaddpublished_date!)MakesureyouareintheDjangoadminforyourpythonanywheresite,https://yourname.pythonanywhere.com/admin.Thenrefreshyourpagetoseeifthepostappearsthere.

Workslikeacharm?We'reproud!Stepawayfromyourcomputerforabit,youhaveearnedabreak.:)

Djangotemplates

92

CSS-makeitpretty!Ourblogstilllooksprettyugly,right?Timetomakeitnice!WewilluseCSSforthat.

WhatisCSS?CascadingStyleSheets(CSS)isalanguageusedfordescribingthelookandformattingofawebsitewritteninmarkuplanguage(likeHTML).Treatitasmake-upforourwebpage;).

Butwedon'twanttostartfromscratchagain,right?Oncemore,we'llusesomethingthatprogrammersreleasedontheInternetforfree.Youknow,reinventingthewheelisnofun.

Let'suseBootstrap!BootstrapisoneofthemostpopularHTMLandCSSframeworksfordevelopingbeautifulwebsites:https://getbootstrap.com/

ItwaswrittenbyprogrammerswhoworkedforTwitter.Nowit'sdevelopedbyvolunteersfromallovertheworld!

InstallBootstrapToinstallBootstrap,youneedtoaddthistoyour<head>inyour.htmlfile:

blog/templates/blog/post_list.html

<linkrel="stylesheet"href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">

<linkrel="stylesheet"href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">

Thisdoesn'taddanyfilestoyourproject.Itjustpointstofilesthatexistontheinternet.Justgoahead,openyourwebsiteandrefreshthepage.Hereitis!

Lookingniceralready!

StaticfilesinDjango

CSS-makeitpretty

93

Finallywewilltakeacloserlookatthesethingswe'vebeencallingstaticfiles.StaticfilesareallyourCSSandimages.Theircontentdoesn'tdependontherequestcontextandwillbethesameforeveryuser.

WheretoputstaticfilesforDjango

Djangoalreadyknowswheretofindthestaticfilesforthebuilt-in"admin"app.Nowwejustneedtoaddsomestaticfilesforourownapp,blog.

Wedothatbycreatingafoldercalledstaticinsidetheblogapp:

djangogirls

├──blog

│├──migrations

│└──static

└──mysite

Djangowillautomaticallyfindanyfolderscalled"static"insideanyofyourapps'folders.Then,itwillbeabletousetheircontentsasstaticfiles.

YourfirstCSSfile!Let'screateaCSSfilenow,toaddyourownstyletoyourweb-page.Createanewdirectorycalledcssinsideyourstaticdirectory.Thencreateanewfilecalledblog.cssinsidethiscssdirectory.Ready?

djangogirls

└───blog

└───static

└───css

└───blog.css

TimetowritesomeCSS!Openuptheblog/static/css/blog.cssfileinyourcodeeditor.

Wewon'tbegoingtoodeepintocustomizingandlearningaboutCSShere.It'sprettyeasyandyoucanlearnitonyourownafterthisworkshop.Thereisarecommendationforafreecoursetolearnmoreattheendofthispage.

Butlet'sdoatleastalittle.Maybewecouldchangethecolorofourheader?Tounderstandcolors,computersusespecialcodes.Thesecodesstartwith#followedby6letters(A-F)andnumbers(0-9).Forexample,thecodeforblueis#0000FF.Youcanfindthecolorcodesformanycolorshere:http://www.colorpicker.com/.Youmayalsousepredefinedcolors,suchasredandgreen.

Inyourblog/static/css/blog.cssfileyoushouldaddthefollowingcode:

blog/static/css/blog.css

h1a{

color:#FCA205;

}

h1aisaCSSSelector.Thismeanswe'reapplyingourstylestoanyaelementinsideofanh1element.Sowhenwehavesomethinglike:<h1><ahref="">link</a></h1>theh1astylewillapply.Inthiscase,we'retellingittochangeitscolorto#FCA205,whichisorange.Ofcourse,youcanputyourowncolorhere!

InaCSSfilewedeterminestylesforelementsintheHTMLfile.Thefirstwayweidentifyelementsiswiththeelementname.YoumightremembertheseastagsfromtheHTMLsection.Thingslikea,h1,andbodyareallexamplesofelementnames.Wealsoidentifyelementsbytheattributeclassortheattributeid.Classandidarenamesyougivetheelementbyyourself.Classesdefinegroupsofelements,andidspointtospecificelements.Forexample,youcouldidentifythefollowingtagbyusingthetagnamea,theclassexternal_link,ortheidlink_to_wiki_page:

CSS-makeitpretty

94

<ahref="https://en.wikipedia.org/wiki/Django"class="external_link"id="link_to_wiki_page">

ReadaboutCSSSelectorsinw3schools.

Then,weneedtoalsotellourHTMLtemplatethatweaddedsomeCSS.Opentheblog/templates/blog/post_list.htmlfileandaddthislineattheverybeginningofit:

blog/templates/blog/post_list.html

{%loadstaticfiles%}

We'rejustloadingstaticfileshere:).Betweenthe<head>and</head>,afterthelinkstotheBootstrapCSSfilesaddthisline:

blog/templates/blog/post_list.html

<linkrel="stylesheet"href="{%static'css/blog.css'%}">

Thebrowserreadsthefilesintheorderthey'regiven,soweneedtomakesurethisisintherightplace.OtherwisethecodeinourfilemayoverridecodeinBootstrapfiles.WejusttoldourtemplatewhereourCSSfileislocated.

Yourfileshouldnowlooklikethis:

blog/templates/blog/post_list.html

{%loadstaticfiles%}

<html>

<head>

<title>DjangoGirlsblog</title>

<linkrel="stylesheet"href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">

<linkrel="stylesheet"href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">

<linkrel="stylesheet"href="{%static'css/blog.css'%}">

</head>

<body>

<div>

<h1><ahref="/">DjangoGirlsBlog</a></h1>

</div>

{%forpostinposts%}

<div>

<p>published:{{post.published_date}}</p>

<h1><ahref="">{{post.title}}</a></h1>

<p>{{post.text|linebreaksbr}}</p>

</div>

{%endfor%}

</body>

</html>

OK,savethefileandrefreshthesite!

CSS-makeitpretty

95

Nicework!Maybewewouldalsoliketogiveourwebsitealittleairandincreasethemarginontheleftside?Let'strythis!

blog/static/css/blog.css

body{

padding-left:15px;

}

AddthistoyourCSS,savethefileandseehowitworks!

Maybewecancustomizethefontinourheader?Pastethisintoyour<head>inblog/templates/blog/post_list.htmlfile:

blog/templates/blog/post_list.html

<linkhref="//fonts.googleapis.com/css?family=Lobster&subset=latin,latin-ext"rel="stylesheet"type="text/css">

Asbefore,checktheorderandplacebeforethelinktoblog/static/css/blog.css.ThislinewillimportafontcalledLobsterfromGoogleFonts(https://www.google.com/fonts).

Findtheh1adeclarationblock(thecodebetweenbraces{and})intheCSSfileblog/static/css/blog.css.Nowaddthelinefont-family:'Lobster';betweenthebraces,andrefreshthepage:

blog/static/css/blog.css

CSS-makeitpretty

96

h1a{

color:#FCA205;

font-family:'Lobster';

}

Great!

Asmentionedabove,CSShasaconceptofclasses.TheseallowyoutonameapartoftheHTMLcodeandapplystylesonlytothispart,withoutaffectingotherparts.Thiscanbesuperhelpful!Maybeyouhavetwodivsthataredoingsomethingdifferent(likeyourheaderandyourpost).Aclasscanhelpyoumakethemlookdifferent.

GoaheadandnamesomepartsoftheHTMLcode.Addaclasscalledpage-headertoyourdivthatcontainsyourheader,likethis:

blog/templates/blog/post_list.html

<divclass="page-header">

<h1><ahref="/">DjangoGirlsBlog</a></h1>

</div>

Andnowaddaclassposttoyourdivcontainingablogpost.

blog/templates/blog/post_list.html

<divclass="post">

<p>published:{{post.published_date}}</p>

<h1><ahref="">{{post.title}}</a></h1>

<p>{{post.text|linebreaksbr}}</p>

</div>

Wewillnowadddeclarationblockstodifferentselectors.Selectorsstartingwith.relatetoclasses.TherearemanygreattutorialsandexplanationsaboutCSSontheWebtohelpyouunderstandthefollowingcode.Fornow,justcopyandpasteitintoyourblog/static/css/blog.cssfile:

blog/static/css/blog.css

CSS-makeitpretty

97

.page-header{

background-color:#ff9400;

margin-top:0;

padding:20px20px20px40px;

}

.page-headerh1,.page-headerh1a,.page-headerh1a:visited,.page-headerh1a:active{

color:#ffffff;

font-size:36pt;

text-decoration:none;

}

.content{

margin-left:40px;

}

h1,h2,h3,h4{

font-family:'Lobster',cursive;

}

.date{

color:#828282;

}

.save{

float:right;

}

.post-formtextarea,.post-forminput{

width:100%;

}

.top-menu,.top-menu:hover,.top-menu:visited{

color:#ffffff;

float:right;

font-size:26pt;

margin-right:20px;

}

.post{

margin-bottom:70px;

}

.posth1a,.posth1a:visited{

color:#000000;

}

ThensurroundtheHTMLcodewhichdisplaysthepostswithdeclarationsofclasses.Replacethis:

blog/templates/blog/post_list.html

{%forpostinposts%}

<divclass="post">

<p>published:{{post.published_date}}</p>

<h1><ahref="">{{post.title}}</a></h1>

<p>{{post.text|linebreaksbr}}</p>

</div>

{%endfor%}

intheblog/templates/blog/post_list.htmlwiththis:

blog/templates/blog/post_list.html

CSS-makeitpretty

98

<divclass="contentcontainer">

<divclass="row">

<divclass="col-md-8">

{%forpostinposts%}

<divclass="post">

<divclass="date">

<p>published:{{post.published_date}}</p>

</div>

<h1><ahref="">{{post.title}}</a></h1>

<p>{{post.text|linebreaksbr}}</p>

</div>

{%endfor%}

</div>

</div>

</div>

Savethosefilesandrefreshyourwebsite.

Woohoo!Looksawesome,right?LookatthecodewejustpastedtofindtheplaceswhereweaddedclassesintheHTMLandusedthemintheCSS.Wherewouldyoumakethechangeifyouwantedthedatetobeturquoise?

Don'tbeafraidtotinkerwiththisCSSalittlebitandtrytochangesomethings.PlayingwiththeCSScanhelpyouunderstandwhatthedifferentthingsaredoing.Ifyoubreaksomething,don'tworry,youcanalwaysundoit!

WereallyrecommendtakingthisfreeonlineCodeacademyHTML&CSScourse.ItcanhelpyoulearnallaboutmakingyourwebsitesprettierwithCSS.

Readyforthenextchapter?!:)

CSS-makeitpretty

99

TemplateextendingAnothernicethingDjangohasforyouistemplateextending.Whatdoesthismean?ItmeansthatyoucanusethesamepartsofyourHTMLfordifferentpagesofyourwebsite.

Templateshelpwhenyouwanttousethesameinformation/layoutinmorethanoneplace.Youdon'thavetorepeatyourselfineveryfile.Andifyouwanttochangesomething,youdon'thavetodoitineverytemplate,justonce!

CreatebasetemplateAbasetemplateisthemostbasictemplatethatyouextendoneverypageofyourwebsite.

Let'screateabase.htmlfileinblog/templates/blog/:

blog

└───templates

└───blog

base.html

post_list.html

Thenopenitupandcopyeverythingfrompost_list.htmltobase.htmlfile,likethis:

blog/templates/blog/base.html

{%loadstaticfiles%}

<html>

<head>

<title>DjangoGirlsblog</title>

<linkrel="stylesheet"href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">

<linkrel="stylesheet"href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">

<linkhref='//fonts.googleapis.com/css?family=Lobster&subset=latin,latin-ext'rel='stylesheet'type='text/css'

>

<linkrel="stylesheet"href="{%static'css/blog.css'%}">

</head>

<body>

<divclass="page-header">

<h1><ahref="/">DjangoGirlsBlog</a></h1>

</div>

<divclass="contentcontainer">

<divclass="row">

<divclass="col-md-8">

{%forpostinposts%}

<divclass="post">

<divclass="date">

{{post.published_date}}

</div>

<h1><ahref="">{{post.title}}</a></h1>

<p>{{post.text|linebreaksbr}}</p>

</div>

{%endfor%}

</div>

</div>

</div>

</body>

</html>

Theninbase.html,replaceyourwhole<body>(everythingbetween<body>and</body>)withthis:

blog/templates/blog/base.html

Templateextending

100

<body>

<divclass="page-header">

<h1><ahref="/">DjangoGirlsBlog</a></h1>

</div>

<divclass="contentcontainer">

<divclass="row">

<divclass="col-md-8">

{%blockcontent%}

{%endblock%}

</div>

</div>

</div>

</body>

Youmightnoticethisreplacedeverythingfrom{%forpostinposts%}to{%endfor%}with:

blog/templates/blog/base.html

{%blockcontent%}

{%endblock%}

Butwhy?Youjustcreatedablock!Youusedthetemplatetag{%block%}tomakeanareathatwillhaveHTMLinsertedinit.ThatHTMLwillcomefromanothertemplatesthatextendsthistemplate(base.html).Wewillshowyouhowtodothisinamoment.

Nowsavebase.html,andopenyourblog/templates/blog/post_list.htmlagain.You'regoingtoremoveeverythingabove{%forpostinposts%}andbelow{%endfor%}.Whenyou'redonethefilewilllooklikethis:

blog/templates/blog/post_list.html

{%forpostinposts%}

<divclass="post">

<divclass="date">

{{post.published_date}}

</div>

<h1><ahref="">{{post.title}}</a></h1>

<p>{{post.text|linebreaksbr}}</p>

</div>

{%endfor%}

Wewanttousethisaspartofourtemplateforallthecontentblocks.Timetoaddblocktagstothisfile!

Youwantyourblocktagtomatchthetaginyourbase.htmlfile.Youalsowantittoincludeallthecodethatbelongsinyourcontentblocks.Todothat,puteverythingbetween{%blockcontent%}and{%endblockcontent%}.Likethis:

blog/templates/blog/post_list.html

{%blockcontent%}

{%forpostinposts%}

<divclass="post">

<divclass="date">

{{post.published_date}}

</div>

<h1><ahref="">{{post.title}}</a></h1>

<p>{{post.text|linebreaksbr}}</p>

</div>

{%endfor%}

{%endblock%}

Onlyonethingleft.Weneedtoconnectthesetwotemplatestogether.Thisiswhatextendingtemplatesisallabout!We'lldothisbyaddinganextendstagtothebeginningofthefile.Likethis:

blog/templates/blog/post_list.html

Templateextending

101

{%extends'blog/base.html'%}

{%blockcontent%}

{%forpostinposts%}

<divclass="post">

<divclass="date">

{{post.published_date}}

</div>

<h1><ahref="">{{post.title}}</a></h1>

<p>{{post.text|linebreaksbr}}</p>

</div>

{%endfor%}

{%endblock%}

That'sit!Checkifyourwebsiteisstillworkingproperly:)

IfyouhaveanerrorTemplateDoesNotExistthatsaysthatthereisnoblog/base.htmlfileandyouhaverunserverrunningintheconsole,trytostopit(bypressingCtrl+C-ControlandCbuttonstogether)andrestartitbyrunningapythonmanage.pyrunservercommand.

Templateextending

102

ExtendyourapplicationWe'vealreadycompletedallthedifferentstepsnecessaryforthecreationofourwebsite:weknowhowtowriteamodel,url,viewandtemplate.Wealsoknowhowtomakeourwebsitepretty.

Timetopractice!

Thefirstthingweneedinourblogis,obviously,apagetodisplayonepost,right?

WealreadyhaveaPostmodel,sowedon'tneedtoaddanythingtomodels.py.

Createatemplatelinktoapost'sdetailWewillstartwithaddingalinkinsideblog/templates/blog/post_list.htmlfile.Sofaritshouldlooklike:

blog/templates/blog/post_list.html

{%extends'blog/base.html'%}

{%blockcontent%}

{%forpostinposts%}

<divclass="post">

<divclass="date">

{{post.published_date}}

</div>

<h1><ahref="">{{post.title}}</a></h1>

<p>{{post.text|linebreaksbr}}</p>

</div>

{%endfor%}

{%endblockcontent%}

Wewanttohavealinkfromapost'stitleinthepostlisttothepost'sdetailpage.Let'schange<h1><ahref="">{{post.title}}</a></h1>sothatitlinkstothepost'sdetailpage:

blog/templates/blog/post_list.html

<h1><ahref="{%url'post_detail'pk=post.pk%}">{{post.title}}</a></h1>

Timetoexplainthemysterious{%url'post_detail'pk=post.pk%}.Asyoumightsuspect,the{%%}notationmeansthatweareusingDjangotemplatetags.ThistimewewilluseonethatwillcreateaURLforus!

blog.views.post_detailisapathtoapost_detailviewwewanttocreate.Pleasenote:blogisthenameofourapplication(thedirectoryblog),viewsisfromthenameoftheviews.pyfileandthelastbit-post_detail-isthenameoftheview.

Nowwhenwegoto:http://127.0.0.1:8000/wewillhaveanerror(asexpected,sincewedon'thaveaURLoraviewforpost_detail).Itwilllooklikethis:

Extendyourapplication

103

CreateaURLtoapost'sdetailLet'screateaURLinurls.pyforourpost_detailview!

Wewantourfirstpost'sdetailtobedisplayedatthisURL:http://127.0.0.1:8000/post/1/

Let'smakeaURLintheblog/urls.pyfiletopointDjangotoaviewnamedpost_detail,thatwillshowanentireblogpost.Addthelineurl(r'^post/(?P<pk>\d+)/$',views.post_detail,name='post_detail'),totheblog/urls.pyfile.Thefileshouldlooklikethis:

blog/urls.py

fromdjango.conf.urlsimporturl

from.importviews

urlpatterns=[

url(r'^$',views.post_list,name='post_list'),

url(r'^post/(?P<pk>\d+)/$',views.post_detail,name='post_detail'),

]

Thispart ̂ post/(?P<pk>\d+)/$looksscary,butnoworries-wewillexplainitforyou:

itstartswith ̂ again--"thebeginning"post/onlymeansthatafterthebeginning,theURLshouldcontainthewordpostand/.Sofarsogood.(?P<pk>\d+)-thispartistrickier.ItmeansthatDjangowilltakeeverythingthatyouplacehereandtransferittoaviewasavariablecalledpk.\dalsotellsusthatitcanonlybeadigit,notaletter(soeverythingbetween0and9).+meansthatthereneedstobeoneormoredigitsthere.Sosomethinglikehttp://127.0.0.1:8000/post//isnotvalid,buthttp://127.0.0.1:8000/post/1234567890/isperfectlyok!/-thenweneed/again$-"theend"!

Thatmeansifyouenterhttp://127.0.0.1:8000/post/5/intoyourbrowser,Djangowillunderstandthatyouarelookingforaviewcalledpost_detailandtransfertheinformationthatpkequals5tothatview.

pkisshortcutforprimarykey.ThisnameisoftenusedinDjangoprojects.Butyoucannameyourvariableasyoulike(remember:lowercaseand_insteadofwhitespaces!).Forexampleinsteadof(?P<pk>\d+)wecouldhavevariablepost_id,sothisbitwouldlooklike:(?P<post_id>\d+).

Ok,we'veaddedanewURLpatterntoblog/urls.py!Let'srefreshthepage:http://127.0.0.1:8000/Boom!Theserverhasstoppedrunningagain.Havealookattheconsole-asexpected,there'syetanothererror!

Extendyourapplication

104

Doyourememberwhatthenextstepis?Ofcourse:addingaview!

Addapost'sdetailviewThistimeourviewisgivenanextraparameterpk.Ourviewneedstocatchit,right?Sowewilldefineourfunctionasdefpost_detail(request,pk):.Notethatweneedtouseexactlythesamenameastheonewespecifiedinurls(pk).Omittingthisvariableisincorrectandwillresultinanerror!

Now,wewanttogetoneandonlyoneblogpost.Todothiswecanusequerysetslikethis:

blog/views.py

Post.objects.get(pk=pk)

Butthiscodehasaproblem.IfthereisnoPostwithgivenprimarykey(pk)wewillhaveasuperuglyerror!

Wedon'twantthat!But,ofcourse,Djangocomeswithsomethingthatwillhandlethatforus:get_object_or_404.IncasethereisnoPostwiththegivenpkitwilldisplaymuchnicerpage(calledPageNotFound404page).

Extendyourapplication

105

ThegoodnewsisthatyoucanactuallycreateyourownPagenotfoundpageandmakeitasprettyasyouwant.Butit'snotsuperimportantrightnow,sowewillskipit.

Ok,timetoaddaviewtoourviews.pyfile!

Weshouldopenblog/views.pyandaddthefollowingcode:

blog/views.py

fromdjango.shortcutsimportrender,get_object_or_404

Nearotherfromlines.Andattheendofthefilewewilladdourview:

blog/views.py

defpost_detail(request,pk):

post=get_object_or_404(Post,pk=pk)

returnrender(request,'blog/post_detail.html',{'post':post})

Yes.Itistimetorefreshthepage:http://127.0.0.1:8000/

Extendyourapplication

106

Itworked!Butwhathappenswhenyouclickalinkinblogposttitle?

Ohno!Anothererror!Butwealreadyknowhowtodealwithit,right?Weneedtoaddatemplate!

CreateatemplateforpostdetailWewillcreateafileinblog/templates/blogcalledpost_detail.html.

Itwilllooklikethis:

blog/templates/blog/post_detail.html

Extendyourapplication

107

{%extends'blog/base.html'%}

{%blockcontent%}

<divclass="post">

{%ifpost.published_date%}

<divclass="date">

{{post.published_date}}

</div>

{%endif%}

<h1>{{post.title}}</h1>

<p>{{post.text|linebreaksbr}}</p>

</div>

{%endblock%}

Onceagainweareextendingbase.html.Inthecontentblockwewanttodisplayapost'spublished_date(ifitexists),titleandtext.Butweshoulddiscusssomeimportantthings,right?

{%if...%}...{%endif%}isatemplatetagwecanusewhenwewanttochecksomething(rememberif...else..fromIntroductiontoPythonchapter?).Inthisscenariowewanttocheckifapost'spublished_dateisnotempty.

Ok,wecanrefreshourpageandseeifTemplateDoesNotExistisgonenow.

Yay!Itworks!

Onemorething:deploytime!It'dbegoodtoseeifyourwebsitewillstillbeworkingonPythonAnywhere,right?Let'strydeployingagain.

command-line

Extendyourapplication

108

$gitstatus

$gitadd--all.

$gitstatus

$gitcommit-m"AddedviewandtemplatefordetailedblogpostaswellasCSSforthesite."

$gitpush

Then,inaPythonAnywhereBashconsole:

command-line

$cdmy-first-blog

$gitpull

[...]

Finally,hoponovertotheWebtabandhitReload.

Andthatshouldbeit!Congrats:)

Extendyourapplication

109

DjangoFormsThefinalthingwewanttodoonourwebsiteiscreateanicewaytoaddandeditblogposts.Django'sadminiscool,butitisratherhardtocustomizeandmakepretty.Withformswewillhaveabsolutepoweroverourinterface-wecandoalmostanythingwecanimagine!

ThenicethingaboutDjangoformsisthatwecaneitherdefineonefromscratchorcreateaModelFormwhichwillsavetheresultoftheformtothemodel.

Thisisexactlywhatwewanttodo:wewillcreateaformforourPostmodel.

LikeeveryimportantpartofDjango,formshavetheirownfile:forms.py.

Weneedtocreateafilewiththisnameintheblogdirectory.

blog

└──forms.py

Ok,let'sopenitandtypethefollowingcode:

blog/forms.py

fromdjangoimportforms

from.modelsimportPost

classPostForm(forms.ModelForm):

classMeta:

model=Post

fields=('title','text',)

WeneedtoimportDjangoformsfirst(fromdjangoimportforms)and,obviously,ourPostmodel(from.modelsimportPost).

PostForm,asyouprobablysuspect,isthenameofourform.WeneedtotellDjango,thatthisformisaModelForm(soDjangowilldosomemagicforus)-forms.ModelFormisresponsibleforthat.

Next,wehaveclassMeta,wherewetellDjangowhichmodelshouldbeusedtocreatethisform(model=Post).

Finally,wecansaywhichfield(s)shouldendupinourform.Inthisscenariowewantonlytitleandtexttobeexposed-authorshouldbethepersonwhoiscurrentlyloggedin(you!)andcreated_dateshouldbeautomaticallysetwhenwecreateapost(i.einthecode),right?

Andthat'sit!Allweneedtodonowisusetheforminaviewanddisplayitinatemplate.

Soonceagainwewillcreate:alinktothepage,aURL,aviewandatemplate.

LinktoapagewiththeformIt'stimetoopenblog/templates/blog/base.html.Wewilladdalinkindivnamedpage-header:

blog/templates/blog/base.html

<ahref="{%url'post_new'%}"class="top-menu"><spanclass="glyphiconglyphicon-plus"></span></a>

Notethatwewanttocallournewviewpost_new.Theclass"glyphiconglyphicon-plus"isprovidedbythebootstrapthemeweareusing,andwilldisplayaplussignforus.

DjangoForms

110

Afteraddingtheline,yourhtmlfileshouldnowlooklikethis:

blog/templates/blog/base.html

{%loadstaticfiles%}

<html>

<head>

<title>DjangoGirlsblog</title>

<linkrel="stylesheet"href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">

<linkrel="stylesheet"href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">

<linkhref='//fonts.googleapis.com/css?family=Lobster&subset=latin,latin-ext'rel='stylesheet'type='text/css'

>

<linkrel="stylesheet"href="{%static'css/blog.css'%}">

</head>

<body>

<divclass="page-header">

<ahref="{%url'post_new'%}"class="top-menu"><spanclass="glyphiconglyphicon-plus"></span></a>

<h1><ahref="/">DjangoGirlsBlog</a></h1>

</div>

<divclass="contentcontainer">

<divclass="row">

<divclass="col-md-8">

{%blockcontent%}

{%endblock%}

</div>

</div>

</div>

</body>

</html>

Aftersavingandrefreshingthepagehttp://127.0.0.1:8000youwillobviouslyseeafamiliarNoReverseMatcherror,right?

URLWeopenblog/urls.pyandaddaline:

blog/urls.py

url(r'^post/new/$',views.post_new,name='post_new'),

Andthefinalcodewilllooklikethis:

blog/urls.py

fromdjango.conf.urlsimporturl

from.importviews

urlpatterns=[

url(r'^$',views.post_list,name='post_list'),

url(r'^post/(?P<pk>\d+)/$',views.post_detail,name='post_detail'),

url(r'^post/new/$',views.post_new,name='post_new'),

]

Afterrefreshingthesite,weseeanAttributeError,sincewedon'thavepost_newviewimplemented.Let'sadditrightnow.

post_newviewTimetoopentheblog/views.pyfileandaddthefollowinglineswiththerestofthefromrows:

blog/views.py

DjangoForms

111

from.formsimportPostForm

andourview:

blog/views.py

defpost_new(request):

form=PostForm()

returnrender(request,'blog/post_edit.html',{'form':form})

TocreateanewPostform,weneedtocallPostForm()andpassittothetemplate.Wewillgobacktothisview,butfornow,let'screatequicklyatemplatefortheform.

TemplateWeneedtocreateafilepost_edit.htmlintheblog/templates/blogdirectory.Tomakeaformworkweneedseveralthings:

wehavetodisplaytheform.Wecandothatforexamplewithasimple{%raw%}{{form.as_p}}{%endraw%}.thelineaboveneedstobewrappedwithanHTMLformtag:<formmethod="POST">...</form>weneedaSavebutton.WedothatwithanHTMLbutton:<buttontype="submit">Save</button>andfinallyjustaftertheopening<form...>tagweneedtoadd{%raw%}{%csrf_token%}{%endraw%}.Thisisveryimportant,sinceitmakesyourformssecure!Djangowillcomplainifyouforgetaboutthisbitifyoutrytosavetheform:

Ok,solet'sseehowtheHTMLinpost_edit.htmlshouldlook:

blog/templates/blog/post_edit.html

{%extends'blog/base.html'%}

{%blockcontent%}

<h1>Newpost</h1>

<formmethod="POST"class="post-form">{%csrf_token%}

{{form.as_p}}

<buttontype="submit"class="savebtnbtn-default">Save</button>

</form>

{%endblock%}

Timetorefresh!Yay!Yourformisdisplayed!

DjangoForms

112

But,waitaminute!Whenyoutypesomethingintitleandtextfieldsandtrytosaveit-whatwillhappen?

Nothing!Weareonceagainonthesamepageandourtextisgone...andnonewpostisadded.Sowhatwentwrong?

Theansweris:nothing.Weneedtodoalittlebitmoreworkinourview.

SavingtheformOpenblog/views.pyonceagain.Currentlyallwehaveinthepost_newviewis:

blog/views.py

defpost_new(request):

form=PostForm()

returnrender(request,'blog/post_edit.html',{'form':form})

Whenwesubmittheform,wearebroughtbacktothesameview,butthistimewehavesomemoredatainrequest,morespecificallyinrequest.POST(thenaminghasnothingtodowithablog"post",it'stodowiththefactthatwe're"posting"data).RememberthatintheHTMLfileour<form>definitionhadthevariablemethod="POST"?Allthefieldsfromtheformarenowinrequest.POST.YoushouldnotrenamePOSTtoanythingelse(theonlyothervalidvalueformethodisGET,butwehavenotimetoexplainwhatthedifferenceis).

Soinourviewwehavetwoseparatesituationstohandle.First:whenweaccessthepageforthefirsttimeandwewantablankform.Second:whenwegobacktotheviewwithallform'sdatawejusttyped.Soweneedtoaddacondition(wewilluseifforthat).

blog/views.py

DjangoForms

113

ifrequest.method=="POST":

[...]

else:

form=PostForm()

It'stimetofillinthedots[...].IfmethodisPOSTthenwewanttoconstructthePostFormwithdatafromtheform,right?Wewilldothatwith:

blog/views.py

form=PostForm(request.POST)

Easy!Nextthingistocheckiftheformiscorrect(allrequiredfieldsaresetandnoincorrectvalueswillbesaved).Wedothatwithform.is_valid().

Wecheckiftheformisvalidandifso,wecansaveit!

blog/views.py

ifform.is_valid():

post=form.save(commit=False)

post.author=request.user

post.published_date=timezone.now()

post.save()

Basically,wehavetwothingshere:wesavetheformwithform.saveandweaddanauthor(sincetherewasnoauthorfieldinthePostFormandthisfieldisrequired!).commit=Falsemeansthatwedon'twanttosavePostmodelyet-wewanttoaddauthorfirst.Mostofthetimeyouwilluseform.save(),withoutcommit=False,butinthiscase,weneedtodothat.post.save()willpreservechanges(addingauthor)andanewblogpostiscreated!

Finally,itwouldbeawesomeifwecanimmediatelygotopost_detailpagefornewlycreatedblogpost,right?Todothatweneedonemoreimport:

blog/views.py

fromdjango.shortcutsimportredirect

Additattheverybeginningofyourfile.Andnowwecansay:gotopost_detailpageforanewlycreatedpost.

blog/views.py

returnredirect('post_detail',pk=post.pk)

post_detailisthenameoftheviewwewanttogoto.Rememberthatthisviewrequiresapkvariable?Topassittotheviewsweusepk=post.pk,wherepostisthenewlycreatedblogpost!

Ok,wetalkedalot,butweprobablywanttoseewhatthewholeviewlookslikenow,right?

blog/views.py

DjangoForms

114

defpost_new(request):

ifrequest.method=="POST":

form=PostForm(request.POST)

ifform.is_valid():

post=form.save(commit=False)

post.author=request.user

post.published_date=timezone.now()

post.save()

returnredirect('post_detail',pk=post.pk)

else:

form=PostForm()

returnrender(request,'blog/post_edit.html',{'form':form})

Let'sseeifitworks.Gotothepagehttp://127.0.0.1:8000/post/new/,addatitleandtext,saveit...andvoilà!Thenewblogpostisaddedandweareredirectedtopost_detailpage!

Youmighthavenoticedthatwearesettingpublishdatebeforesavingthepost.Lateron,wewillintroduceapublishbuttoninDjangoGirlsTutorial:Extensions.

Thatisawesome!

AswehaverecentlyusedtheDjangoadmininterfacethesystemcurrentlythinksweareloggedin.Thereareafewsituationsthatcouldleadtousbeingloggedout(closingthebrowser,restartingtheDBetc.).Ifyoufindthatyouaregettingerrorscreatingapostreferringtoalackofaloggedinuser,headtotheadminpagehttp://127.0.0.1:8000/adminandloginagain.Thiswillfixtheissuetemporarily.ThereisapermanentfixawaitingyouintheHomework:addsecuritytoyourwebsite!chapterafterthemaintutorial.

FormvalidationNow,wewillshowyouhowcoolDjangoformsare.Ablogpostneedstohavetitleandtextfields.InourPostmodelwedidnotsay(asopposedtopublished_date)thatthesefieldsarenotrequired,soDjango,bydefault,expectsthemtobeset.

Trytosavetheformwithouttitleandtext.Guess,whatwillhappen!

DjangoForms

115

Djangoistakingcareofvalidatingthatallthefieldsinourformarecorrect.Isn'titawesome?

EditformNowweknowhowtoaddanewform.Butwhatifwewanttoeditanexistingone?Itisverysimilartowhatwejustdid.Let'screatesomeimportantthingsquickly(ifyoudon'tunderstandsomething,youshouldaskyourcoachorlookatthepreviouschapters,sincewecoveredallthesestepsalready).

Openblog/templates/blog/post_detail.htmlandaddthisline:

blog/templates/blog/post_detail.html

<aclass="btnbtn-default"href="{%url'post_edit'pk=post.pk%}"><spanclass="glyphiconglyphicon-pencil"></span></a

>

sothatthetemplatewilllooklike:

blog/templates/blog/post_detail.html

DjangoForms

116

{%extends'blog/base.html'%}

{%blockcontent%}

<divclass="post">

{%ifpost.published_date%}

<divclass="date">

{{post.published_date}}

</div>

{%endif%}

<aclass="btnbtn-default"href="{%url'post_edit'pk=post.pk%}"><spanclass="glyphiconglyphicon-pencil"></

span></a>

<h1>{{post.title}}</h1>

<p>{{post.text|linebreaksbr}}</p>

</div>

{%endblock%}

Inblog/urls.pyweaddthisline:

blog/urls.py

url(r'^post/(?P<pk>\d+)/edit/$',views.post_edit,name='post_edit'),

Wewillreusethetemplateblog/templates/blog/post_edit.html,sothelastmissingthingisaview.

Let'sopenablog/views.pyandaddattheveryendofthefile:

blog/views.py

defpost_edit(request,pk):

post=get_object_or_404(Post,pk=pk)

ifrequest.method=="POST":

form=PostForm(request.POST,instance=post)

ifform.is_valid():

post=form.save(commit=False)

post.author=request.user

post.published_date=timezone.now()

post.save()

returnredirect('post_detail',pk=post.pk)

else:

form=PostForm(instance=post)

returnrender(request,'blog/post_edit.html',{'form':form})

Thislooksalmostexactlythesameasourpost_newview,right?Butnotentirely.Firstthing:wepassanextrapkparameterfromurls.Next:wegetthePostmodelwewanttoeditwithget_object_or_404(Post,pk=pk)andthen,whenwecreateaformwepassthispostasaninstancebothwhenwesavetheform:

blog/views.py

form=PostForm(request.POST,instance=post)

andwhenwejustopenedaformwiththisposttoedit:

blog/views.py

form=PostForm(instance=post)

Ok,let'stestifitworks!Let'sgotopost_detailpage.Thereshouldbeaneditbuttoninthetop-rightcorner:

DjangoForms

117

Whenyouclickityouwillseetheformwithourblogpost:

Feelfreetochangethetitleorthetextandsavechanges!

Congratulations!Yourapplicationisgettingmoreandmorecomplete!

IfyouneedmoreinformationaboutDjangoformsyoushouldreadthedocumentation:https://docs.djangoproject.com/en/1.9/topics/forms/

DjangoForms

118

SecurityBeingabletocreatenewpostsjustbyclickingalinkisawesome!But,rightnow,anyonethatvisitsyoursitewillbeabletopostanewblogpostandthat'sprobablynotsomethingyouwant.Let'smakeitsothebuttonshowsupforyoubutnotforanyoneelse.

Inblog/templates/blog/base.html,findourpage-headerdivandtheanchortagyouputinthereearlier.Itshouldlooklikethis:

blog/templates/blog/base.html

<ahref="{%url'post_new'%}"class="top-menu"><spanclass="glyphiconglyphicon-plus"></span></a>

We'regoingtoaddanother{%if%}tagtothiswhichwillmakethelinkonlyshowupforusersthatareloggedintotheadmin.Rightnow,that'sjustyou!Changethe<a>tagtolooklikethis:

blog/templates/blog/base.html

{%ifuser.is_authenticated%}

<ahref="{%url'post_new'%}"class="top-menu"><spanclass="glyphiconglyphicon-plus"></span></a>

{%endif%}

This{%if%}willcausethelinktoonlybesenttothebrowseriftheuserrequestingthepageisloggedin.Thisdoesn'tprotectthecreationofnewpostscompletely,butit'sagoodfirststep.We'llcovermoresecurityintheextensionlessons.

Remembertheediticonwejustaddedtoourdetailpage?Wealsowanttoaddthesamechangethere,sootherpeoplewon'tbeabletoeditexistingposts.

Openblog/templates/blog/post_detail.htmlandfind:

blog/templates/blog/post_detail.html

<aclass="btnbtn-default"href="{%url'post_edit'pk=post.pk%}"><spanclass="glyphiconglyphicon-pencil"></span></a

>

Changeitto:

blog/templates/blog/post_detail.html

{%ifuser.is_authenticated%}

<aclass="btnbtn-default"href="{%url'post_edit'pk=post.pk%}"><spanclass="glyphiconglyphicon-pencil"></sp

an></a>

{%endif%}

Sinceyou'relikelyloggedin,ifyourefreshthepage,youwon'tseeanythingdifferent.Loadthepageinanewbrowseroranincognitowindow,though,andyou'llseethatthelinkdoesn'tshowup,andtheicondoesn'tdisplayeither!

Onemorething:deploytime!Let'sseeifallthisworksonPythonAnywhere.Timeforanotherdeploy!

First,commityournewcode,andpushituptoGithub

command-line

DjangoForms

119

$gitstatus

$gitadd--all.

$gitstatus

$gitcommit-m"Addedviewstocreate/editblogpostinsidethesite."

$gitpush

Then,inaPythonAnywhereBashconsole:

command-line

$cdmy-first-blog

$gitpull

[...]

Finally,hoponovertotheWebtabandhitReload.

Andthatshouldbeit!Congrats:)

DjangoForms

120

What'snext?Congratulateyourself!You'retotallyawesome.We'reproud!<3

Whattodonow?

Takeabreakandrelax.Youhavejustdonesomethingreallyhuge.

Afterthatmakesureto:

FollowDjangoGirlsonFacebookorTwittertostayuptodate

Canyourecommendanyfurtherresources?Yes!First,goaheadandtryourotherbook,calledDjangoGirlsTutorial:Extensions.

Lateron,youcantrytheresourceslistedbelow.They'reallveryrecommended!

Django'sofficialtutorialNewCodertutorialsCodeAcademyPythoncourseCodeAcademyHTML&CSScourseDjangoCarrotstutorialLearnPythonTheHardWaybookGettingStartedWithDjangovideolessonsTwoScoopsofDjango:BestPracticesforDjango1.8bookHelloWebApp:LearnHowtoBuildaWebApp

What'snext?

121