DjangoGirlsTutorial Tabladecontenido Introducción 0 ¿CómofuncionaInternet? 1 Introducciónalalíneadecomandos 2 InstalacióndePython 3 Editordecódigo 4 IntroducciónaPython 5 ¿QuéesDjango? 6 InstalacióndeDjango 7 ComenzarunproyectoenDjango 8 ModelosenDjango 9 AdministradordeDjango 10 ¡Desplegar! 11 Djangourls 12 VistasdeDjango-¡Eshoradecrear! 13 IntroducciónaHTML 14 ORMdeDjango(Querysets) 15 Datosdinámicosenplantillas 16 PlantillasdeDjango 17 CSS-Hazlobonito 18 Extenderplantillas 19 Amplíatuaplicación 20 FormulariosenDjango 21 Dominio 22 ¿Quésigue? 23 2 DjangoGirlsTutorial TutorialdeDjangoGirls EstetrabajoestábajolalicenciainternacionalCreativeCommonsAttributionShareAlike4.0.Paraverunacopiadeestalicencia,visitaelsiguienteenlace http://creativecommons.org/licenses/by-sa/4.0/ Translation ThistutorialhasbeentranslatedfromEnglishintoSpanishbyawonderfulgroupof volunteers.SpecialthanksgoestoVictoriaMartinezdelaCruz,KevinMorales,Joshua Aranda,SilviaFrias,Leticia,AndreaGonzalez,AdrianManjarres,RodrigoCaicedo,Maria Chavez,MarceloNicolasManso,RosaDurante,Moises,IsraelMartinezVargas, JuanCarlos_,N0890Dy,IvanYivoff,KhaterineCastellano,ErickNavarro,cyncyncyn, ZeroSoul13,ErickAguayo,ErnestoRico-Schmidt,MiguelLozano,osueboy,dynarroand GeraldinaGarciaAlvarez. Introducción ¿Algunavezhassentidoqueelmundoestácadavezmáscercanoalatecnologíayde ciertomodotehasquedadoatrás?¿Algunaveztehaspreguntadocómocrearunsitioweb peronuncahastenidolasuficientemotivaciónparaempezar?¿Haspensadoalgunavez queelmundodelsoftwareesdemasiadocomplicadoparaticomoparaintentarhaceralgo portucuenta? Bueno,¡tenemosbuenasnoticiasparati!Programarnoestandifícilcomoaparentay queremosmostrartecuándivertidopuedellegaraser. Estetutorialnoteconvertiráenprogramadormágicamente.Siquieresserbuenaenesto, necesitarásmesesoinclusosañosdeaprendizajeypráctica.Peroqueremosmostrarteque programarocrearsitioswebnoestancomplicadocomoparece.Intentaremosexplicar pequeñasparteslomejorquepodamos,deformaquenotesientasintimidadaporla tecnología. ¡Esperamospoderhacerteamarlatecnologíatantocomonosotraslohacemos! ¿Quéaprenderásconestetutorial? Introducción 3 DjangoGirlsTutorial Cuandotermineseltutorial,tendrásunaaplicaciónwebsimpleyfuncional:tupropioblog. Temostraremoscomopublicarlaonline,¡asíotrospodránvertutrabajo! Tendrá(másomenos)éstaapariencia: Siestássiguiendoestetutorialportucuentaynotienesanadiequeteayudeencaso desurgiralgúnproblema,tenemosunchatparati: .¡Hemos pedidoanuestrostutoresyparticipantesanterioresqueesténahídevezencuando paraayudaraotrosconeltutorial!¡Notemasdejartuspreguntasallí! Bien,empecemosporelprincipio... Sobrenosotrosycómocontribuir EstetutoriallomantieneDjangoGirls.Siencuentrasalgúnerroroquieresactualizarel tutorial,porfavorsiguelaguíadecómocontribuir. Introducción 4 DjangoGirlsTutorial ¿Tegustaríaayudarnosatraducireltutoriala otrosidiomas? Actualmente,lastraduccionessellevanacabosobrelaplataformacrowdin.comen: https://crowdin.com/project/django-girls-tutorial Situidiomanoestalistadoencrowdin,porfavorabreunnuevoproblemainformandoel idiomaasípodemosagregarlo. Introducción 5 DjangoGirlsTutorial ¿CómofuncionaInternet? Estecapituloestáinspiradoporlacharla"HowtheInternetworks"deJessicaMcKellar (http://web.mit.edu/jesstess/www/). ApostamosqueutilizasInternettodoslosdías.Pero,¿sabesloquepasacuandoescribes unadireccióncomohttp://djangogirls.orgentunavegadorypresionas'Enter'? Loprimeroquetienesqueentenderesqueunsitiowebessólounmontóndearchivos guardadosenundiscoduro.Aligualquetuspelículas,músicaofotos.Sinembargo,los sitioswebposeenunapeculiaridad:ellosincluyenuncódigodecomputadorasllamado HTML. Sinoestásfamiliarizadaconlaprogramación,puedeserdifícildecaptarHTMLalprincipio, perotusnavegadoresweb(comoChrome,Safari,Firefox,etc.)loaman.Losnavegadores webestándiseñadosparaentenderestecódigo,seguirsusinstruccionesymostrartodos esosarchivosdeloscualestusitiowebestáhechodelamaneraexactacomotuquieres quesemuestren. Comocualquierotroarchivo,tenemosqueguardarlosarchivosHTMLenalgúnlugardeun discoduro.ParaInternet,usamosunascomputadorasespecialesypoderosasllamadas servidores.Ellasnotienenunapantalla,mouseoteclado,debidoaquesupropósitoes almacenardatosyservirlos.Poresarazónsonllamadosservidores--porqueellossirven losdatos. Ok,quizástepreguntescómoluceInternet,¿cierto? ¡Tehemoshechounaimagen!Lucealgoasí: ¿CómofuncionaInternet? 6 DjangoGirlsTutorial Pareceunlío,¿no?Enrealidadesunareddemáquinasconectadas(losmencionados servidores).¡Cientosdemilesdemáquinas!¡Muchos,muchoskilómetrosdecables alrededordelmundo!PuedesvisitarelsitiowebSubmarineCableMap (http://submarinecablemap.com/)dondesemuestranlasconexionesdecablessubmarinos alrededordelmundoyverlocomplicadaqueeslared.Aquíhayunacapturadepantallade lapáginaweb: ¿CómofuncionaInternet? 7 DjangoGirlsTutorial Esfascinante,¿no?Pero,obviamente,noesposibleteneruncableentrecadamáquina conectadaaInternet.Asíque,parallegaraunamáquina(porejemplolaquealojaa http://djangogirls.org)tenemosquepasarunasolicitudatravésdemuchasmáquinas diferentes. Separeceaesto: ¿CómofuncionaInternet? 8 DjangoGirlsTutorial Imaginaquecuandoescribeshttp://djangogirls.org,estasenviandounacartaquedice: "QueridosDjangoGirls,megustaríaversusitiowebdjangogrils.org.Porfavor,envíenmelo!" Tucartavahacialaoficinadecorreomáscercana.Luegovaaotraunpocomáscercana desudestinatario,luegoaotrayaotrahastaqueesentregadaensudestino.Loúnica cosadiferenteesquesituenvíascartas(paquetesdedatos)confrecuenciaalmismolugar, cadacartapuedepasarporoficinasdecorreos(routers)totalmentediferentes,dependiendo decómosedistribuyenencadaoficina. ¿CómofuncionaInternet? 9 DjangoGirlsTutorial Sí,estansimplecomoeso.Enviarmensajesyesperaralgunarespuesta.Porsupuesto,en vezdepapelylapicerausasbytesdedatos,¡perolaideaeslamisma! Enlugardedireccionesconelnombredelacalle,ciudad,códigopostalynombredelpaís, utilizamosdireccionesIP.TucomputadorapideprimeroelDNS(DomainNameSystem-en españolSistemadeNombresdeDominio)paratraducirdjangogirls.orgaunadirecciónIP. Funcionacomolosviejosdirectoriostelefónicosdondepuedesbuscarelnombredela personaquesedeseascontactaryestenosmuestrasunúmerodeteléfonoydirección. Cuandoenvíasunacarta,éstanecesitatenerciertascaracterísticasparaserentregada correctamente:unadirección,sello,etc.Tambiénutilizasunlenguajequeelreceptorpueda entender,¿cierto?Lomismosucedeconlospaquetesdedatosqueenvíasparaverunsitio web:utilizasunprotocolollamadoHTTP(HypertextTransferProtocol-enespañolProtocolo deTransferenciadeHipertexto). Asíque,básicamente,cuandotienesunsitiowebnecesitastenerunservidor(lamáquina) dondevive.Elservidorestáesperandocualquiersolicitudentrante(cartasquepidenal servidorqueenvíetusitioweb)yésterespondeenviandotusitioweb(enotracarta). PuestoqueesteesuntutorialdeDjango,segurotepreguntarásquéesloquehaceDjango. Bueno,cuandoenvíasunarespuesta,nosiemprequieresenviarlomismoatodoelmundo. Esmuchomejorsituscartassonpersonalizadas,especialmenteparalapersonaqueacaba deescribir,¿cierto?Djangonosayudaconlacreacióndeestascartaspersonalizadas:). ¿CómofuncionaInternet? 10 DjangoGirlsTutorial Bastadecharlas,¡pongamosmanosalaobra! ¿CómofuncionaInternet? 11 DjangoGirlsTutorial Introducciónalainterfazdelíneade comandos Esemocionante,¿verdad?Vasaescribirtuprimeralíneadecódigoenpocosminutos:) Permítenospresentarteatuprimernuevoamigo:¡lalíneadecomandos! Lossiguientespasostemostraráncómousaraquellaventananegraquetodosloshackers usan.Puedeparecerunpocoaterradoralprincipioperoessolounmensajeenpantallaque esperaaqueledesórdenes. ¿Quéeslalíneadecomandos? Laventana,quegeneralmenteesllamadalíneadecomandosointerfazdelíneade comandos,esunaaplicaciónbasadaentextoparaver,manejarymanipulararchivosentu computadora(comoporejemploelExploradordeWindowsoFinderenMac,perosinla interfazgráfica).Otrosnombresparalalíneadecomandosson:cmd,CLI,símbolodel sistema,consolaoterminal. Abrirlainterfazdelíneadecomandos Loprimeroquedebemoshacerparaempezaraexperimentarconnuestrainterfazdelinea decomandosesabrirla. Windows IralmenúInicio→Todoslosprogramas→Accesorios→CommandPrompt MacOSX Aplicaciones→Servicios→Terminal Linux EstáprobablementeenAplicaciones→Accesorios→Terminal,peroesodependedetu distribución.Sinoloencuentras,Googlealo:) Introducciónalalíneadecomandos 12 DjangoGirlsTutorial Prompt Ahoradeberíasverunaventanablancaonegraqueestáesperandotusórdenes. SiestásenMacoLinux,probablementeverás $,así: $ EnWindows,esunsignoasí >,comoeste: > Cadacomandoseráprecedidoporestesignoyunespacio,peronotienesqueescribirlo.Tu computadoraloharáporti:) Sólounapequeñanota:entucaso,talvezhayalgocomo C:\Users\ola>o OlasMacBook-Air:~ola$antesdelpromptyesoes100%correcto.Enestetutoriallo simplificaremoslomásposible. Tuprimercomando(¡YAY!) Vamosaempezarconalgosimple.Escribeestecomando: $whoami o >whoami YluegooprimelateclaEnter.Esteeselresultado: $whoamiolasitarska Comopuedesver,lacomputadorasólotepresentótunombredeusuario.Bien,¿eh?:) Tratadeescribircadacomando,nocopiesypegues.¡Teacordarásmásdeesta manera! Básicos Introducciónalalíneadecomandos 13 DjangoGirlsTutorial Cadasistemaoperativotieneunconjuntodiferentedecomandosparalalíneade comandos,asíqueasegúratedeseguirlasinstruccionesparatusistemaoperativo.Vamos aintentarlo,¿deacuerdo? Directorioactual Seríabuenosaberdóndeestamosahora,¿cierto?Vamosaver.Escribeestecomandoy oprimeEnter: $pwd /Users/olasitarska SiestásenWindows: >cd C:\Users\olasitarska Probablementeverásalgosimilarentumáquina.Unavezqueabreslalíneadecomandos generalmenteempiezaseneldirectoriohomedetuusuario. Nota:'pwd'significa'printworkingdirectory'-enespañol,'mostrardirectoriodetrabajo'. Listadearchivosydirectorios ¿Quéhayaquí?Seríabuenosaber.Veamos: $ls Applications Desktop Downloads Music ... Windows: >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 ... Introducciónalalíneadecomandos 14 DjangoGirlsTutorial Cambiaeldirectorioactual ¿Quizáspodemosiranuestroescritorio? $cdDesktop Windows: >cdDesktop Compruebasirealmentehacambiado: $pwd /Users/olasitarska/Desktop Windows: >cd C:\Users\olasitarska\Desktop ¡Aquíestá! Protip:siescribes cdDyluegooprimes tabenelteclado,lalíneadecomandos automáticamentecompletaráelrestodelnombreparaquepuedasnavegarmás rápido.Sihaymásdeunacarpetaqueempiececon"D",presionaelbotón tabdos vecesparaobtenerunalistadeopciones. Creardirectorio ¿QuétalsicreamosundirectoriodeDjangoGirlsentuescritorio?Puedeshacerlodeesta manera: $mkdirdjangogirls Windows: >mkdirdjangogirls Introducciónalalíneadecomandos 15 DjangoGirlsTutorial Estepequeñocomandocrearáunacarpetaconelnombre djangogirlsentuescritorio. ¡Puedescomprobarsiestáallíbuscandoentuescritoriooejecutandoelcomando ls/dir! Inténtalo:) Protip:Sinoquieresescribirunayotravezlosmismoscomandos,pruebaoprimiendo la flechaarribay flechaabajodetutecladoparaverrecientescomandos utilizados. ¡Ejercicios! Unpequeñoretoparati:eneldirectorioreciéncreado djangogirlscreaundirectorio llamado test.Utilizaloscomandos cdy mkdir. Solución: $cddjangogirls $mkdirtest $ls Windows: >cddjangogirls >mkdirtest >dir 08/05/201419:28<DIR>test ¡Felicitaciones!:) Limpiar Noqueremosdejarundesorden,asíquevamosaeliminartodoloquehicimoshastaeste momento. Enprimerlugar,tenemosquevolveralescritorio: $cd.. Windows: Introducciónalalíneadecomandos 16 DjangoGirlsTutorial >cd.. cd ..cambiaráeldirectorioactualaldirectoriopadre(quesignificaeldirectorioque contieneeldirectorioactual). Revisadóndeestás: $pwd /Users/olasitarska/Desktop Windows: >cd C:\Users\olasitarska\Desktop Ahoraeshoradeeliminareldirectorio djangogirls. Atención:Eliminararchivosutilizando del, rmdiro rmhacequenopuedan recuperarse,loquesignificaquelosarchivosborradosdesapareceránparasiempre Debessermuycuidadosaconestecomando. $rm-rdjangogirls Windows: >rmdir/sdjangogirls djangogirls,¿Estásseguro<Y/N>?Y Hecho!Asegurémonosqueenverdadfueronborrados,vamosaver: $ls Windows: >dir Salida ¡Estoestodoporahora!Ahorapuedescerrarlalíneadecomandossinproblemas.Vamosa hacerloalestilohacker,¿bien?:) Introducciónalalíneadecomandos 17 DjangoGirlsTutorial $exit Windows: >exit Genial,¿no?:) Índice Aquíhayunalistadealgunoscomandosútiles: Comando (Windows) Comando(Mac OS/Linux) Descripción Ejemplo exit exit Cierralaventana exit cd cd Cambiael directorio cdtest dir ls Lista directorios/archivos dir copy cp Copiadearchivos copyc:\test\test.txt c:\windows\test.txt move mv Muevearchivos movec:\test\test.txt c:\windows\test.txt mkdir mkdir Creaunnuevo directorio mkdirtestdirectory del rm Elimina archivos/directorios delc:\test\test.txt Estossonsoloalgunosdeloscomandosquepuedesejecutarenlalíneadecomandos.No vasausarnadamásqueesosporahora. Sitienescuriosidad,ss64.comcontieneunareferenciacompletadecomandosparatodos lossistemasoperativos. ¿Lista? ¡VamosasumergirnosenPython! Introducciónalalíneadecomandos 18 DjangoGirlsTutorial VamosaempezarconPython ¡Porfinestamosaquí! Peroprimero,déjenosdecirtequéesPython.Pythonesunlenguajedeprogramaciónmuy popularquepuedeutilizarseparalacreacióndesitiosweb,juegos,softwareacadémico, gráficosymucho,muchomás. Pythonseoriginóenladécadade1980ysuobjetivoprincipalesserlegibleporlosseres humanos(¡nosóloparalasmáquinas!),poresoparecemuchomássimplequeotros lenguajesdeprogramación.Estohacequeseamásfácildeaprender,peronote preocupes,¡Pythonestambiénmuypoderoso! InstalacióndePython EstesubcapítulosebasaenuntutorialdeGeekGirlsCarrots(http://django.carrots.pl/) DjangoestáescritoenPython.NecesitamosPythonparacualquiercosaenDjango.¡Vamos aempezarconlainstalación!QueremosqueinstalesPython3.4,asíquesitienesalguna versiónanterior,deberásactualizarla. Windows PuedesdescargarPythonparaWindowsdesdeelsitioweb https://www.python.org/downloads/release/python-343/.Despuésdedescargarelarchivo *.msi,debesejecutarlo(hazdobleclickenelarchivo)ysiguelasinstrucciones.Es importanterecordarlaruta(eldirectorio)dondesehainstaladoPython.¡Seránecesario másadelante! Algoparatenerencuenta:enlasegundapantalladelasistentedeinstalación,llamada "Customize",asegúratedeirhaciaabajoyelegirlaopción"Addpython.exetothePath", comoen InstalacióndePython 19 DjangoGirlsTutorial Linux EsmuyposiblequeyatengasPythoninstalado.Paraverificarqueyalotienesinstalado(y quéversiónes),abreunaconsolaytipeaelsiguientecomando: $python3--version Python3.4.2 SinotienesPythoninstaladoosiquieresunaversióndiferente,puedesinstalarlocomo sigue: Ubuntu Tipeaestecomandoentuconsola: sudoapt-getinstallpython3.4 Fedora Usaestecomandoentuconsola: InstalacióndePython 20 DjangoGirlsTutorial sudoyuminstallpython3.4 OSX Debesiralsitiowebhttps://www.python.org/downloads/release/python-342/ydescargarel instaladordePython: descargaelarchivoDMGMacOSX64-bit/32-bitinstaller, hazdobleclickparaabrirlo, dobleclickenPython.mpkgparaejecutaralinstalador. VerificaquelainstalaciónfueexitosaabriendolaTerminalyejecutandoelcomando python3: $python3--version Python3.4.2 Sitienesalgunadudaosialgosaliómalynosabescómoresolverlo-¡pideayudaatututor! Algunasveceslascosasnosalentanfácilmenteyesmejorpedirayudaaalguienconmás experiencia. InstalacióndePython 21 DjangoGirlsTutorial Editordecódigo Estásapuntodeescribirtuprimeralíneadecódigo,asíque¡eshoradedescargaruneditor decódigo! Haymuchoseditoresdiferentes,cuálusardependemuchodelapreferenciapersonal.La mayoríadeprogramadoresdePythonusanIDEs(EntornosdeDesarrolloIntegrados) complejosperomuypoderosos,comoPyCharm.Sinembargo,comoprincipiante,esoes probablementemenosconveniente;nuestrasrecomendacionessonigualdepoderosas peromuchomassimples. Nuestrassugerenciasestánlistadasabajo,perosiéntetelibredepreguntarleatututor cuálessonsuspreferencias-asíserámásfácilobtenersuayuda. Gedit Geditesuneditordecódigoabierto,gratis,disponibleparatodoslossistemasoperativos. Descárgaloaquí SublimeText2 SublimeTextesuneditormuypopularconunperiododepruebagratis.Esfácildeinstalary estádisponibleparatodoslossistemasoperativos. Descárgaloaquí Atom AtomesuneditordecódigomuynuevocreadoporGitHub.Esgratis,decódigoabierto, fácildeinstalaryfácildeusar.EstádisponibleparaWindows,OSXyLinux. Descárgaloaquí ¿Porquéestamosinstalandouneditorde código? Editordecódigo 22 DjangoGirlsTutorial Puedesestarpreguntándoteporquéestamosinstalandouneditorespecial,enlugarde usaruneditorconvencionalcomoWordoNotepad. Enprimerlugar,elcódigotienequesertextoplanoyelproblemadelasaplicacionescomo WordoTexteditesqueenrealidadnoproducentextoplano.Loquegeneranestexto enriquecido(contipografíasyformato),usandoformatospropioscomortf. Lasegundarazónesqueloseditoresdecódigosonherramientasespecializadasy,como tales,tienencaracterísticasmuyútiles,comoresaltarlasintáxisdelcódigocondiferentes coloresdeacuerdoasusignificadoocerrarcomillasportiautomáticamente. Veremostodoestoenacciónmásadelante.Enbreveempezarásapensarentufieleditor decódigocomounadetusherramientasfavoritas:) Editordecódigo 23 DjangoGirlsTutorial IntroducciónaPython PartedeestecapítulosebasaentutorialesporGeekGirlsCarrots (http://django.carrots.pl/). ¡Vamosaescribiralgodecódigo! Pythonprompt ParaempezarajugarconPython,tenemosqueabrirunalíneadecomandosennuestra computadora.Yasabescómohacerlo,loaprendisteenelcapítulodeIntroducciónalalínea decomandos. Unavezqueestéslisto,siguelassiguientesinstrucciones. QueremosabrirunaconsoladePython,asíqueescribe python3ypulsaEnter. $python3 Python3.4.2(...) Type"copyright","credits"or"license"formoreinformation. >>> TuprimercomandoenPython! DespuésdeejecutarelcomandodePython,elcursorcambiaa >>>.Paranosotrosesto significaqueporahorasólopodemosutilizarcomandosenellenguajePython.Notienes queescribirel >>>-Pythonloharáporti. SideseassalirdelaconsoladePythonencualquiermomento,simplementeescribe exit()ousaelatajo Ctrl+ZparaWindowsy Ctrl+DparaMac/Linux.Luegono verásmás >>>. PeroahoranoqueremossalirdelaconsoladePython.Queremosaprendermássobreella. Vamosaempezarconalgomuysimple.Porejemplo,tratadeescribiralgodematemáticas, como 2+3ypulsaEnter. >>>2+3 5 IntroducciónaPython 24 DjangoGirlsTutorial ¡Bien!¿Vescomosaliólarespuesta?¡Pythonsabematemáticas!Podríasintentarotros comandoscomo:- 4*5- 5-1- 40/2 Diviérteteconestoporunmomentoyluegovuelveaquí:). Comopuedesver,Pythonesunagrancalculadora.Siteestáspreguntandoquémáspuede hacer... Strings ¿Ytunombre?Escribatunombredepilaenfrasescomoésta: >>>"Ola" 'Ola' ¡Hascreadotuprimerstring!Esunasecuenciadecaracteresquepuedeserprocesadapor unacomputadora.Elstring(oenespañol,cadena)debecomenzaryterminarconelmismo carácter.Estopuedesercomillassimples( ')odobles( ")-ellasledicenaPythonquelo queestadentroesunacadena. Lascadenaspuedenserconcatenadas.Pruebaesto: >>>"Hola"+"Ola" 'HolaOla' Tambiénpuedesmultiplicarlascadenasconunnúmero: >>>"Ola"*3 'OlaOlaOla' Sinecesitasponerunapóstrofedentrodetucadena,tienesdosmanerasdehacerlo. Usandocomillasdobles: >>>"Runnin'downthehill" "Runnin'downthehill" oescapandoelapóstrofeconunabarrainvertida(``): >>>'Runnin\'downthehill' "Runnin'downthehill" IntroducciónaPython 25 DjangoGirlsTutorial Bien,¿eh?Paravertunombreenletrasmayúsculas,simplementeescribe: >>>"Ola".upper() 'OLA' ¡Usastelafunción upperentucadena!Unafunción(como upper())esunconjuntode instruccionesquePythontienequerealizarsobreunobjetodeterminado( "Ola")unavez quesellama. Siquisierassaberelnúmerodeletrasquecontienetunombre,tambiénexisteunafunción paraesto. >>>len("Ola") 3 Tepreguntarásporquéavecessellamaalasfuncionesconun .alfinaldeunacadena (como "Ola".upper())yavecessellamaaunafunciónycolocaslacadenaentre paréntesis.Bueno,enalgunoscasoslasfuncionespertenecenaobjetos,como upper(), quesólopuedeserutilizadosobrecadenas(upper()esunafuncióndelosobjetosstring). Enestecaso,llamamosmétodoaestafunción.Otraveces,lasfuncionesnopertenecena ningúnobjetoespecíficoypuedenserusadosendiferentesobjetos,como len().Estaes larazóndeporquéestamospasando "Ola"comounparámetroalafunción len. Resumen Ok,suficientesobrelascadenas.Hastaahorahasaprendidosobre: laterminal-teclearcomandos(código)dentrodelaterminaldePythonresultaen respuestasdePython númerosystrings-enPythonlosnúmerossonusadosparamatemáticasystrings paraobjetosdetexto operadores-como+y*,combinavaloresparaproducirunonuevo funciones-comoupper()ylen(),realizanopcionessobrelosobjetos. Estossonlosconocimientosbásicosquepuedesaprenderdecualquierlenguajede programación.¿Listaparaalgounpocomásdifícil?¡Apostamosqueloestás! Errores IntroducciónaPython 26 DjangoGirlsTutorial Intentemosconalgonuevo.¿Podríamosobtenerlalongituddeunnúmerodelamisma maneraqueobtuvimoslalongituddenuestronombre?Teclea len(304023)ypresiona Enter: >>>len(304023) Traceback(mostrecentcalllast): File"<stdin>",line1,in<module> TypeError:objectoftype'int'hasnolen() ¡Obtuvimosnuestroprimererror!Dicequelosobjetosdetipo"int"(númerosenteros)no tienenningunalongitud.¿Quépodemoshacerahora?Quizáspodemosescribirelnumero comounstring.Losstringstienenlongitud,¿cierto? >>>len(str(304023)) 6 ¡Funcionó!Utilizamoslafunción strdentrodelafunción len. str()conviertetodoa strings. Lafunción strconviertecosasenstrings Lafunción intconviertecosasenintegers Importante:podemosconvertirnúmerosentexto,peronopodemosnecesariamente convertirtextoennúmeros-¿quésería int('hello')? Variables Unconceptoimportanteenprogramaciónsonlasvariables.Unavariablenoesmásqueun nombreparaalgunacosaparaquepuedasusarlamástarde.Losprogramadoresusan estasvariablesparaalmacenardatos,hacersucódigomáslegibleyasínotenerqueseguir recordandoquehacecadacosa. Supongamosquequeremoscrearunanuevavariablellamada name: >>>name="Ola" ¿Ves?¡Esfácil!Essimplemente:nameequivaleaOla. Comotehasdadocuenta,elprogramanoregresaalgocomolohaciaantes.Entonces, ¿Cómosabemosquelavariableexisterealmente?Simplementeintroduce nameypulsa Enter: IntroducciónaPython 27 DjangoGirlsTutorial >>>name 'Ola' ¡Súper!Tuprimervariable:).Siemprepodráscambiaraloqueserefiere: >>>name="Sonja" >>>name 'Sonja' Puedesusarladentrodefuncionestambién: >>>len(name) 5 Increíble,¿verdad?Porsupuesto,lasvariablespuedensercualquiercosa,¡también números!Pruebaesto: >>>a=4 >>>b=6 >>>a*b 24 Pero¿quépasasiusamoselnombreequivocado?¿Puedesadivinarquépasaría?¡Vamos aprobar! >>>city="Tokyo" >>>ctiy Traceback(mostrecentcalllast): File"<stdin>",line1,in<module> NameError:name'ctiy'isnotdefined ¡Unerror!Comopuedesver,Pythontienediferentestiposdeerroresyestesellama NameError.Pythontedaráesteerrorsiintentasutilizarunavariablequenohasido definidaaún.Simásadelanteteencuentrasconesteerror,verificatucódigoparaversino hasescritomalunavariable. Juegaconestoporunratoymiraquepuedeshacer! Lafunciónprint Intentaesto: IntroducciónaPython 28 DjangoGirlsTutorial >>>name='Maria' >>>name 'Maria' >>>print(name) Maria Cuandosóloescribes name,elintérpretedePythonrespondeconlarepresentacióndel stringdelavariable'name',quesonlasletrasM-a-r-i-a,rodeadasdecomillassimples''. Cuandodices print(name),Pythonvaa"imprimir"elcontenidodelavariablealapantalla, sinlascomillas,queesmejor. Comoveremosdespués, print()tambiénesútilcuandoqueremosimprimircosasdesde adentrodelasfunciones,obiencuandoqueremosimprimircosasenmúltipleslíneas. Listas Ademásdestringeintegers,Pythontienetodaclasedediferentestiposdeobjetos.Ahora vamosaintroducirunollamadolist.Laslistassonexactamenteloquepiensasqueson:son objetosquesonlistasdeotrosobjetos:) Anímateycreaunalista: >>>[] [] Sí,estalistaestávacía.Noesmuyútil,¿verdad?Vamosacrearunalistadenúmerosde lotería.Noqueremosrepetirtodoeltiempo,asíquelospondremosenunavariabletambién: >>>lottery=[3,42,12,19,30,59] Muybien,¡tenemosunalista!¿Quépodemoshacerconella?Vamosavercuántos númerosdeloteríahayenlalista.¿Tienesalgunaideadequéfuncióndeberíasusarpara eso?¡Yasabesesto! >>>len(lottery) 6 ¡Sí! len()puededarteelnúmerodeobjetosenunalista.Útil,¿verdad?Talvezla ordenemosahora: IntroducciónaPython 29 DjangoGirlsTutorial >>>lottery.sort() Estonodevuelvenada,sólocambióelordenenquelosnúmerosaparecenenlalista. Vamosaimprimirlalistaotravezyverquepasó: >>>print(lottery) [3,12,19,30,42,59] Comopuedesver,losnúmerosentulistaahoraestánordenadosdemenoramayor. ¡Felicidades! ¿Tegustaríainvertireseorden?¡Vamosahacerlo! >>>lottery.reverse() >>>print(lottery) [59,42,30,19,12,3] Fácil,¿no?Siquieresañadiralgoatulista,puedeshacerloescribiendoestecomando: >>>lottery.append(199) >>>print(lottery) [59,42,30,19,12,3,199] Sideseasmostrarsóloelprimernúmero,puedeshacerlomedianteelusodeindexes(en español,índices).Uníndiceeselnúmeroquetedicedóndeenunalistaapareceunítem. Lacomputadorainicialacuentaen0,asíqueelprimerobjetoentulistaestáenelíndice0, elsiguientees1,yasísucesivamente.Intentaesto: >>>print(lottery[0]) 59 >>>print(lottery[1]) 42 Comopuedesver,puedesaccederadiferentesobjetosentulistautilizandoelnombredela listayelíndicedelobjetodentrodecorchetes. Paradiversiónadicional,pruebaalgunosotrosíndices:6,7,1000,-1,-6ó-1000.Aversise puedespredecirelresultadoantesdeintentarelcomando.¿Tienensentidolosresultados? Puedesencontrarunalistadetodoslosmétodosdisponiblesparalistasenestecapítulode ladocumentacióndePython:https://docs.python.org/3/tutorial/datastructures.html IntroducciónaPython 30 DjangoGirlsTutorial Diccionarios Undiccionarioessimilaraunalista,peroaccedesavaloresusandounaclaveenvezdeun índice.Unaclavepuedesercualquiercadenaonúmero.Lasintaxisparadefinirun diccionariovacíoes: >>>{} {} Estodemuestraqueacabasdecrearundiccionariovacío.¡Hurra! Ahora,trataescribiendoelsiguientecomando(intentareemplazandoconpropia información): >>>participant={'name':'Ola','country':'Poland','favorite_numbers':[7,42,92]} Conestecomando,acabasdecrearunavariable participantcontresparesclave-valor: Laclave nameapuntaalvalor 'Ola'(unobjeto string), countryapuntaa 'Poland'(otro string), y favorite_numbersapuntaa [7,42,92](una listcontresnúmerosenella). Puedesverificarelcontenidodeclavesindividualesconestasintaxis: >>>print(participant['name']) Ola Loves,essimilaraunalista.Perononecesitasrecordarelíndice-sóloelnombre. ¿QuépasasilepedimosaPythonelvalordeunaclavequenoexiste?¿Puedesadivinar? ¡Pruébaloyverás! >>>participant['age'] Traceback(mostrecentcalllast): File"<stdin>",line1,in<module> KeyError:'age' ¡Mira,otroerror!EsteesunKeyError.Pythonteayudaytedicequelallave 'age'no existeenestediccionario. ¿Cuándoutilizarundiccionarioounalista?Bueno,esoesunbuenpuntoparareflexionar. Sólotenunasoluciónenmenteantesdemirarlarespuestaenlasiguientelínea. IntroducciónaPython 31 DjangoGirlsTutorial ¿Sólonecesitasunasecuenciaordenadadeelementos?Usaunalista. ¿Necesitasasociarvaloresconclaves,asípuedesbuscarloseficientemente(usando lasclaves)másadelante?Utilizaundiccionario. Losdiccionarios,comolaslistas,sonmutables,loquesignificaquepuedensercambiados despuésdesercreados.Puedesagregarnuevosparesclave/valoreneldiccionario despuésdequehasidocreado,porejemplo: >>>participant['favorite_language']='Python' Comoenlaslistas,elmétodo len()enlosdiccionarios,devuelveelnúmerodepares clave-valoreneldiccionario.Adelante,escribeelcomando: >>>len(participant) 4 Esperotengasentidohastaahora.:)¿Listaparamásdiversiónconlosdiccionarios?Saltaa lasiguientelíneaparaalgunascosassorprendentes. Puedesutilizarelcomando delparaborrarunelementoeneldiccionario.Porejemplo,si deseaseliminarlaentradacorrespondientealaclave 'favorite_numbers',sólotienesque escribirelsiguientecomando: >>>delparticipant['favorite_numbers'] >>>participant {'country':'Poland','favorite_language':'Python','name':'Ola'} Comopuedesverenlasalida,elpardeclave-valorcorrespondientealaclave 'favorite_numbers'hasidoeliminado. Ademásdeesto,tambiénpuedescambiarunvalorasociadoaunaclaveyacreadaenel diccionario.Teclea: >>>participant['country']='Germany' >>>participant {'country':'Germany','favorite_language':'Python','name':'Ola'} Comopuedesver,elvalordelaclave 'country'hasidomodificadode 'Poland'a 'Germany'.:)¿Emocionante?¡Hurra!Hasaprendidootracosaasombrosa. Resumen IntroducciónaPython 32 DjangoGirlsTutorial ¡Genial!Sabesmuchosobreprogramaciónahora.Enestaúltimaparteaprendistesobre: errors-ahorasabescómoleeryentenderloserroresqueaparecensiPythonno entiendeuncomandoquelehasdado variables-nombresparalosobjetosquetepermitencodificarmásfácilmenteyhacer elcódigomáslegible lists-listasdeobjetosalmacenadosenunordendeterminado dictionaries-objetosalmacenadoscomoparesclave-valor ¿Emocionadaporlasiguienteparte?:) Comparacosas Unagranpartedelaprogramaciónincluyecompararcosas.¿Quéeslomásfácilpara comparar?Números,porsupuesto.Vamosavercómofunciona: >>>5>2 True >>>3<1 False >>>5>2*2 True >>>1==1 True >>>5!=2 True LedimosaPythonalgunosnúmerosparacomparar.Comopuedesver,Pythonnosólo puedecompararnúmeros,sinoquetambiénpuedecompararresultadosdemétodo.Bien, ¿eh? ¿Tepreguntasporquépusimosdossignosigual ==alladodelotroparacompararsilos númerossoniguales?Utilizamosunsolo =paraasignarvaloresalasvariables.Siempre, siempreesnecesarioponerdos ==Sideseascomprobarquelascosassonigualesentre sí.Tambiénpodemosafirmarquelascosasnosonigualesaotras.Paraeso,utilizamosel símbolo !=,comomostramosenelejemploanterior. DadostareasmásaPython: >>>6>=12/2 True >>>3<=2 False IntroducciónaPython 33 DjangoGirlsTutorial >y <sonfáciles,pero¿quéessignifica >=y <=?Seleenasí: x >ysignifica:xesmayorquey x <ysignifica:xesmenorquey x <=ysignifica:xesmenoroigualquey x >=ysignifica:xesmayoroigualquey ¡Genial!¿Quiereshacerunomas?Intentaesto: >>>6>2and2<3 True >>>3>2and2<1 False >>>3>2or2<1 True PuedesdarleaPythontodoslosnúmerosparacompararquequieras,ysiempretedará unarespuesta.Muyinteligente,¿verdad? and-siutilizaseloperador and,ambascomparacionesdebenserTrueparaqueel resultadodetodoelcomandoseaTrue or-siutilizaseloperador or,sólounadelascomparacionestienequeserTruepara queelresultadodetodoelcomandoseaTrue ¿Hasoídolaexpresión"compararmanzanasconnaranjas"?Vamosaprobarelequivalente enPython: >>>1>'django' Traceback(mostrecentcalllast): File"<stdin>",line1,in<module> TypeError:unorderabletypes:int()>str() Aquíverásquealigualqueenlaexpresión,Pythonnoescapazdecompararunnúmero ( int)yunstring( str).Encambio,muestraunTypeErrorynosdicequelosdostiposno sepuedencomparados. Boolean Porcierto,acabasdeaprenderacercadeunnuevotipodeobjetoenPython.Sellamaun Boolean--yesprobablementeeltipomássimplequeexiste. HaysólodosobjetosBoolean:-True-False IntroducciónaPython 34 DjangoGirlsTutorial PeroparaquePythonentiendaesto,esnecesarioquesiempreloescribascomoTrue (primeraletramayúscula,conelrestodelaletrasminúsculas).true,TRUE,tRUEno funcionarán--sóloTrueescorrecto.(LomismoaplicaaFalsetambién,porsupuesto.) Losvaloresbooleanospuedenservariables,también.Veelsiguienteejemplo: >>>a=True >>>a True Tambiénpuedeshacerlodeestamanera: >>>a=2>5 >>>a False Practicaydiviérteteconlosbooleanosejecutandolossiguientescomandos: TrueandTrue FalseandTrue Trueor1==1 1!=2 ¡Felicidades!Losbooleanossonunadelasfuncionesmásgenialesenprogramacióny acabasdeaprendercómousarlos. ¡Guárdalo! HastaahorahemosestadoescribiendonuestrocódigoPythonenelintérprete,locualnos limitaaunalíneadecódigoalavez.Normalmentelosprogramassonguardadosen archivosysonejecutadosporelintérpreteocompiladordenuestrolenguajede programación.Hastaahora,hemosestadocorriendonuestrosprogramasdeaunalíneapor vezenelintérpretedePython.Necesitaremosmásdeunalíneadecódigoparalas siguientestareas,entoncesnecesitaremoshacerrápidamenteloquesigue: SalirdelintérpretedePython Abrireleditordetextodenuestraelección GuardaralgodecódigoenunnuevoarchivodePython ¡Ejecutarlo! ParasalirdelintérpretedePythonquehemosestadousando,simplementeescribela funciónexit(): IntroducciónaPython 35 DjangoGirlsTutorial >>>exit() $ Estotellevarádevueltaalalíneadecomandos. Anteriormente,elegimosuneditordecódigoenlaseccióndeEditordecódigo.Tendremos queabrireleditorahorayescribiralgodecódigoenunarchivonuevo: print('Hello,Djangogirls!') NotaDeberíasnotarunadelascosasmásgenialesdeloseditoresdecódigo:¡los colores!EnlaconsoladePython,todoeradelmismocolor,peroahorapuedesverque lafunción printesdeuncolordiferentedelstringqueestáadentrodeella.Esose denomina"resaltadodesintaxis",yesunagranayudacuandoestásprogramando. Prestaatenciónaloscolores,yobtendrásunapistacuandoteolvidesdecerrarun stringocometesunerroralescribirunapalabraclave(comoel defenunafunción, queveremosabajo).Estaesunadelasrazonesporlascualesusaruneditorde código:) Obviamente,ahoraeresunadesarrolladoraPythonmuyexperimentada,asíquesiéntete libredeescribiralgodelcódigoquehasaprendidohoy. Ahoratenemosqueguardarelarchivoyasignarleunnombredescriptivo.Vamosallamaral archivopython_intro.pyyguardarloentuescritorio.Podemosnombrarelarchivode cualquiermaneraquequeramos,loimportanteaquíesasegurarsequeelarchivofinalice con.py,estoleindicaanuestracomputadoraqueesteesunarchivoejecutablede PythonyquePythonpuedecorrerlo. Conelarchivoguardado,¡eshoradeejecutarlo!Utilizandolashabilidadesquehas aprendidoenlaseccióndelíneadecomandos,utilizalaterminalparacambiarlos directorioseiralescritorio. EnunaMac,elcomandoseveráalgocomoesto: cd/Users/<your_name>/Desktop EnLinux,vaaserasí(lapalabra"Desktop"puedeestartraducidaatuidioma): cd/home/<your_name>/Desktop YenWindows,seráasí: IntroducciónaPython 36 DjangoGirlsTutorial cdC:\Users\<your_name>\Desktop Sitequedasatascada,sólopideayuda. yluegousaPythonparaejecutarelcódigoenelarchivocomosigue: $python3python_intro.py Hello,Djangogirls! ¡Muybien!EjecutastetuprimerprogramadePythondesdeunarchivo.¿Nosesiente increíble? Ahorapuedesmoverteaunaherramientaesencialenlaprogramación: If...elif...else Unmontóndecosasenelcódigosólosonejecutadascuandosecumplenlascondiciones dadas.PoresoPythontienealgollamadosentenciasif. Reemplazaelcódigoentuarchivopython_intro.pyporesto: if3>2: Siloguardáramosyloejecutáramos,veríamosunerrorcomoeste: $python3python_intro.py File"python_intro.py",line2 ^ SyntaxError:unexpectedEOFwhileparsing Pythonesperaqueledemosmásinstruccionesquesesuponeseránejecutadassila condición 3>2resultaserverdadera(o Trueenestecaso).Intentemoshacerque Pythonimprima"Itworks!".Cambiatucódigoenelarchivopython_intro.pyparaquese veacomoesto: if3>2: print('Itworks!') ¿Observascómohemosindentadolasiguientelíneadecódigocon4espacios?Tenemos quehacerestoparaquePythonsepaquécódigoejecutarsilacomparaciónresulta verdadera.Puedesponerunespacio,perocasitodoslosprogramadoresPythonhacen4 IntroducciónaPython 37 DjangoGirlsTutorial espaciosparahacerqueelcódigoseamáslegible.Unsolotabtambiéncontarácomo4 espacios. Guárdaloyejecútalodenuevo: $python3python_intro.py Itworks! ¿Quépasasilacondiciónnoesverdadera? Enejemplosanteriores,elcódigofueejecutadosólocuandolascondicioneseranciertas. PeroPythontambiéntienedeclaraciones elify else: if5>2: print('5isindeedgreaterthan2') else: print('5isnotgreaterthan2') Alejecutarestoseimprimirá: $python3python_intro.py 5isindeedgreaterthan2 Si2fueraunnúmeromayorque5,entonceselsegundocomandoseríaejecutado.Fácil, ¿verdad?Vamosavercómofunciona elif: name='Sonja' ifname=='Ola': print('HeyOla!') elifname=='Sonja': print('HeySonja!') else: print('Heyanonymous!') yalejecutarlo: $python3python_intro.py HeySonja! ¿Vesloquepasóahí? Resumen IntroducciónaPython 38 DjangoGirlsTutorial Enlosúltimostresejerciciosaprendisteacercade: Compararcosas-enPythonpuedescompararcosashaciendousode >, >=, ==, <=, <ydelosoperatores andy or Boolean-untipodeobjetoquesólopuedetenerunodedosvalores: Trueo False Guardararchivos-cómoalmacenarcódigoenarchivosasípuedesejecutar programasmásgrandes if...elif...else-sentenciasquetepermitenejecutarcódigosólocuandosecumplen ciertascondiciones ¡Eshoradeleerlaúltimapartedeestecapítulo! ¡Tuspropiasfunciones! ¿Recuerdaslasfuncionescomo len()quepuedesejecutarenPython?Bien,buenas noticias,¡ahoraaprenderáscómoescribirtuspropiasfunciones! UnafunciónesunasecuenciadeinstruccionesquePythondebeejecutar.Cadafunciónen Pythoncomienzaconlapalabraclave def,seleasignaunnombreypuedeteneralgunos parámetros.Vamosaempezarconalgofácil.Reemplazaelcódigoenpython_intro.pycon losiguiente: defhi(): print('Hithere!') print('Howareyou?') hi() Bien,¡nuestraprimerafunciónestálista! Tepreguntarásporquéhemosescritoelnombredelafunciónenlaparteinferiordel archivo.EstoesporquePythonleeelarchivoyloejecutadesdearribahaciaabajo.Asíque parapoderutilizarnuestrafunción,tenemosquereescribirsunombreenlaparteinferior. Ejecutemosestoyveamosquésucede: $python3python_intro.py Hithere! Howareyou? ¡Esofuefácil!Vamosaconstruirnuestraprimerafunciónconparámetros.Utilizaremosel ejemploanterior-unafunciónquedice'Hi'alapersonaqueejecutaelprograma-conun nombre: IntroducciónaPython 39 DjangoGirlsTutorial defhi(name): Comopuedesver,ahoradimosanuestrafunciónunparámetroquellamamos name: defhi(name): ifname=='Ola': print('HiOla!') elifname=='Sonja': print('HiSonja!') else: print('Hianonymous!') hi() Comopuedesnotar,tuvimosqueponerdosindentacionesantesdelafunción print porque ifnecesitasaberloquedeberíaocurrircuandosecumplelacondición.Vamosa vercómofunciona: $python3python_intro.py Traceback(mostrecentcalllast): File"python_intro.py",line10,in<module> hi() TypeError:hi()missing1requiredpositionalargument:'name' Oops,unerror.Porsuerte,Pythonnosdaunmensajedeerrorbastanteútil.Nosdicequela función hi()(laquedefinimos)tieneunargumentorequerido(llamado name)yquese nosolvidópasarloalllamaralafunción.Vamosaarreglarloenlaparteinferiordelarchivo: hi("Ola") yloejecutamosotravez: $python3python_intro.py HiOla! ¿Ysicambiamoselnombre? hi("Sonja") ylocorremos: IntroducciónaPython 40 DjangoGirlsTutorial $python3python_intro.py HiSonja! Ahora,¿quécreesquepasarásiescribesotronombreallí?(NoOlaoSonja).Pruébaloy verássitienesrazón.Estodeberíaimprimir: Hianonymous! Estoesincreíble,¿verdad?Deestaformanotienesquerepetirtodocadavezquedeseas cambiarelnombredelapersonaalaquelafuncióndeberíasaludar.Yesoesexactamente porquénecesitamosfunciones-¡paranorepetirtucódigo! Vamosahaceralgomásinteligente-haymásdedosnombres,yescribirunacondición paracadaunoseríadifícil,¿no? defhi(name): print('Hi'+name+'!') hi("Rachel") Ahoravamosallamaralcódigo: $python3python_intro.py HiRachel! ¡Felicidades!Acabasdeaprendercómoescribirfunciones:) Bucles Estayaeslaúltimaparte.¿Esofuerápido,verdad?:) Comohemosmencionado,losprogramadoressonperezosos,nolesgustarepetircosas.La programaciónintentaautomatizarlascosas,asíquenoqueremossaludaracadapersona porsunombremanualmente,¿verdad?Esahídondelosbuclessevuelvenmuyútiles. ¿Todavíarecuerdaslaslistas?Hagamosunalistadelaschicas: girls=['Rachel','Monica','Phoebe','Ola','You'] Queremossaludaratodasellasporsunombre.Tenemoslafunción hiquehaceeso,así quevamosausarlaenunbucle: IntroducciónaPython 41 DjangoGirlsTutorial fornameingirls: Lasentenciaforsecomportademanerasimilaralasentenciaif,elcódigoquesigue continuacióndebeestarindentadousandocuatroespacios. Aquíestáelcódigocompletoqueestaráenelarchivo: defhi(name): print('Hi'+name+'!') girls=['Rachel','Monica','Phoebe','Ola','You'] fornameingirls: hi(name) print('Nextgirl') ycuandoloejecutamos: $python3python_intro.py HiRachel! Nextgirl HiMonica! Nextgirl HiPhoebe! Nextgirl HiOla! Nextgirl HiYou! Nextgirl Comopuedesver,todoloqueponesconunaindentacióndentrodeunasentencia for serárepetidoparacadaelementodelalista girls. Tambiénpuedesusarel forennúmerosusandolafunción range: foriinrange(1,6): print(i) Loqueimprimirá: 1 2 3 4 5 IntroducciónaPython 42 DjangoGirlsTutorial rangeesunafunciónquecreaunalistadenúmerosenserie(estosnúmerosson proporcionadosporticomoparámetros). Tenencuentaqueelsegundodeestosdosnúmerosnoseráincluidoenlalistaque retornaráPython(esdecir, range(1,6)cuentadesde1a5,peronoincluyeelnúmero6). Resumen Esoestodo.¡Eresgenial!Estonofuetanfácilrealmente,asíquedeberíassentirte orgullosadetimisma.¡Estamosmuyorgullososdequehayasllegadohastaaquí! Talvezquierashaceralgodistintoporunmomento-estirarte,caminarunpoco,descansar tusojos-antesdepasaralsiguientecapítulo.:) IntroducciónaPython 43 DjangoGirlsTutorial ¿QuéesDjango? Django(gdh/ˈdʒæŋɡoʊ/jang-goh)esunframeworkparaaplicacioneswebgratuitoyopen source,escritoenPython.EsunWEBframework-unconjuntodecomponentesquete ayudanadesarrollarsitioswebmásfácilyrápidamente. Verás,cuandoestásconstruyendounsitioweb,frecuentementenecesitasunconjuntode componentessimilares:unamanerademanejarlaautenticacióndeusuarios(registrarse, iniciarsesión,cerrarsesión),unpaneldeadministraciónparatusitioweb,formularios,una formadesubirarchivos,etc. Porsuerteparati,hacetiempovariaspersonasnotaronquelosdesarrolladoresweb enfrentanproblemassimilarescuandoconstruyenunsitionuevo,poresojuntaroncabezas ycrearonframeworks(Djangoesunodeellos)queteofrecencomponenteslistospara usarse. Losframeworksexistenparaahorrartetenerquereinventarlaruedayayudarteaaliviarla cargacuandoconstruyesunsitio. ¿Porquénecesitasunframework? ParaentenderparaqueesDjango,necesitamosmirarmasdecercaalosservidores.Lo primeroesqueelservidornecesitasaberquequieresquetesirvaunapáginaweb. Imaginaunbuzón(puerto)elcualesmonitoreadoporcartasentrantes(peticiones).Estoes realizadoporunservidorweb.Elservidorwebleelacarta,yenvíaunarespuestaconuna páginaweb.Perocuandoquieresenviaralgo,tienesqueteneralgúncontenido.YDjango esalgoqueteayudaacrearelcontenido. ¿Quésucedecuandoalguiensolicitauna páginawebdetuservidor? CuandollegaunapeticiónaunservidorwebespasadoaDjangoqueintentaaveriguarlo querealmenteessolicitado.Tomaprimerounadireccióndepáginawebytratadeaveriguar quéhacer.EstaparteesrealizadaporurlresolverdeDjango(tengaencuentaquela direccióndeunsitiowebesllamadaURL-UniformResourceLocator-asíqueelnombre ¿QuéesDjango? 44 DjangoGirlsTutorial urlresolvertienesentido).Estenoesmuyinteligente-tomaunalistadepatronesytratade igualarlaURL.Djangocompruebalospatronesdearribahaciaabajoysialgosecoincide entoncesDjangolepasalasolicitudalafunciónasociada(quesellamavista). Imaginaauncarterollevandounacarta.Ellaestácaminandoporlacalleycompruebacada númerodecasaconelqueestáenlacarta.Sicoincide,elladejalacartaallí.Asíescomo funcionaelurlresolver! Enlafuncióndevistasehacentodaslascosasinteresantes:podemosmiraraunabasede datosparabuscaralgunainformación.¿Talvezelusuariopidiócambiaralgoenlosdatos? Comounacartadiciendo"Porfavorcambialadescripcióndemitrabajo."Lavistapuede comprobarsitenespermitidohacereso,entoncesactualizarladescripcióndeltrabajopara ustedydevolverleunmensaje:"¡hecho!".EntonceslavistageneraunarespuestayDjango puedeenviarlaalnavegadordelusuario. Porsupuesto,ladescripciónanteriorsesimplificaunpoco,perononecesitassabertodas lascosastécnicasaun.Tenerunaideageneralessuficiente. Asíqueenlugardemeternosdemasiadoenlosdetalles,simplementecomenzaremos creandoalgoconDjangoyaprenderemostodaslaspiezasimportantesenelcamino! ¿QuéesDjango? 45 DjangoGirlsTutorial InstalacióndeDjango PartedeestecapituloestabasadoenlostutorialesdeGeekGirlsCarrots (http://django.carrots.pl/). Partedeestecapítulosebasaeneldjango-marcadortutorialbajolicenciadeCreative CommonsAttribution-ShareAlike4.0internacional.Eltutorialdedjango-marcadortiene derechosdeautordeMarkusZapke-Gündemannetal. Entornovirtual AntesdeinstalarDjango,instalaremosunaherramientaextremadamenteútilqueayudaráa mantenertuentornodedesarrolloordenadoensucomputadora.Esposibleomitireste paso,peroesmuyrecomendablenohacerlo-¡comenzarconlamejorconfiguraciónposible ayudaraaevitarmuchosproblemasenelfuturo! Asíque,vamosacrearunentornovirtual(tambiénllamadounvirtualenv).Aislarála configuraciónPython/Djangoenbasedecadaproyecto,loquesignificaquecualquier cambioquerealiceenunsitiowebnoafectaráaotrosquetambiénestésdesarrollando. Genial,¿no? Todoloquenecesitashaceresencontrarundirectorioenelquedeseescrearel virtualenv;tudirectoriohome,porejemplo.EnWindowspuedeversecomo C:\Users\Name(donde nombreeselnombredetuusuario). Paraestetutorialusaremosunnuevodirectorio djangogirlsentudirectoriohome: mkdirdjangogirls cddjangogirls Haremosunvirtualenvllamado myvenv.Elcomandogeneralestaráenelformato: python3-mvenvmyvenv Windows Paracrearunnuevo virtualenv,debesabrirlaconsola(teloindicamosunoscuantos capítulosantes,¿recuerdas?)yejecuta C:\Python34\python-mvenvmyvenv.Severáasí: InstalacióndeDjango 46 DjangoGirlsTutorial C:\Users\Name\djangogirls>C:\Python34\python-mvenvmyvenv endonde C:\Python34\pythoneseldirectorioenelqueinstalastePythonpreviamentey myvenveselnombredetu virtualenv.Puedesutilizarcualquierotronombre,pero asegúratedeusarminúsculasynodejarespacios,acentosocaracteresespeciales. Tambiénesunabuenaideamantenerelnombrecorto.¡Vasareferirteaélmucho! LinuxyOSX Crearun virtualenvenLinuxyOSXestansimplecomoejecutar python3-mvenv myvenv.Severáasí: ~/djangogirls$python3-mvenvmyvenv myvenveselnombredetu virtualenv.Puedesusarcualquierotronombre,peromantén elusodeminúsculasynoincluyasespacios.Tambiénesunabuenaideamantenerel nombrecorto.¡Vasareferirteaélmucho! Nota:Actualmente,iniciarelentornovirtualenUbuntu14.04deestamaneraproduce elsiguienteerror: Error:Command'['/home/eddie/Slask/tmp/venv/bin/python3','-Im','ensurepip','--upgrade','--defa Paraevitaresto,utilizadirectamenteelcomando virtualenv. ~/djangogirls$sudoapt-getinstallpython-virtualenv ~/djangogirls$virtualenv--python=python3.4myvenv Trabajarconvirtualenv Estecomandoanteriorcrearáundirectoriollamado myvenv(ocualquiernombrequehayas escogido)quecontienenuestroentornovirtual(básicamenteunmontóndearchivosy carpetas).Todoloquequeremoshacerahoraesiniciarloejecutando: C:\Users\Name\djangogirls>myvenv\Scripts\activate enWindows,o: InstalacióndeDjango 47 DjangoGirlsTutorial ~/djangogirls$sourcemyvenv/bin/activate enOSXyLinux. ¡Recuerdareemplazar myvenvcontunombrede virtualenvquehayaselegido! Nota:avecesla fuentepodríanoestardisponible.Enesoscasostratadehacerlo esto: ~/djangogirls$.myvenv/bin/activate Sabrásquetienes virtualenviniciadocuandoveasquepareceestemensajeenla consola: (myvenv)C:\Users\Name\djangogirls> o: (myvenv)~/djangogirls$ ¡Notaqueelprefijo (myvenv)aparece! Cuandotrabajesenunentornovirtual, pythonautomáticamentesereferiráalaversión correcta,demodoquepuedesutilizar pythonenvezde python3. Tenemostodaslasdependenciasimportantesensulugar.¡Finalmentepodemosinstalar Django! InstalarDjango Ahoraquetienestu virtualenviniciado,puedesinstalarDjangousando pip.Enla consola,ejecuta pipinstalldjango==1.8(fíjatequeutilizamosundoblesignoigual: ==). (myvenv)~$pipinstalldjango==1.8 Downloading/unpackingdjango==1.8 Installingcollectedpackages:django Successfullyinstalleddjango Cleaningup... EnWindows InstalacióndeDjango 48 DjangoGirlsTutorial SiobtienesunerroralejecutarpipenWindowscompruebasilarutadetuproyecto contieneespacios,acentosocaracteresespeciales(porejemplo, C:\Users\User Name\djangogirls).Silotiene,porfavorconsideramoverlaaotrolugarsinespacios, acentosocaracteresespeciales(sugerencia: C:\djangogirls).Despuésdehacerlo ejecutanuevamenteelcomandoanterior. enLinux SiobtienesunerroralcorrerpipenUbuntu12.04ejecuta python-mpipinstall-Uforce-resintallpipparaarreglarlainstalacióndepipenelvirtualenv. Esoestodo!Ahoraestáslisto(porfin)paracrearunaaplicaciónDjango! InstalacióndeDjango 49 DjangoGirlsTutorial ¡TuprimerproyectoenDjango! PartedeestecapituloestabasadoenlostutorialesdeGeekGirlsCarrots (http://django.carrots.pl/). Partedeestecapítulosebasaeneldjango-marcadortutorialbajolicenciadeCreative CommonsAttribution-ShareAlike4.0internacional.Eltutorialdedjango-marcadortiene derechosdeautordeMarkusZapke-Gündemannetal. Vamosacrearunsimpleblog! ElprimerpasoparacrearloesparainiciarunnuevoproyectoenDjango.Básicamente,esto significaquepodráscorreralgunosscriptsproporcionadosporDjangoquecrearánel esqueletodeunproyectoparanosotros:unmontóndedirectoriosyarchivosquevamosa utilizarmásadelante. LosnombresdealgunosarchivosydirectoriossonmuyimportantesparaDjango.No deberíasrenombrarlosarchivosqueestamosapuntodecrear.Moverlosaunlugar diferentetampocoesunabuenaidea.Djangotienequemantenerunaciertaestructurapara sercapazdeencontrarcosasimportantes. Recuerdacorrertodoenelvirtualenv.Sinovesunprefijo (myvenv)entuconsola necesitasactivartuvirtualenv.Explicamoscómohaceresoenelcapítulode InstalacióndeDjangoenlasecciónTrabajandoconvirtualenv.Puedeshacerlo escribiendoelsiguientecomando: myvenv\Scripts\activateenWindowso myvenv/bin/activateenMacOS/Linux. NotaControladosvecesqueincluisteelpunto( .)alfinaldelcomando,esimportante porqueledicealscriptqueinstaleDjangoeneldirectorioactual. Enlaconsoladebesejecutar(recuerdanoescribir (myvenv)~/djangogirls$,¿ok?): (myvenv)~/djangogirls$django-adminstartprojectmysite. EnWindows: (myvenv)C:\Users\Name\djangogirls>django-admin.pystartprojectmysite. django-admin.pyesunscriptquecrearálosarchivosydirectoriosparati.Ahoradeberías tenerunaestructuradedirectoriosparecidaaesto: ComenzarunproyectoenDjango 50 DjangoGirlsTutorial djangogirls ├───manage.py └───mysite settings.py urls.py wsgi.py __init__.py manage.pyesunscriptqueayudaconlaadministracióndelsitio.Conellopodremosiniciar unservidorwebennuestroordenadorsinnecesidaddeinstalarnadamás,entreotras cosas. Elarchivo settings.pycontienelaconfiguracióndetusitioweb. ¿Recuerdascuandohablamosdeuncarteroquedebíacomprobardondeentregaruna carta?Elarchivo urls.pycontieneunalistadelospatronesutilizadospor urlresolver. Ignoremoslosotrosarchivosporahora-noloscambiaremos.¡Loúnicoquedebesrecordar esnoborrarlosporaccidente! Cambiandolaconfiguración Vamosahaceralgunoscambiosen mysite/settings.py.Abreelarchivousandoeleditor decódigoquehasinstaladoanteriormente. Seríabuenotenerelhorariocorrectoennuestrositioweb.Vealalistadehusoshorariosde Wikipediaycopiatuzonahoraria(TZ).(porejemplo, Europe/Berlin) Ensettings.py,encuentralalíneaquecontiene TIME_ZONEymodifícalaparaelegirtupropia zonahoraria: TIME_ZONE='Europe/Berlin' Modificando"Europe/Berlin"comocorresponda Tambiénnecesitaremosagregarunarutaparalosarchivosestáticos(aprenderemostodo sobrelosarchivosestáticosyCSSmástardeenestetutorial).Vehaciaabajohastaelfinal delarchivo,yjustopordebajodelaentrada STATIC_URL,agregaunanuevallamada STATIC_ROOT: STATIC_URL='/static/' STATIC_ROOT=os.path.join(BASE_DIR,'static') ComenzarunproyectoenDjango 51 DjangoGirlsTutorial Configurarunabasededatos Hayunagranvariedaddeopcionesdebasesdedatosparaalmacenarlosdatosdetusitio. Utilizaremoselquevienepordefecto, sqlite3. Estoyaestáconfiguradoenestapartedetuarchivo mysite/settings.py: DATABASES={ 'default':{ 'ENGINE':'django.db.backends.sqlite3', 'NAME':os.path.join(BASE_DIR,'db.sqlite3'), } } Paracrearunabasededatosparanuestroblog,ejecutemoslosiguienteenlaconsola: pythonmanage.pymigrate(necesitamosestareneldirectoriode djangogirlsquecontiene elarchivo manage.py).Siesovabien,deberíasveralgoasí: (myvenv)~/djangogirls$pythonmanage.pymigrate Operationstoperform: Applyallmigrations:admin,contenttypes,auth,sessions Runningmigrations: Applyingcontenttypes.0001_initial...OK Applyingauth.0001_initial...OK Applyingadmin.0001_initial...OK Applyingsessions.0001_initial...OK ¡Ylisto!¡Eshoradeiniciarelservidorwebyversinuestrositiowebestáfuncionando! Debesestareneldirectorioquecontieneelarchivo manage.py(enlacarpeta djangogirls).Enlaconsola,podemosiniciarelservidorwebejecutando pythonmanage.py runserver: (myvenv)~/djangogirls$pythonmanage.pyrunserver Ahoratodoloquedebeshacerescontrolarquetusitioestécorriendo-abretunavegador (Firefox,Chrome,Safari,InternetExploreroelqueutilices)eingresaladirección: http://127.0.0.1:8000/ Elservidorwebseapropiarádetuconsolahastaqueloterminesmanualmente:paratipear máscomandosoabresunanuevaterminal(ynoteolvidesdeactivartuvirtualenvallí también),ofrenaelservidorwebyendoalaconsolaenlaqueestácorriendoypresionando ComenzarunproyectoenDjango 52 DjangoGirlsTutorial Ctrl+C-lasteclasControlyCjuntas(enWindows,deberáspresionarCtrl+Break). ¡Felicitaciones!¡Hascreadotuprimersitiowebylohasejecutadousandounservidorweb! ¿Noesgenial? ¿Listaparaelpróximopaso?¡Esmomentodecrearalgodecontenido! ComenzarunproyectoenDjango 53 DjangoGirlsTutorial ModelosenDjango Loquequeremoscrearahoraesalgoquevaaalmacenartodoslospostsennuestroblog. Peroparapoderhacerlotenemosquehablarunpocodeacercadealgollamado objetos. Objetos Hayunconceptoenelmundodelaprogramaciónllamado programaciónorientadaa objetos.Laideaesqueenlugardeescribirtodocomounaaburridasecuenciade instruccionesdeprogramaciónpodemosmodelarcosasydefinircómointeractúanconlas demás. Entonces¿Quéesunobjeto?Esunconjuntodepropiedadesyacciones.Suenararo,pero tedaremosunejemplo. Siqueremosunmodelarungatocrearemosunobjeto Gatoquetienealgunas propiedades,comosonporejemplo color, edad, estadodeánimo(esdecir,bueno,malo, sueño;)), dueño(queesunobjeto Personao,talvez,enelcasodequeelgatosea callejero,estapropiedadestarávacía). Yluegoel Gatotienealgunasacciones: ronronear, rasguñaro alimentarse(enlacual daremosalgatoalgunos ComidaDeGato,quepodríaserunobjetoindependientecon propiedades,comoporejemplo, sabor). Gato --------color edad humor dueño ronronear() rasguñar() alimentarse(comida_de_gato) ComidaDeGato ---------sabor Básicamentesetratadedescribircosasrealesenelcódigoconpropiedades(llamadas propiedadesdelobjeto)ylasacciones(llamadas métodos). Yahora,¿cómomodelamoslospostsenelblog?Queremosconstruirunblog,¿no? ModelosenDjango 54 DjangoGirlsTutorial Tenemosprimeroqueresponderalgunaspreguntas:¿Quéesunpostdeunblog?¿Qué característicasdebetener? Bueno,seguroquenuestrospostsnecesitanuntextoconsucontenidoyuntítulo,¿cierto? Tambiénseríabuenosaberquiénloescribió,asíquenecesitamosunautor.Porúltimo, queremossabercuándoelpostfuecreadoypublicado. Post -------title text author created_date published_date ¿Quétipodecosaspodríahacerseconunaentradadelblog?Seríabuenoteneralgún métodoquepubliquelaentrada,¿no? Asíquevamosanecesitarelmétodo publicar. Puestoqueyasabemosloquequeremoslograr,¡podemosempezaramoderlarloen Django! ModeloenDjango Sabiendoquéesunobjeto,podemoscrearunmodeloenDjangoparanuestrospostsenel blog. UnmodeloenDjangoesuntipoespecialdeobjetoqueseguardaenla basededatos. Unabasededatosesunacoleccióndedatos.Allíesellugarenelcualalmacenarásla informaciónsobreusuarios,postsdelblog,etc.UtilizaremosunabasededatosSQLitepara almacenarnuestrosdatos.Esteeseladaptadordebasededatospredeterminadaen Django--serásuficienteparanosotrosporahora. Piensaenelmodeloenlabasededatoscomounahojadecálculoconcolumnas(campos) yfilas(datos). Creandounaaplicación Paramantenertodoenorden,crearemosunaaplicaciónseparadadentrodenuestro proyecto.Esmuybuenotenertodoorganizadodesdeelprincipio.Paracrearuna aplicación,necesitamosejecutarelsiguientecomandoenlaconsola(dentrodelacarpeta de djangogirlsdondeestáelarchivo manage.py): ModelosenDjango 55 DjangoGirlsTutorial (myvenv)~/djangogirls$pythonmanage.pystartappblog Vasnotarquesecreaunnuevodirectoriollamado blogycontieneunaseriedearchivos. Nuestrosdirectoriosyarchivosennuestroproyectodeberíanparecerseaesto: djangogirls ├──mysite |__init__.py |settings.py |urls.py |wsgi.py ├──manage.py └──blog ├──migrations |__init__.py ├──__init__.py ├──admin.py ├──models.py ├──tests.py └──views.py DespuésdecrearunaaplicacióntambiénnecesitamosdecirleaDjangoquedebeutilizarla. Lohacemosenelarchivo mysite/settings.py.Tenemosqueencontrar INSTALLED_APPSy añadirunalíneaquecontiene 'blog',justoporencimade ).Elproductofinaldebetener esteaspecto: INSTALLED_APPS=( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'blog', ) CreandoelModeloPost Enelarchivo blog/models.pydefinimostodoslosobjetosllamados Models-esteesun lugarenelcualdefiniremosnuestromodelopost. Vamosabrir blog/models.py,quitamostodoyescribimosuncódigocomoeste: ModelosenDjango 56 DjangoGirlsTutorial 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 Vuelveaverificarqueusastedosguionesbajos( _)encadaladodel str.Estosse utilizanconfrecuenciaenPythonyavecestambiénlosllamamos"dunder"(diminutivo eninglésde"double-underscore"). Daunpocodemiedo,¿verdad?Peronotepreocupes,¡vamosaexplicarquésignifican estaslíneas! Todaslaslíneasquecomienzancon fromo importsonlíneasparaañadiralgodeotros archivos.Asíqueenvezdecopiarypegarlasmismascosasencadaarchivo,podemos incluiralgunaspartescon from...import.... classPost(models.Model):-estalíneadefinenuestromodelo(esun objeto). classesunapalabraclavequeindicaqueestamosdefiniendounobjeto. Posteselnombredenuestromodelo.Podemosdarleunnombrediferente(pero debemosevitarespaciosenblancoycaracteresespeciales).Unaclasesiempre comienzaconsuprimeraletraenmayúscula. models.ModelsignificaquePostesunmodelodeDjango,asíDjangosabequedebe guardarloenlabasededatos. Ahoradefinimoslaspropiedadesquehablábamos: title, text, created_date, published_datey author.Parahaceresotenemosquedefiniruntipodecampo(¿es texto?¿unnúmero?¿unafecha?¿unarelaciónconotroobjeto-esdecir,unusuario?). models.CharField-estoescomodefinesuntextoconunnúmerolimitadode caracteres. models.TextField-estoesparatextoslargossinunlímite.Seráidealparael ModelosenDjango 57 DjangoGirlsTutorial contenidodeunpost,¿verdad? models.DateTimeField-estoesfechayhora. modelos.ForeignKey-esteesunvínculoconotromodelo. Novamosaexplicarcadapedacitodecódigo,yaquenostomaríademasiadotiempo. DebesecharunvistazoaladocumentacióndeDjangosiquieressabermássobrelos camposdelosModelosycómodefinircosasdiferentesalasdescritasanteriormente (https://docs.djangoproject.com/en/1.8/ref/models/fields/#field-types). ¿Yquésobre defpublish(self):?Esexactamentenuestrométodo publishque mencionamosanteriormente. defsignificaquesetratadeunafunciónométodo. publish eselnombredelmétodo.Puedescambiarlo,siquieres.Lareglaesqueusamosminúsculas yguionesbajosenlugardeespacios(esdecir,siquierestenerunmétodoquecalculeel preciomedio,estepodríallamarse calculate_average_price). Losmétodosmuyamenudo devuelvenalgo.Hayunejemplodeestoenelmétodo __str__.Enesteescenario,cuandollamamosa __str__()obtendremosuntexto(string) conuntítulodePost. Sialgotodavíanoestáclarosobremodelos,¡nodudesenpreguntaratututor!Sabemos queesmuycomplicado,sobretodocuandoestásentendiendoquéfuncionesyobjetosson mientrassiguesestedocumento.Consuerte,¡todotieneunpocomássentidoparatiahora! Creartablasparalosmodelosentubasededatos Elúltimopasoesañadirnuestronuevomodeloanuestrabasededatos.Primerotenemos quehacerqueDjangosepaquetenemosalgunoscambiosennuestromodelo(acabamos decrearlo),escribe pythonmanage.pymakemigrationsblog.Severáasí: (myvenv)~/djangogirls$pythonmanage.pymakemigrationsblog Migrationsfor'blog': 0001_initial.py: -CreatemodelPost Djangoprepararáunarchivodemigraciónquetenemosqueaplicarahoraanuestrabase dedatosescribiendo pythonmanage.pymigrateblog.Elresultadodebeser: (myvenv)~/djangogirls$pythonmanage.pymigrateblog Operationstoperform: Applyallmigrations:blog Runningmigrations: Applyingblog.0001_initial...OK ModelosenDjango 58 DjangoGirlsTutorial ¡Hurra!NuestromodelodePostestáahoraennuestrabasededatos.Seríabuenoverlo, ¿no?¡DirígetealsiguientecapítuloparavercómolucetuPost! ModelosenDjango 59 DjangoGirlsTutorial AdministradordeDjango Paraagregar,editaryborrarlospostsquehemosmodelado,utilizaremoseladministrador deDjango. Vamosaabrirelarchivo blog/admin.pyyreemplazarsucontenidoconesto: fromdjango.contribimportadmin from.modelsimportPost admin.site.register(Post) Comopuedesver,importamos(incluimos)elmodeloPostdefinidoenelcapítuloanterior. Parahacernuestromodelovisibleenlapáginadeladministrador,tenemosqueregistrarel modelocon admin.site.register(Post). Ok,eshoradevertumodeloPost.Recuerdaejecutar pythonmanage.pyrunserverenla consolaparacorrerelservidorweb.Vealnavegadorytipealadirección http://127.0.0.1:8000/admin/.Verásunapáginadeingresocomolaquesigue: Parapoderingresardeberáscrearunsuperusuario-unusuarioquetienecontrolsobretodo loquehayenelsitio.Vuelvehaciaatrásatulíneadecomandosytipea pythonmanage.py createsuperuser,presionaenterytipeatunombredeusuario(enminúsculas,sinespacios), direccióndeemailycontraseñacuandoseanrequeridos.Notepreocupesquenopuedes AdministradordeDjango 60 DjangoGirlsTutorial vertucontraseñamientraslatipeas-asíescomodebeser.Simplementetipéalaypresiona 'Enter'paracontinuar.Lasalidadeestecomandodeberíaverseasí(nombredeusuarioy emaildeberíanserlostuyos): (myvenv)~/djangogirls$pythonmanage.pycreatesuperuser Username:admin Emailaddress:[email protected] Password: Password(again): Superusercreatedsuccessfully. Vuelveatunavegadoreingresaconlascredencialesdesuperusuarioqueelegiste,ahora deberíaspoderverelpaneldeadministracióndeDjango. VeaPostsyexperimentaunpococonesto.Agregacincooseispostsdelblog.Note preocupesporelcontenido-puedessimplementecopiarypegartextodeestetutorialenel contenidodetuspostsparaahorrartiempo:). Asegúratedequeporlomenosdosotresposts(peronotodas)tienenlafechade publicación.Seráútilluego. AdministradordeDjango 61 DjangoGirlsTutorial SiquieressabermássobreeladministradordeDjango,puedesvisitarladocumentaciónde Django:https://docs.djangoproject.com/en/1.8/ref/contrib/admin/ Esteprobablementeseaunbuenmomentoparatomaruncafé(oté)oalgoparacomery re-energizarte.CreastetuprimermodelodeDjango-¡merecesunpequeñorecreo! AdministradordeDjango 62 DjangoGirlsTutorial ¡Despliega! Nota:Elsiguientecapítulopuedeseravecesunpocodifícildesuperar.Sepersistente yacábalo.Eldespliegueesunaparteimportantedelprocesoeneldesarrolloweb. Estecapítuloestásituadoenelmediodeltutorialparaquetututorpuedaayudartea ponertusitiowebenlínea,loquepuedeserunprocesoalgomáscomplicado.Esto significaquepodrásacabareltutorialportucuentasiseteacabaeltiempo. Hastaahoratusitiowebestabadisponiblesóloentuordenador,¡ahoraaprenderáscómo desplegarlo!EldespliegueeselprocesodepublicartuaplicaciónenInternetparaquela gentepuedaaccederyvertuaplicación:). Comoyahasaprendido,unsitiowebtienequeestarenunservidor.Haymuchos proveedores,perousaremosunoquetieneunprocesodedesplieguerelativamentesimple: PythonAnywhere.PythonAnywhereesgratisparapequeñasaplicacionesquenotienen demasiadosvisitantes,definitivamentesuficienteparaestecaso. ElotroservicioexternoquevamosautilizaresGitHub,unserviciodealojamientode código.Hayotrasopcionesporahí,perohoyendíacasitodoslosprogramadorestienen unacuentadeGitHub,¡yahoratútambiénlavasatener! UsaremosGitHubcomopasointermedioparatransportarnuestrocódigodesdeyhasta PythonAnywhere. Git Gitesun"sistemadecontroldeversiones"usadopormuchosprogramadores-esun sistemaqueregistraloscambiosenlosarchivosatravésdeltiempodeformatalque puedasaccederaversionesespecíficascuandolodesees.Esmuysimilaralaopciónde "registrarcambios"enMicrosoftWord,peromuchomáspoderoso. InstalarGit Windows PuedesdescargarGitdegit-scm.com.Puedeshacerclicen"Next"paratodoslospasos exceptoenuno;enelquintopasotitulado"AdjustingyourPATHenvironment",elije"RunGit andassociatedUnixtoolsfromtheWindowscommand-line"(laúltimaopción).Apartede ¡Desplegar! 63 DjangoGirlsTutorial eso,losvalorespordefectofuncionaránbien."CheckoutWindows-style,commitUnix-style lineendings"tambiénestábien. MacOS DescargaGitdegit-scm.comysiguelasinstrucciones. Linux Sinolotienesyainstalado,gitdeberíaestardisponibleatravésdeladministradorde paquetes,pruebacon: sudoapt-getinstallgit #o sudoyuminstallgit IniciarnuestrorepositorioGit Gitrastrealoscambiosrealizadosaungrupodeterminadodeficherosenloquellamamos unrepositoriodecódigo(o"repo"paraabreviar).Iniciemosunoparanuestroproyecto.Abre laconsolayejecutalossiguientescomandoseneldirectoriode djangogirls: Nota:Compruebaeldirectoriodetrabajoactualconelcomando pwd(OSX/Linux)o cd(Windows)antesdeinicializarelrepositorio.Deberíasestarenlacarpeta djangogirls. $gitinit InitializedemptyGitrepositoryin~/djangogirls/.git/ $gitconfiguser.name"Tunombre" $gitconfiguser.emailtú@ejemplo.com Inicializarelrepositoriogitesalgoquesólonecesitamoshacerunavezporproyecto(yno tendrásquevolveraponertuusuarioycorreoelectróniconuncamás) Gitllevaráunregistrodeloscambiosrealizadosentodoslosficherosycarpetaseneste directorio,perohayalgunosficherosquequeremosqueignore.Estolohacemoscreando unficherollamado .gitignoreeneldirectoriobase.Abretueditorycreaunnuevofichero conelsiguientecontenido: ¡Desplegar! 64 DjangoGirlsTutorial *.pyc __pycache__ myvenv db.sqlite3 .DS_Store Yguárdalocomo .gitignoreenlaprimeracarpeta"djangogirls". Nota:¡Elpuntoalprincipiodelnombredelficheroesimportante!Sitienesdificultades paracrearlo(alosMacnolesgustaquecreesficherosqueempiezanporpuntodesde Finder,porejemplo),usalaopción"Guardarcomo"entueditor,esonofalla. Esbuenaideautilizarelcomando gitstatusantesde gitaddocuandonoestéssegura deloquevaahacer,paraevitarcualquiersorpresa(porejemplo,añadirohacercommitde ficherosnodeseados).Elcomando gitstatusdevuelveinformaciónsobrelosficherossin seguimiento(untracked),modificados,preparados(staged),elestadodelaramaymucho más.Lasalidadeberíasersimilara: $gitstatus Onbranchmaster Initialcommit Untrackedfiles: (use"gitadd<file>..."toincludeinwhatwillbecommitted) .gitignore blog/ manage.py mysite/ nothingaddedtocommitbutuntrackedfilespresent(use"gitadd"totrack) Yfinalmenteguardamosnuestroscambios.Vealaconsolayejecutaestoscomandos: $gitadd-A. $gitcommit-m"MiappDjangoGirls,primercommit" [...] 13fileschanged,200insertions(+) createmode100644.gitignore [...] createmode100644mysite/wsgi.py EnviarnuestrocódigoaGitHub ¡Desplegar! 65 DjangoGirlsTutorial VisitaGitHub.comyregistraunanuevacuentadeusuariogratuita.Luego,creaunnuevo repositorioconelnombre"my-first-blog".Dejadesmarcadalaopción"Initialisewitha README",dejalaopción.gitignoreenblanco(lohemoshechoamano)ydejalalicencia como"None". NotaElnombre my-first-blogesimportante.Podríaselegirotracosa,perovaa aparecermuchasvecesenlasinstruccionesquesiguenytendríasquesustituirlocada vez.Probablementeseamássencilloquedarteconelnombre my-first-blog. EnlapróximapantallaveráslaURLparaclonarturepositorio.Eligelaversión"HTTPS", cópialayenunmomentolapegaremosenlaconsola: ¡Desplegar! 66 DjangoGirlsTutorial AhoratenemosqueconectarelrepositorioGitdetuordenadorconelqueestáenGitHub. $gitremoteaddoriginhttps://github.com/<your-github-username>/my-first-blog.git $gitpush-uoriginmaster EscribetunombredeusuarioycontraseñadeGitHubydeberíasveralgoasí: Usernamefor'https://github.com':hjwp Passwordfor'https://[email protected]': 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. TucódigoestáahoraenGitHub.¡Veymíralo!Verásqueestáenbuenacompañía;Django, elTutorialdeDjangoGirlsymuchosotrosgrandesproyectosdecódigoabiertotambién alojansucódigoenGitHub:) Configurarnuestroblogen PythonAnywhere Eshoraderegistrarunacuentagratuitadetipo"Beginner"enPythonAnywhere. www.pythonanywhere.com Nota:CuandoelijastunombredeusuariotenencuentaquelaURLdetublogtendrá laforma nombredeusuario.pythonanywhere.com,asíqueobienelijetupropioapodoo bienunnombrequedescribasobrequétratatublog. BajarnuestrocódigoenPythonAnywhere CuandotehayasregistradoenPythonAnywhereserásredirigidaatupaneldecontrolo página"Consoles".Elijelaopciónparainiciarunaconsola"Bash",queeslaversión PythonAnywheredeunaconsola,comolaquetienesentuPC Nota:PythonAnywhereestábasadoenLinux,porloquesiestásenWindowsla consolaseráunpocodistintaalaquetienesentuordenador. ¡Desplegar! 67 DjangoGirlsTutorial DescarguemosnuestrocódigodesdeGitHubaPythonAnywheremediantelacreacióndeun "clon"delrepositorio.EscribelosiguienteenlaconsoladePythonAnywhere: $gitclonehttps://github.com/<tu-usuario-github>/my-first-blog.git EstovaadescargarunacopiadetucódigoenPythonAnywhere.Compruébaloescribiendo: $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 CrearunvirtualenvenPythonAnywhere Talycomohicisteentupropioordenador,puedescrearunvirtualenvenPythonAnywhere. EnlaconsolaBash,escribe: 20:20~$cdmy-first-blog 20:20~$virtualenv--python=python3.4myvenv Runningvirtualenvwithinterpreter/usr/bin/python3.4 [...] Installingsetuptools,pip...done. 20:20~$sourcemyvenv/bin/activate (mvenv)20:20~$pipinstalldjangowhitenoise Collectingdjango [...] Successfullyinstalleddjango-1.8whitenoise-1.0.6 Recopilarficherosestáticos ¡Desplegar! 68 DjangoGirlsTutorial ¿Teestabaspreguntandoquéesesode"whitenoise"?Esunaherramientaparaservirlos llamados"ficherosestáticos".Losficherosestáticosfuncionandedistintaformaenlos servidoresencomparaciónconcómolohacenennuestropropioordenadorynecesitamos unaherramientacomo"whitenoise"paraservirlos. Aprenderemosunpocomássobrelosficherosestáticosmásadelante,cuandoeditemosel CSSdenuestrositio. Porahorasólonecesitamosejecutarenelservidoruncomandoadicionalllamado "collectstatic".LediceaDjangoquerecopiletodoslosficherosestáticosquenecesitaenel servidor.Porelmomento,principalmentesonlosficherosestáticosquehacenqueelpanel deadministraciónestébonito. 20:20~$pythonmanage.pycollectstatic Youhaverequestedtocollectstaticfilesatthedestination locationasspecifiedinyoursettings: /home/edith/my-first-blog/static Thiswilloverwriteexistingfiles! Areyousureyouwanttodothis? Type'yes'tocontinue,or'no'tocancel:yes Escribe"yes",¡yahíva!¿Noteencantahacerquelascomputadorasimprimanpáginasy páginasdetextoimposibledeentender?Siemprehagoruiditosparaacompañarlo.Brp,brp brp... Copying'/home/edith/.virtualenvs/mvenv/lib/python3.4/site-packages/django/contrib/admin/static/admin Copying'/home/edith/.virtualenvs/mvenv/lib/python3.4/site-packages/django/contrib/admin/static/admin [...] Copying'/home/edith/.virtualenvs/mvenv/lib/python3.4/site-packages/django/contrib/admin/static/admin Copying'/home/edith/.virtualenvs/mvenv/lib/python3.4/site-packages/django/contrib/admin/static/admin 62staticfilescopiedto'/home/edith/my-first-blog/static'. CrearlabasededatosenPythonAnywhere Aquíhayotracosaqueesdiferenteentretuordenadoryelservidor:ésteutilizaunabase dedatosdiferente.Porlotanto,lascuentasdeusuarioylasentradaspuedenserdiferentes enelservidoryentuordenador. Asíqueinicializamoslabasededatosenelservidorigualquelohicimosennuestro ordenador,con migratey createsuperuser: ¡Desplegar! 69 DjangoGirlsTutorial (mvenv)20:20~$pythonmanage.pymigrate Operationstoperform: [...] Applyingsessions.0001_initial...OK (mvenv)20:20~$pythonmanage.pycreatesuperuser Publicarnuestroblogcomounaaplicación web AhoraquenuestrocódigoestáenPythonAnywhere,elvirtualenvestálisto,losficheros estáticoshansidorecopiladosylabasededatosestáinicializada,estamoslistaspara publicarlacomounaaplicaciónweb. HazclicenellogodePythonAnywhereparavolveralpanelprincipal,hazclicenlapestaña WebypinchaenAddanewwebapp. Enlaventanadediálogo,despuésdeconfirmarelnombrededominio,elijemanual configuration(configuraciónmanual)(NBlaopción"Django"no).Luego,elijePython3.4y hazclicen"Next"paraterminarconelasistente. Notaasegúratedeelegirlaopciónde"Manualconfiguration",nolade"Django". SomosdemasiadobuenasparalaconfiguraciónpordefectodeDjangode PythonAnywhere;-) Configurarelvirtualenv SerásredirigidaalapantalladeconfiguracióndePythonAnywhereparatuaplicaciónweb,a laquedeberásaccedercadavezquequierashacercambiosenlaaplicacióndelservidor. ¡Desplegar! 70 DjangoGirlsTutorial Enlasección"Virtualenv",hazcliceneltextorojoquedice"Enterthepathtoavirtualenv" (Introducelarutaaunvirtualenv)yescribe: /home/<-tu-usuario->/my-first-blog/myvenv/ Nota:sustituyetupropionombredeusuariocomocorresponda.Sicometesunerror, PythonAnywheretemostraráunapequeñaadvertencia. ConfigurarelficheroWSGI Djangofuncionautilizandoel"protocoloWSGI",unestándarparaservirsitioswebusando Python,quePythonAnywheresoporta.LaformadeconfigurarPythonAnywhereparaque reconozcanuestroblogDjangoeseditarunficherodeconfiguraciónWSGI. Hazclicenelenlace"WSGIconfigurationfile"(enlasección"Code"enlapartedearribade lapágina;sellamaráalgoparecidoa /var/www/<tu-usuario>_pythonanywhere_com_wsgi.py)y teredirigiráaleditor. Eliminatodoelcontenidoactualyreemplázaloconalgocomoesto: ¡Desplegar! 71 DjangoGirlsTutorial importos importsys path='/home/<tu-usuario>/my-first-blog'#aquíutilizatupropiousuario ifpathnotinsys.path: sys.path.append(path) os.environ['DJANGO_SETTINGS_MODULE']='mysite.settings' fromdjango.core.wsgiimportget_wsgi_application fromwhitenoise.djangoimportDjangoWhiteNoise application=DjangoWhiteNoise(get_wsgi_application()) Notanoolvidessustituirtupropionombredeusuariodondedice <tu-usuario> EsteficheroseencargadedecirleaPythonAnywheredóndevivenuestraaplicaciónweby cómosellamaelficherodeconfiguracióndeDjango.Tambiénconfiguralaherramientapara ficherosestáticos"whitenoise". DaleaSaveyvuelvealapestañaWeb. ¡Todolisto!DalealbotónverdegrandequediceReloadypodrásvertuaplicación.Verásun enlaceaellaenlapartedearribadelapágina. Consejosdedepuración Siapareceunerrorcuandointentasvisitartusitio,elprimerlugarquedeberásrevisarpara obtenerinformacióndedepuracióneselerrorlog;encontrarásunenlaceaélenlapestaña WebdePythonAnywhere.Miraaversihayalgúnmensajedeerrorahí.Losmásrecientes estánalfinal.Losproblemasmáscomunesincluyen olvidaralgunodelospasosquehicimosenlaconsola:crearelvirtualenv,activarlo, instalarDjangoenél,ejecutarcollectstatic,inicializarlabasededatos cometerunerrorenlarutadelvirtualenvenlapestañaWeb;suelehaberunmensajito deerrordecolorrojo,sihayalgúnproblema cometerunerrorenelficherodeconfiguraciónWSGI;¿haspuestobienlarutaala carpetamy-first-blog? ¡Tututorestáahíparaayudar! ¡Estásenvivo! ¡Desplegar! 72 DjangoGirlsTutorial Lapáginapordefectodetusitiodeberíadecir"WelcometoDjango",igualqueentuPC local.Intentaañadir /admin/alfinaldelaURLyteredirigiráalpaneldeadministración. Ingresacontunombredeusuarioycontraseñayverásquepuedesañadirnuevasentradas enelservidor. DateunaGRANpalmadaenlaespalda;losdesplieguesenelservidorsonunadelas partesmáscomplejasdeldesarrollowebymuchasvecesalagentelecuestavariosdías tenerlofuncionando.Perotútienestusitioenvivo,enInternetdeverdad,¡asícomosuena! ¡Desplegar! 73 DjangoGirlsTutorial Djangourls Vamosaconstruirnuestraprimerapáginaweb--¡unapáginadeinicioparatublog!Pero primero,vamosaaprenderunpocosobreDjangourls. ¿QuéesunaURL? UnaURLessimplementeunadirecciónweb,puedesverunaURLcadavezquevisitas cualquiersitioweb-esvisibleenlabarradedireccionesdetunavegador(¡Sí! 127.0.0.1:8000esunaURL.Yhttp://djangogirls.comestambiénunaURL): CadapáginaenInternetnecesitasupropiaURL.Deestamaneratuaplicaciónsabeloque debemostraraunusuarioqueabreunaURL.EnDjangoseusaalgollamado URLconf (configuracióndeURL),unconjuntodepatronesqueDjangointentaráhacercoincidirconla direcciónURLrecibidaparaencontrarlavistacorrecta. ¿CómofuncionanlasURLsenDjango? Vamosaabrirelarchivo mysite/urls.pyyvercómoes: fromdjango.conf.urlsimportinclude,url fromdjango.contribimportadmin urlpatterns=[ #Examples: #url(r'^$','mysite.views.home',name='home'), #url(r'^blog/',include('blog.urls')), url(r'^admin/',include(admin.site.urls)), ] Comopuedesver,Djangoyapusoalgoaquíparanosotros. Djangourls 74 DjangoGirlsTutorial Laslíneasquecomienzancon #soncomentarios-significaqueesaslíneasnoserán ejecutadasporPython.Muyútil,¿verdad? YaestáaquílaURLdeadmin,quevisitasteenelcapítuloanterior: url(r'^admin/',include(admin.site.urls)), EstosignificaqueparacadaURLqueempiezacon admin/Djangoencontrarásu correspondienteview.EnestecasoestamosincluyendoenunasolalíneamuchasURLsde admin,asínoestátodocomprimidoenestepequeñoarchivo-esmáslimpioylegible. Regex ¿TepreguntascómoDjangocoincidelasdireccionesURLconlasvistas?Bueno,estaparte esdifícil.Djangoutiliza regex--expresionesregulares.Regextienemuchas(¡unmontón!) denormasqueformanunpatróndebúsqueda.Dadoquelasexpresionesregularessonun temaavanzado,noentraremosendetallessobresufuncionamiento. Siteinteresaentendercómocreamosesospatrones,aquíhayunejemplodelprocesosolamentenecesitaremosunsubconjuntodereglaslimitadoparaexpresarelpatrónque estamosbuscando: ^denotaelprincipiodeltexto $denotaelfinaldeltexto \drepresentaundígito +indicaqueelítemanteriordeberíaserrepetido<strong>porlomenos</strong>unavez ()paraencerrarunapartedelpatrón CualquierotracosaenladefinicióndelURLserátomadaliteralmente. Ahoraimaginaquetienesunsitiowebconunadireccióncomoesta: http://www.mysite.com/post/12345/,donde 12345eselnúmerodepost. Escribirvistasseparadasparatodoslosnúmerosdepostseríarealmentemolesto.Conlas expresionesregularespodemoscrearunpatrónquecoincidirálaURLyextraeráelnúmero paranosotras: ^post/(\d+)/$.Analicemosestaexpresiónparteporparteparaentender quéesloqueestamoshaciendoaquí: ^post/leestádiciendoaDjangoquetomecualquiercosaquetenga post/alprincipio delURL(justoantesde ^) (\d+)significaquehabráunnúmero(deunoomásdígitos)yquequeremosqueese númeroseacapturadoyextraído Djangourls 75 DjangoGirlsTutorial /lediceaDjangoqueotrocaracter /deberíaveniracontinuación $indicaelfinaldelURL,loquesignificaquesólocadenasfinalizandocon / coincidiránconestepatrón ¡TuprimerURLdeDjango! ¡EshoradecrearnuestroprimerURL!Queremosque'http://127.0.0.1:8000/'sealapágina deiniciodenuestroblogyquemuestreunalistadeposts. Tambiénqueremosmantenerelarchivo mysite/urls.pylimpio,asíqueimportaremosURLs denuestro blogalarchivo mysite/urls.pyprincipal. Eliminalaslíneascomentadas(líneascomenzandocon #)yagregaunalíneaque importará blog.urlsenelurlprincipal( ''). Tuarchivo mysite/urls.pydeberíaversecomoeste: fromdjango.conf.urlsimportinclude,url fromdjango.contribimportadmin urlpatterns=[ url(r'^admin/',include(admin.site.urls)), url(r'',include('blog.urls')), ] Djangoahoraredirigirátodoloquevayahacia'http://127.0.0.1:8000/'a blog.urlsy buscarámásinstruccionesallí. CuandoescribesexpresionesregularesenPythonacostúmbrateaponer ralprincipiode lacadena-estoessolamenteunapistaparaquePythonentiendaquelacadenacontenerá caracteresespecialesquenosonparaserinterpretadosporPythonsinoquesonpartede laexpresiónregular. blog.urls Creaunnuevoarchivovacío blog/urls.py.¡Muybien!Agregaestasprimerasdoslíneas: fromdjango.conf.urlsimportinclude,url from.importviews AquísoloestamosimportandolosmétodosdeDjangoytodasnuestras viewsdel blog (todavíanotenemosninguna,peroloharemosenunminuto) Djangourls 76 DjangoGirlsTutorial Luegodeesto,podemosagregarnuestroprimerpatrónURL: urlpatterns=[ url(r'^$',views.post_list), ] Comopuedesver,ahoraestamosasignandouna viewllamada post_listalURL ^$. Estaexpresiónregularcoincidirácon ^(uninicio)seguidode $(unfinal)-porlotanto, sólounacadenavacíacoincidirá.Yestoescorrecto,yaqueenlosURLresolversde Django'http://127.0.0.1:8000/'noespartedelURL.EstepatrónmostraráaDjangoque views.post_listesellugarcorrectoalqueirsialguieningresaatusitiowebconla dirección'http://127.0.0.1:8000/'. ¿Todobien?Abrehttp://127.0.0.1:8000/entunavegadorparaverelresultado. Nohaymásun"Itworks",¿verdad?Notepreocupes,essolamenteunapáginadeerror, ¡nadaquenosasuste!Dehecho,sonbastanteútiles: Puedesleerquenohayningúnatributo'post_list'.¿post_listterecuerdaalgo?¡Asíes comollamamosanuestravista!Estosignificaquetodoestáensulugar,sóloqueno creamosnuestraviewtodavía.Notepreocupes,yallegaremosaeso. SiquieressabermássobreDjangoURLconfs,miraladocumentaciónoficial: https://docs.djangoproject.com/en/1.8/topics/http/urls/ Djangourls 77 DjangoGirlsTutorial VistasdeDjango-¡Eshoradecrear! Eshoradedeshacersedelerrorquehemoscreadoenelcapítuloanterior:) UnaViewesunlugardondeponemosla"lógica"denuestraaplicación.Sesolicitará informacióndel modelquecreasteanteriormenteysepasaráauna viewquecrearásen elpróximocapítulo.LasvistassonsólométodosdePythonquesonunpocomás complicadosqueloquehicimosenelcapítulodeIntroducciónaPython. LasVistassecolocanenelarchivo views.py.Agregaremosnuestrasviewsalarchivo blog/views.py. blog/views.py Bien,vamosabrirestearchivoyverloquecontiene: fromdjango.shortcutsimportrender #Createyourviewshere. Nodemasiadascosasaquítodavía.Laviewmássimplepuedesercomoesto: defpost_list(request): returnrender(request,'blog/post_list.html',{}) Comopuedesver,hemoscreadounmétodo( def)llamado post_listquetomaun requestyhaceun returndeunmétodo renderquerenderizará(construirá)nuestra plantilla blog/post_list.html. Guardaelarchivo,dirígeteahttp://127.0.0.1:8000/yveamosloquetenemosahora. ¡Otroerror!Leamosloqueestápasandoahora: VistasdeDjango-¡Eshoradecrear! 78 DjangoGirlsTutorial Esteesunofácil:TemplateDoesNotExist.¡Vamosaarreglaresteerrorcreandounaplantilla enelsiguientecapítulo! AprendemásacercadelasviewsdeDjangoleyendoladocumentaciónoficial: https://docs.djangoproject.com/en/1.8/topics/http/views/ VistasdeDjango-¡Eshoradecrear! 79 DjangoGirlsTutorial IntroducciónaHTML Teestaráspreguntando,¿quéesunaplantilla? Unaplantillaesunarchivoquepodemosreutilizarparapresentarinformacióndiferentede formaconsistente-porejemplo,sepodríautilizarunaplantillaparaayudarteaescribiruna carta,porqueaunquecadacartapuedecontenerunmensajedistintoydirigirseauna personadiferente,compartiránelmismoformato. ElformatodeunaplantilladeDjangosedescribeenunlenguajellamadoHTML(queesel códigoHTMLquemencionamosenelprimercapítuloCómofuncionaInternet). ¿QuéesHTML? HTMLesunsimplecódigoqueesinterpretadoportunavegadorweb-comoChrome, FirefoxoSafari-paramostrarunapáginawebalusuario. HTMLsignificaHyperTextMarkupLanguage-enespañol,LenguajedeMarcasde HyperTexto.HyperTextsignificaqueesuntipodetextoquesoportahipervínculosentre páginas.Markupsignificaquehemostomadoundocumentoylohemosmarcadocon códigoparadecirleaalgo(enestecaso,unnavegador)cómointerpretarlapágina.El códigoHTMLestáconstruidoconetiquetas,cadaunacomenzandocon <yterminando con >.Estasetiquetasdemarcadosonelementos. ¡Tuprimeraplantilla! Crearunaplantillasignificacrearunarchivodeplantilla.Todoesunarchivo,¿verdad? Probablementehayasnotadoestoya. Lasplantillasseguardaneneldirectoriode blog/templates/blog.Asíqueprimerocreaun directoriollamado templatesdentrodetudirectorioblog.Luegocreaotrodirectorio llamado blogdentrodetudirectoriodetemplates: blog └───templates └───blog IntroducciónaHTML 80 DjangoGirlsTutorial (Talveztepreguntesporquénecesitamosdosdirectoriosllamados blog-como descubrirásmásadelante,estoessimplementeunaútilconvencióndenomenclaturaque hacelavidamásfácilcuandolascosasempiezanacomplicarsemás.) Yahoracreaunarchivo post_list.html(déjaloenblancoporahora)dentrodelacarpeta blog/templates/blog. Miracómosevesusitiowebahora:http://127.0.0.1:8000/ Sitodavíatienesunerror TemplateDoesNotExists,intentareiniciarelservidor.Veala líneadecomandos,deténelservidorpulsandoCtrl+C(teclasControlyCjuntas)y comienzadenuevomediantelaejecucióndelcomando pythonmanage.pyrunserver. ¡Ningúnerrormás!Felicidades:)Sinembargo,porahora,tusitiowebnoestápublicando nadaexceptounapáginaenblanco,porquelaplantillatambiénestávacía.Tenemosque arreglarlo. Añadelosiguienteatuarchivodeplantilla: <html> <p>Hithere!</p> <p>Itworks!</p> </html> ¿Cómoluceahoratusitioweb?Hazclickparaver:http://127.0.0.1:8000/ ¡Funcionó!Buentrabajo:) Laetiquetamásbásica, <html>,essiempreelprincipiodecualquierpáginaweby </html>essiempreelfinal.Comopuedesver,todoelcontenidodelapáginawebva desdeelprincipiodelaetiqueta <html>yhastalaetiquetadecierre </html> <p>esunaetiquetaparaloselementosdepárrafo; </p>cierracadapárrafo Cabeza&cuerpo CadapáginaHTMLtambiénsedivideendoselementos:headybody. headesunelementoquecontieneinformaciónsobreeldocumentoquenosemuestra enlapantalla. bodyesunelementoquecontienetodoloquesemuestracomopartedelapágina web. IntroducciónaHTML 81 DjangoGirlsTutorial Usamos <head>paradecirleelnavegadoracercadelaconfiguracióndelapáginay <body>paradecirloquerealmenteestáenlapágina. Porejemplo,puedesponerleuntítuloalapáginawebdentrodela <head>,así: <html> <head> <title>Ola'sblog</title> </head> <body> <p>Hithere!</p> <p>Itworks!</p> </body> </html> Guardaelarchivoyactualizatupágina. ¿Observascómoelnavegadorhacomprendidoque"Ola'sblog"eseltítulodetupágina? Hainterpretado <title>Ola'sblog</title>ycolocóeltextoenlabarradetítulodetu navegador(tambiénseutilizaráparamarcadoresyasísucesivamente). Probablementetambiénhayasnotadoquecadaetiquetadeaperturacoincideconuna etiquetadecierre,conun /,yqueloselementossonanidados(esdecir,nopuedescerrar unaetiquetaparticularhastaquetodoslosqueestabanensuinteriorsehayancerrado también). Escomoponercosasencajas.Tienesunacajagrande, <html></html>;ensuinteriorhay <body></body>,yquecontienelascajasaúnmáspequeñas: <p></p>. Tienesqueseguirestasreglasdeetiquetasdecierreydeanidacióndeelementos-sinolo haces,elnavegadorpuedenosercapazdeinterpretarloscorrectamenteytupáginase mostraráincorrectamente. Personalizatuplantilla ¡Ahorapuedesdivertirteunpocoytratardepersonalizartuplantilla!Aquíhayalgunas etiquetasútilesparaeso: <h1>Untítulo</h1>-paratutítulomásimportante <h2>Unsubtítulo</h2>-paraeltítulodelsiguientenivel <h3>Unsubsubtítulo</h3>-...yasíhasta <h6> <em>texto</em>-poneencursivatutexto <strong>texto</strong>-poneennegritatutexto IntroducciónaHTML 82 DjangoGirlsTutorial <br/>-unsaltodelínea(nopuedescolocarnadadentrodebr) <ahref="http://djangogirls.org">link</a>-creaunvínculo <ul><li>primerelemento</li><li>segundoelemento</li></ul>-creaunalista,¡igualque esta! <div></div>-defineunaseccióndelapágina Aquíhayunejemplodeunaplantillacompleta: <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.Don </div> <div> <p>published:14.06.2014,12:14</p> <h2><ahref="">Mysecondpost</a></h2> <p>Aeneaneuleoquam.Pellentesqueornaresemlaciniaquamvenenatisvestibulum.Don </div> </body> </html> Aquíhemoscreadotressecciones div. Elprimerelemento divcontieneeltítulodenuestroblog-esunencabezadoyun enlace Otrosdoselementos divcontienennuestrospostsconlafechadepublicación, h2 conuntítuloqueesclickeableydos p(párrafo)detexto,unoparalafechayunopara nuestropost. Nosdaesteefecto: ¡Yaaay!Perohastaelmomento,nuestraplantillasólomuestraexactamentelamisma información-considerandoqueanteshablábamosdeplantillascomopermitiéndonos mostrarinformacióndiferenteenelmismoformato. IntroducciónaHTML 83 DjangoGirlsTutorial Loquequeremosrealmenteesmostrarpostsrealesañadidosennuestrapáginade administracióndeDjango-yahíesadondevamosacontinuación. Unacosamás:¡despliega! SeríabuenovertodoestodisponibleenInternet,¿no?Hagamosotrodespliegueen PythonAnywhere: HazuncommitysubetucódigoaGitHub Enprimerlugar,veamosquéarchivoshancambiadodesdequehicimoseldesplieguepor últimavez: $gitstatus Asegúratedequeestáseneldirectorio djangogirlsyvamosadecirlea gitqueincluya todosloscambiosdentrodeestedirectorio: $gitadd-A. Nota -A(abreviaturade"all")significaque gittambiénreconocerásihaseliminado archivos(pordefecto,sóloreconocearchivosnuevos/modificados).Tambiénrecuerda (delcapítulo3)que .significaeldirectorioactual. Antesdequesubamostodoslosarchivos,vamosaverquéesloque gitsubirá(todoslos archivosque gitcargarádeberíanaparecerenverde): $gitstatus Yacasiestamos,ahoraestiempodedecirlequeguardeestecambioensuhistorial.Vamos adarleun"mensajedecommit"dondedescribimosloquehemoscambiado.Puedes escribircualquiercosaquetegustaríaenestaetapa,peroesútilescribiralgodescriptivo paraquepuedesrecordarloquehashechoenelfuturo. $gitcommit-m"CambieelHTMLparalapágina." NotaAsegúratedeusarcomillasdoblesalrededordelmensajedecommit. Unavezquehicimosesto,subimos(push)nuestroscambiosaPythonAnywhere: IntroducciónaHTML 84 DjangoGirlsTutorial gitpush DescargatunuevocódigoaPythonAnywhereyactualizatu aplicaciónweb AbrelapáginadeconsolasdePythonAnywhereyveatuconsolaBash(ocomienza unanueva).Luego,ejecuta: $cd~/my-first-blog$gitpull[...] Ymiracómotucódigosedescarga.Siquierescomprobarqueyahaterminado,puedesira lapestañaFilesyvertucódigoenPythonAnywhere. Finalmente,dirígetealapestañaWebyseleccionaReloadentuaplicaciónweb. ¡Tuactualizacióndeberíaestarenlínea!Actualizatusitiowebenelnavegador.Ahora deberíaspodervertuscambios:) IntroducciónaHTML 85 DjangoGirlsTutorial ORMdeDjangoyQuerySets EnestecapítuloaprenderáscómoDjangoseconectaalabasededatosyalmacenalos datosenella.¡Vamosasumergirnos! ¿QuéesunQuerySet? UnQuerySetes,enesencia,unalistadeobjetosdeunmodelodeterminado.UnQuerySet tepermiteleerlosdatosdebasededatos,filtrarlosyordenarlos. Esmásfácildeaprenderconejemplos.Vamosaintentarlo,¿deacuerdo? Djangoshell Abrelaconsolayescribeestecomando: (myvenv)~/djangogirls$pythonmanage.pyshell Elresultadodeberíaser: (InteractiveConsole) >>> AhoraestásenlaconsolainteractivadeDjango.EscomolaconsoladePython,perocon untoquedemagiaDjango:).PuedesutilizartodosloscomandosPythonaquítambién,por supuesto. Vertodoslosobjetos Vamosamostrartodosnuestrospostsprimero.Puedeshacerloconelsiguientecomando: >>>Post.objects.all() Traceback(mostrecentcalllast): File"<console>",line1,in<module> NameError:name'Post'isnotdefined ¡Uy!Aparecióunerror.NosdicequenohayningúnobjetoPost.Estoescorrecto,¡nos olvidamosdeimportarloprimero! ORMdeDjango(Querysets) 86 DjangoGirlsTutorial >>>fromblog.modelsimportPost Estoessimple:importamoselmodelo Postde blog.models.Vamosaintentarmostrar todoslospostsnuevamente: >>>Post.objects.all() [<Post:myposttitle>,<Post:anotherposttitle>] Estaesunalistadelaspostscreadasanteriormente.Hemoscreadoestospostsusandola interfazdeladministradordeDjango.Sinembargo,ahoraqueremoscrearnuevosposts usandoPython,¿cómolohacemos? Crearobjetos EstaeslaformadecrearunnuevoobjetoPostenlabasededatos: >>>Post.objects.create(author=me,title='Sampletitle',text='Test') Perohayuningredientefaltante: me.Necesitamospasarunainstanciadelmodelo User comoautor.¿Cómohacemoseso? PrimeroimportemoselmodeloUser: >>>fromdjango.contrib.auth.modelsimportUser ¿Quéusuariostenemosennuestrabasededatos?Veamos: >>>User.objects.all() [<User:ola>] Esteeselsuperusuarioquecreamosanteriormente,Vamosaobtenerunainstanciadeese usuarioahora: me=User.objects.get(username='ola') Comopuedesver,hicimosun getdeun Userconel usernamequeseaiguala'ola'. ¡Genial!Acuérdatedeponertunombredeusuarioparaobtenertuusuario. Ahorafinalmentepodemoscrearnuestroprimerpost: ORMdeDjango(Querysets) 87 DjangoGirlsTutorial >>>Post.objects.create(author=me,title='Sampletitle',text='Test') ¡Hurra!¿Quieresprobarsifuncionó? >>>Post.objects.all() [<Post:Sampletitle>] Agregamásposts Ahorapuedesdivertirteunpocoyañadirmáspostsparavercómofunciona.Añade2ó3 másyavanzaalasiguienteparte. Filtrarobjetos UnaparteimportantedelosQuerySetseslahabilidadparafiltrarlos.Digamosque queremosencontrartodoslospostscuyoautoreselUserola.Usaremos filterenvezde allen Post.objects.all().Enlosparéntesisestableceremosquécondicióno conducionesdebencumplirseporunpostdelblogparaterminarennuestroqueryset.En nuestrocasosería authoresiguala me.LaformadeescribirloenDjangoes: author=me. Ahoranuestrobloquedecódigosevecomoesto: >>>Post.objects.filter(author=me) [<Post:Sampletitle>,<Post:Postnumber2>,<Post:My3rdpost!>,<Post:4thtitleofpost>] ¿Otalvezquerramosvertodoslospostsquecontenganlapalabra'title'enelcampo title? >>>Post.objects.filter(title__contains='title') [<Post:Sampletitle>,<Post:4thtitleofpost>] NotaHaydosguionesbajos( _)entre titley contains.DjangoORMutilizaesta sintaxisparasepararlosnombresdeloscampos("title")yoperacionesofiltros ("contains").Sisóloutilizasunguiónbajo,obtendrásunerrorcomo"FieldError:Cannot resolvekeywordtitle_contains". Tambiénpuedesobtenerunalistadetodoslospostspublicados.Lohacemosfiltrandolos postsquetienenelcampo published_dateenelpasado: fromdjango.utilsimporttimezone Post.objects.filter(published_date__lte=timezone.now())[] ORMdeDjango(Querysets) 88 DjangoGirlsTutorial Desafortunadamente,ningunodenuestrospostshansidopublicadostodavía.¡Vamosa cambiaresto!Primeroobténunainstanciadeunpostquequerramospublicar: >>>post=Post.objects.get(id=1) ¡Luegoutilizaelmétodo publishparapublicarlo! >>>post.publish() Ahoraintentaobtenerlalistadepostspublicadosnuevamente(presionalateclaconla flechahaciaarriba3vecesypresionaEnter): >>>Post.objects.filter(published_date__lte=timezone.now()) [<Post:Sampletitle>] Ordenandoobjetos LosQuerySetstambiéntepermitenordenarlalistadeobjetos.Intentemosordenarlosporel campo created_date: >>>Post.objects.order_by('created_date') [<Post:Sampletitle>,<Post:Postnumber2>,<Post:My3rdpost!>,<Post:4thtitleofpost>] Tambiénpodemosinvertirelordenamientoagregando -alprincipio: >>>Post.objects.order_by('-created_date') [<Post:4thtitleofpost>,<Post:My3rdpost!>,<Post:Postnumber2>,<Post:Sampletitle>] ¡Genial!¡Ahoraestáslistaparalasiguienteparte!Paracerrarlaconsola,tipea: >>>exit() $ ORMdeDjango(Querysets) 89 DjangoGirlsTutorial QuerysetsdeDjango Tenemosdiferentespiezasensulugar:elmodelo Postestádefinidoen models.py, tenemosa post_listen views.pyylaplantillaagregada.¿Perocómoharemosrealmente paraquenuestrospostsaparezcanennuestraplantillaHTML?Porqueesoesloque queremoshacer:tomaralgúncontenido(modelosguardadosenlabasededatos)y mostrarloadecuadamenteennuestraplantilla,¿no? Estoesexactamenteloquelasviewssesuponequehacen:conectarmodeloscon plantillas.Ennuestraview post_listnecesitaremostomarlosmodelosquedeseamos mostrarypasarlosaunaplantilla.Asíquebásicamenteenunaviewdecidimosqué (modelo)semostraráenunaplantilla. Muybien,ahora¿cómolohacemos? Necesitamosabrirnuestroarchivo blog/views.py.Hastaahoralaview post_listseve así: fromdjango.shortcutsimportrender defpost_list(request): returnrender(request,'blog/post_list.html',{}) ¿Recuerdascuandohablamosdeincluircódigoendiferentesarchivos?Ahoratenemosque incluirelmodeloquedefinimosenelarchivo models.py.Agregaremoslalínea from .modelsimportPostdelasiguienteforma: fromdjango.shortcutsimportrender from.modelsimportPost Elpuntodespuésde fromindicaeldirectorioactualolaaplicaciónactual.Como views.py y models.pyestánenelmismodirectorio,simplementeusamos .yelnombredelarchivo (sin .py).Ahoraimportamoselnombredelmodelo( Post). ¿Peroahoraquésigue?Paratomarpublicacionesrealesdelmodelo Post,necesitamos algollamado QuerySet(conjuntodeconsultas). QuerySet Datosdinámicosenplantillas 90 DjangoGirlsTutorial YadebesestarfamiliarizadaconlaformaenquefuncionanlosQuerySets.Hablamosde ellosenelcapítuloDjangoORM(QuerySets). Entoncesahoranosinteresaobtenerunalistadeentradasdelblogquehansidopublicadas yordenadaspor published_date(fechadepublicación),¿no?¡Yahicimosesoenel capítuloQuerySets! Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date') Ahorapondremosestebloquedecódigoenelarchivo blog/views.py,agregándoloala función defpost_list(request): 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',{}) ObservaquecreamosunavariableennuestroQuerySet: posts.Tómalacomoelnombre denuestroQuerySet.DeaquíenadelantevamosareferirnosalQuerySetconesenombre. LaúltimaparteespasarelQuerySet postsalaplantilla(veremoscómomostrarlaenel siguientecapítulo). Enlafunción renderyatenemoselparámetro request(todoloquerecibimosdelusuario viaInternet)yelarchivo 'blog/post_list.html'comoplantilla.Elúltimoparámetro,quese veasí: {}esuncampoenelquepodemosagregaralgunascosasparaquelaplantillalas use.Necesitamosnombrarlos(losseguiremosllamando 'posts'porahora:)).Sedebería verasí: {'posts':posts}.Observaquelapartequevaantesde :estáentrecomillas ''. Finalmentenuestroarchivo blog/views.pydeberíaverseasí: 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}) Datosdinámicosenplantillas 91 DjangoGirlsTutorial ¡Terminamos!AhoraregresemosanuestraplantillaymostremosesteQuerySet. SiquieresleerunpocomásacercadeQuerySetsenDjango,puedesdarleunvistazoa: https://docs.djangoproject.com/en/1.8/ref/models/querysets/ Datosdinámicosenplantillas 92 DjangoGirlsTutorial PlantillasdeDjango ¡Eshorademostraralgunosdatos!Djangonosproveelasútilestemplatetagsparaello. ¿Quésonlastemplatetags? Verás,enHTMLnopuedesrealmenteponercódigoPython,porquelosnavegadoresnolo entienden.EllossólosabenHTML.SabemosqueHTMLesalgoestático,mientrasque Pythonesmuchomásdinámico. DjangotemplatetagsnospermitentransferircosasdePythoncomocosasenHTML,así quetupuedesconstruirsitioswebdinámicosmásrápidoyfácil. Mostrarlaplantillapostlist Enelcapítuloanteriordimosanuestraplantillaunalistadepostsenlavariable posts. AhoralomostraremosenHTML. ParaimprimirunavariableenunaplantilladeDjango,utilizamosllavesdoblesconel nombredelavariabledentro,así: {{posts}} Pruebaestoentuplantilla blog/templates/blog/post_list.html(reemplazaelsegundoyel tercerpardeetiquetas <div></div>conlalínea {{posts}}),guardaelarchivoy actualizalapáginaparaverlosresultados: Comopuedesver,todoloqueobtenemosesesto: PlantillasdeDjango 93 DjangoGirlsTutorial [<Post:Misegundopost>,<Post:Miprimerpost>] EstosignificaqueDjangoloentiendecomounalistadeobjetos.¿Recuerdasde IntroducciónaPythoncómopodemosmostrarlistas?Sí,¡conlosciclosfor!Enuna plantilladeDjango,lohacesdeestamanera: {%forpostinposts%} {{post}} {%endfor%} Pruebaestoentuplantilla. ¡Funciona!Peroqueremosquesemuestrencomolospostsestáticosquecreamos anteriormenteenelcapítulodeIntroducciónaHTML.PuedesmezclarHTMLytemplate tags.Nuestro bodyseveráasí: <div> <h1><ahref="/">DjangoGirlsBlog</a></h1> </div> {%forpostinposts%} <div> <p>published:{{post.published_date}}</p> <h1><ahref="">{{post.title}}</a></h1> <p>{{post.text|linebreaks}}</p> </div> {%endfor%} Todoloqueponesentre {%for%}y {%endfor%}serepetiráparacadaobjetoenlalista. Actualizatupágina: PlantillasdeDjango 94 DjangoGirlsTutorial ¿Hasnotadoqueutilizamosunanotacióndiferenteestavez {{post.title}}o {{ post.text}}?Estamosaccediendoadatosencadaunodeloscamposdefinidosen nuestromodelo Post.Tambiénel |linebreaksestádirigiendoeltextodelospostsa travésdeunfiltroparaconvertirsaltosdelíneaenpárrafos. Unacosamás SeríabuenoversitusitiowebseguiráfuncionandoenlaInternetpública,¿verdad? IntentemosdesplegándolaenPythonAnywherenuevamente.Aquítedejamosunayuda memoria... Primero,subetucódigoaGitHub $gitstatus[...]$gitadd-A.$gitstatus[...]$gitcommit-m"Addedviewstocreate/edit blogpostinsidethesite."[...]$gitpush Luego,identifícateenPythonAnywhereyveatuconsolaBash(oempiezauna nueva),yejecuta: $cdmy-first-blog$gitpull[...] Finalmente,vealapestañaWebypresionaReloadentuaplicaciónweb.¡Tu actualizacióndeberíapoderverse! PlantillasdeDjango 95 DjangoGirlsTutorial ¡Felicidades!Ahorasigueadelante,tratadeagregarunnuevopostusandoelpanelde administradordeDjango(¡recuerdaañadirpublished_date!)yluegoactualizatupáginapara versiaparecetunuevopost. ¿Funcionacomounencanto?¡Estamosorgullosos!Aléjatedetucomputadoraporunrato, tehasganadoundescanso.:) PlantillasdeDjango 96 DjangoGirlsTutorial CSS-¡Hazlobonito! Nuestroblogtodavíasevebastantefeo,¿verdad?¡Eshoradehacerlobonito!Vamosa usarCSSparaeso. ¿QuéesCSS? CSS(CascadingStyleSheets,quesignifica'hojasdeestiloencascada')esunlenguaje utilizadoparadescribirelaspectoyelformatodeunsitiowebescritoenlenguajede marcado(comoHTML).Trátalocomomaquillajeparanuestrapáginaweb;). Peronoqueremosempezardecerootravez,¿verdad?Unavezmás,usaremosalgoque yahasidorealizadoporprogramadoresypublicadoenInternetdeformagratuita.Yasabes, reinventarlaruedanoesdivertido. ¡VamosausarBootstrap! BootstrapesunodelosframeworksHTMLyCSSmáspopularesparadesarrollarwebs bonitas:http://getbootstrap.com/ LoescribieronprogramadoresquetrabajabanparaTwitteryahoralodesarrollanvoluntarios detodoelmundo. InstalarBootstrap ParainstalarBootstraptienesqueañadirestoal <head>detufichero .html ( 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.cs Estonoañadeningúnficheroatuproyecto.Simplementeapuntaaficherosqueexistenen Internet.Adelante,abretusitiowebyactualizalapágina.¡Aquíestá! CSS-Hazlobonito 97 DjangoGirlsTutorial ¡Sevemuchomejor! FicherosestáticosenDjango Finalmentenosvamosafijarenestascosasquehemosestadollamandoficheros estáticos.LosficherosestáticossontodostusCSSeimágenes;ficherosquenoson dinámicos,porloquesucontenidonodependedelcontextodelapeticiónyserániguales paratodoslosusuarios. DóndeponerlosficherosestáticosparaDjango Comohasvistocuandohemosejecutado collectstaticenelservidor,Djangoyasabe dóndeencontrarlosficherosestáticosparalaaplicación"admin".Ahoranecesitamosañadir algunosficherosestáticosparanuestrapropiaaplicación, blog. Estoloconseguimoscreandounacarpetallamada staticdentrodelaaplicaciónblog: djangogirls ├──blog │├──migrations │└──static └──mysite Djangoencontraráautomáticamentecualquiercarpetaquesellame"static"dentrodelas carpetasdetusaplicacionesypodráutilizarsucontenidocomoficherosestáticos. ¡TuprimerficheroCSS! CSS-Hazlobonito 98 DjangoGirlsTutorial CreemosunficheroCSSahora,paraañadirtupropioestiloatupáginaweb.Crearun nuevodirectoriollamado cssdentrodetudirectorio static.Despuéscreaunnuevo ficherollamado blog.cssdentrodeestedirectorio css.¿Lista? djangogirls └───blog └───static └───css └───blog.css ¡EshoradeescribiralgodeCSS!Abreelfichero blog/static/css/blog.cssentueditorde código. NovamosaentrarmuchoenlapersonalizaciónyelaprendizajedeCSSaquíporquees bastantefácilylopuedesaprenderportucuentadespuésdeestetaller.Recomendamos enormementehacerestecursodeHTMLyCSSenCodecademyparaaprendertodoloque necesitassabersobrecómohacertussitioswebmásbonitosconCSS. Perovamosahacerunpocoalmenos.¿Talvezpodríamoscambiarelcolordenuestro título?Losordenadoresutilizancódigosespecialesparaentenderloscolores.Empiezan con #ylessiguen6letras(A-F)ynúmeros(0-9).Puedesencontrarcódigosdecolor,por ejemplo,aquí:http://www.colorpicker.com/.Tambiénpuedesutilizarcolorespredefinidos utilizandosunombreeninglés,como redy green. Entufichero blog/static/css/blog.cssdeberíasañadirelsiguientecódigo: h1a{ color:#FCA205; } h1aesunselectorCSS.Quieredecirqueestamosaplicandonuestrosestilosacualquier elemento aqueseencuentredentrodeunelemento h1(porejemplocuandotenemos encódigoalgocomo: <h1><ahref="">enlace</a></h1>).Enestecasoleestamosdiciendo quecambieelcolora #FCA205,queesnaranja.Porsupuesto,¡puedesponertupropio coloraquí! EnunficheroCSSdefinimoslosestilosparaloselementosdelficheroHTML.Los elementosseidentificanporelnombredelelemento(esdecir, a, h1, body),elatributo class(clase)oelatributo id(identificador).Classeidsonnombresqueleasignastú mismaalelemento.Lasclasesdefinengruposdeelementosylosidsapuntanaelementos específicos.Porejemplo,lasiguienteetiquetasepuedeidentificarconCSSusandoel nombre a,laclase external_linkoelid link_to_wiki_page: CSS-Hazlobonito 99 DjangoGirlsTutorial <ahref="http://en.wikipedia.org/wiki/Django"class="external_link"id="link_to_wiki_page" PuedesleermássobreselectoresdeCSSenw3schools. TambiénnecesitamosdecirleanuestraplantillaHTMLquehemosañadidoCSS.Abreel fichero blog/templates/blog/post_list.htmlyañadeestalíneajustoalprincipio: {%loadstaticfiles%} Aquísóloestamoscargandoficherosestáticos:).Luego,entreel <head>y </head>, despuésdelosenlacesalosficherosCSSdeBootstrap(elnavegadorleelosficherosenel ordenenqueestán,asíquenuestroficheropodríasobrescribirpartesdelcódigode Bootstrap),añadelasiguientelínea: <linkrel="stylesheet"href="{%static'css/blog.css'%}"> LeacabamosdedeciranuestraplantilladóndeseencuentranuestroficheroCSS. Ahoratuficherodeberíateneresteaspecto: {%loadstaticfiles%} <html> <head> <title>DjangoGirlsblog</title> <linkrel="stylesheet"href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min. <linkrel="stylesheet"href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-them <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|linebreaks}}</p> </div> {%endfor%} </body> </html> Deacuerdo,¡guardaelficheroyactualizaelsitio! CSS-Hazlobonito 100 DjangoGirlsTutorial ¡Buentrabajo!¿Quizánosgustaríadarleunpocodeaireanuestrositiowebyaumentar tambiénelmargenenelladoizquierdo?¡Vamosaintentarlo! body{ padding-left:15px; } AñadeestoatuCSS,guardaelficheroy¡miracómofunciona! ¿Quizápodríamospersonalizarlatipografíadeltítulo?Pegaestoenlasección <head>del fichero blog/templates/blog/post_list.html: <linkhref="http://fonts.googleapis.com/css?family=Lobster&subset=latin,latin-ext"rel CSS-Hazlobonito 101 DjangoGirlsTutorial EstalíneavaaimportarunatipografíallamadaLobsterdeGoogleFonts (https://www.google.com/fonts). Ahoraañadelalínea font-family:'Lobster';enelficheroCSS blog/static/css/blog.css dentrodelbloquededeclaración h1a(elcódigoentrellaves {y })yactualizala página: h1a{ color:#FCA205; font-family:'Lobster'; } ¡Genial! Comosemencionóanteriormente,CSStieneunconceptodeclasesquebásicamente permitenombrarunapartedelcódigoHTMLyaplicarestilossóloaestaparte,sinafectara otras.Esmuyútilsitienesdosdivsquehacenalgomuydiferente(comoelencabezadoyla entrada)ynoquieresquetenganelmismoaspecto. ¡Adelante!NombraalgunaspartesdelcódigoHTML.Añadeunaclasellamada pageheaderal divquecontieneelencabezado,así: <divclass="page-header"> <h1><ahref="/">DjangoGirlsBlog</a></h1> </div> Yahoraañadelaclase postal divquecontieneunaentradadelblog. CSS-Hazlobonito 102 DjangoGirlsTutorial <divclass="post"> <p>published:{{post.published_date}}</p> <h1><ahref="">{{post.title}}</a></h1> <p>{{post.text|linebreaks}}</p> </div> Ahoraañadiremosbloquesdedeclaraciónadiferentesselectores.Losselectoresque comienzancon .hacenreferenciaalasclases.Haymuchostutorialesyexplicaciones sobreCSSenlawebqueteayudaránaentenderelsiguientecódigo.Porahora, simplementecopiaypegaestebloquedecódigoentufichero blog/static/css/blog.css: CSS-Hazlobonito 103 DjangoGirlsTutorial .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{ float:right; 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; } LuegorodeaelcódigoHTMLquemuestralasentradasconlasdeclaracionesdeclases. Sustituyeesto: CSS-Hazlobonito 104 DjangoGirlsTutorial {%forpostinposts%} <divclass="post"> <p>published:{{post.published_date}}</p> <h1><ahref="">{{post.title}}</a></h1> <p>{{post.text|linebreaks}}</p> </div> {%endfor%} en blog/templates/blog/post_list.htmlporesto: <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|linebreaks}}</p> </div> {%endfor%} </div> </div> </div> Guardalosficherosyactualizatusitio. ¡Bien!Seveincreíble,¿verdad?Enrealidadelcódigoqueacabamosdepegarnoestan difícildeentenderydeberíassercapazdeentenderlamayoríasóloconleerlo. NotengasmiedodejugarunpococonesteCSSeintentarcambiaralgunascosas.Si rompesalgo,notepreocupes,¡siemprepuedesdeshacerlo! CSS-Hazlobonito 105 DjangoGirlsTutorial Decualquierforma,tevolvemosarecomendarquehagaselcursodeHTMLyCSSde Codeacademycomounatareapost-tallerparaqueaprendastodoloquenecesitassaber parahacertussitioswebmásbonitosconCSS. ¡¿Listaparaelsiguientecapítulo?!:) CSS-Hazlobonito 106 DjangoGirlsTutorial ExtendiendoPlantillas OtracosabuenaqueDjangotieneparatíeslaextensióndeplantillas.¿Quésignifica esto?SignificaquepuedesusarlasmismaspartesdetuHTMLparadiferentespáginasde tusitioweb. Deestaformanotienesquerepetirelcódigoencadaunodelosarchivoscuandoquieres usarunamismainformaciónounmismoesquema.Ysiquierescambiaralgo,nonecesitas hacerloencadaplantilla. Creandounaplantillabase Unaplantillabaseeslaplantillamásbásicaqueextiendesencadapáginadetusitioweb. Vamosacrearunarchivo base.htmlen blog/templates/blog/: blog └───templates └───blog base.html post_list.html Luegoábreloycopiatodoloquehayen post_list.htmlalarchivo base.html,dela siguientemanera: Extenderplantillas 107 DjangoGirlsTutorial {%loadstaticfiles%} <html> <head> <title>DjangoGirlsblog</title> <linkrel="stylesheet"href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min. <linkrel="stylesheet"href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-them <linkhref='//fonts.googleapis.com/css?family=Lobster&subset=latin,latin-ext' <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|linebreaks}}</p> </div> {%endfor%} </div> </div> </div> </body> </html> Luego,en base.htmlreemplazaporcompletotu <body>(todoloquehayaentre <body> and </body>)conesto: <body> <divclass="page-header"> <h1><ahref="/">DjangoGirlsBlog</a></h1> </div> <divclass="contentcontainer"> <divclass="row"> <divclass="col-md-8"> {%blockcontent%} {%endblock%} </div> </div> </div> </body> Extenderplantillas 108 DjangoGirlsTutorial Básicamenteremplazamostodoentre {%forpostinposts%}{%endfor%}con: {%blockcontent%} {%endblock%} ¿Quésignificaesto?Acabasdecrearun block,unatemplatetagquetepermiteinsertar HTMLenestebloqueenotrasplantillasqueextiendana base.html.Temostraremoscomo hacerestoenunmomento. Ahoraguárdaloyabretuarchivo blog/templates/blog/post_list.htmldenuevo.Elimina todoloquenoestédentrodelbodyyluegoeliminatambién <divclass="page-header"> </div>,deformaquetuarchivoseveráasi: {%forpostinposts%} <divclass="post"> <divclass="date"> {{post.published_date}} </div> <h1><ahref="">{{post.title}}</a></h1> <p>{{post.text|linebreaks}}</p> </div> {%endfor%} Yahoraagregaestalíneaaliniciodelarchivo: {%extends'blog/base.html'%} Significaqueahoraestamosextendiendodelaplantilla base.htmlen post_list.html. Sólonosfaltaunacosa:ponertodo(exceptolalíneaqueacabamosdeagregar)entre {% blockcontent%}y {%endblockcontent%}.Comoesto: {%extends'blog/base.html'%} {%blockcontent%} {%forpostinposts%} <divclass="post"> <divclass="date"> {{post.published_date}} </div> <h1><ahref="">{{post.title}}</a></h1> <p>{{post.text|linebreaks}}</p> </div> {%endfor%} {%endblockcontent%} Extenderplantillas 109 DjangoGirlsTutorial ¡Esoestodo!Verificaquetusitiowebaúnfuncioneapropiadamente:) Sitienesunerror TemplateDoesNotExistsquedigaquenohayunarchivo blog/base.htmlytienes runserverejecutándoseenlaconsola,intentapararlo (presionandoCtrl+C-lasteclasControlyCjuntas)yreinicialoejecutandoelcomando pythonmanage.pyrunserver. Extenderplantillas 110 DjangoGirlsTutorial Amplíatuaplicación Yahemoscompletadotodoslospasosnecesariosparalacreacióndenuestrositioweb: sabemoscómoescribirunmodel,url,viewytemplate.Tambiénsabemoscómohacerque nuestrositiowebsevealindo. ¡Horadepracticar! Loprimeroquenecesitamosennuestrobloges,obviamente,unapáginaparamostrarun post,¿cierto? Yatenemosunmodelo Post,asíquenonecesitamosañadirnadaa models.py. Creaunenlaceenlaplantilla Vamosaempezarañadiendounenlacedentrodelarchivo blog/templates/blog/post_list.html.Hastaelmomentodeberíaverseasí: {%extends'blog/base.html'%} {%blockcontent%} {%forpostinposts%} <divclass="post"> <divclass="date"> {{post.published_date}} </div> <h1><ahref="">{{post.title}}</a></h1> <p>{{post.text|linebreaks}}</p> </div> {%endfor%} {%endblockcontent%} Queremostenerunenlaceaunapáginadedetallesobreeltítulodelpost.Vamosacambiar <h1><ahref="">{{post.title}}</a></h1>dentrodelenlace: <h1><ahref="{%url'blog.views.post_detail'pk=post.pk%}">{{post.title}}</a></h1> Eshoradeexplicarelmisterioso {%url'blog.views.post_detail'pk=post.pk%}.Como probablementesospeches,lanotación {%%}significaqueestamosutilizandoDjango templatetags.¡EstavezvamosautilizarunoquevaacrearunadirecciónURLpara nosotros! Amplíatuaplicación 111 DjangoGirlsTutorial blog.views.post_detailesunarutahacia post_detailviewquequeremoscrear.Porfavor nota: blogeselnombredenuestraaplicación(el blogdedirectorio), viewsesel nombredelarchivo views.pyy post_detaileselnombredelaview. Ahoracuandovayamosa:http://127.0.0.1:8000/tendremosunerror(comoeradeesperar, yaquenotenemosunadirecciónURLounaviewpara post_detail).Severáasí: VamosacrearunaURLen urls.pyparanuestroview post_detail! URL:http://127.0.0.1:8000/post/1/ QueremoscrearunaURLqueapunteaDjangoaunaviewdenominada post_detail,que mostraráunaentradadelblog.Agregalalínea url(r'^post/(?P<pk>[0-9]+)/$', views.post_detail),alarchivo blog/urls.py.Deberíateneresteaspecto: ```fromdjango.conf.urlsimportinclude,urlfrom.importviews urlpatterns=[ url(r'^$',views.post_list), url(r'^post/(?P<pk>[0-9]+)/$',views.post_detail), ] Amplíatuaplicación 112 DjangoGirlsTutorial Esedaunpocodemiedo,peronotepreocupes-loexplicaremosparati:comienzacon`^`otravez," Esosignificaquesientrasen`http://127.0.0.1:8000/post/5/`entunavegador,Djangoentenderáque `pk`eslaabreviaciónde`primarykey`.EstenombreseutilizaamenudoenproyectosdeDjango.Pero ¡Bien!¡Actualizalapágina:http://127.0.0.1:8000/¡Boom!¡Sinembargovemosotroerror!Comoerade ![AttributeError][2] [2]:images/attribute_error2.png ¿Teacuerdasdelpróximopaso?Porsupuesto:¡agregarunaview! ##post_detailview Estaveznuestra*view*tomaráunparámetroadicional`pk`.¿Nuestra*view*necesitarecibirla,ciert Ahora,queremossólounpostdelblog.Paraellopodemosusarquerysetscomoeste: Post.objects.get(pk=pk) Peroestecódigotieneunproblema.Sinohayningún`Post`con`llaveprimaria`(`pk`)tendremosun ![DoesNotExisterror][3] [3]:images/does_not_exist2.png ¡Noqueremoseso!Pero,porsupuesto,Djangovieneconalgoqueseencargarádeeseproblemapornoso ![Pagenotfound][4] [4]:images/404_2.png Labuenanoticiaesquepuedescreartupropiapágina`PageNotFound`ydiseñarlacomodesees.Pero ¡Eshoradeagregaruna*view*anuestroarchivo`views.py`! Deberíamosabrir`blog/views.py`yagregarelsiguientecódigo: fromdjango.shortcutsimportrender,get_object_or_404 Cercadeotraslíneas`from`.Yenelfinaldelarchivoañadimosnuestra*view*: ```defpost_detail(request,pk): post=get_object_or_404(Post,pk=pk) returnrender(request,'blog/post_detail.html',{'post':post}) Amplíatuaplicación 113 DjangoGirlsTutorial Sí.Eshoradeactualizarlapágina:http://127.0.0.1:8000/ ¡Funcionó!Pero¿quépasacuandohacesclickenunenlaceeneltítulodelpost? ¡Ohno!¡Otroerror!Peroyasabemoscómolidiarconeso,¿no?¡Tenemosqueañadiruna plantilla! Crearemosunarchivoen blog/templates/blogllamado post_detail.html. Severáasí: {%extends'blog/base.html'%} {%blockcontent%} <divclass="post"> {%ifpost.published_date%} <divclass="date"> {{post.published_date}} </div> {%endif%} <h1>{{post.title}}</h1> <p>{{post.text|linebreaks}}</p> </div> {%endblock%} Unavezmásestamosextendiendo base.html.Enelbloque contentqueremosmostrarla fechadepublicación(siexiste),títuloytextodenuestrosposts.Perodeberíamosdiscutir algunascosasimportantes,¿cierto? {%if...%}...{%endif%}esuntemplatetagquepodemosusarcuandoquerramosver algo(¿recuerdasif...else...</code>delcapítulodeIntroducciónaPython?).Eneste escenarioqueremoscomprobarsielcampo published_datedeunpostnoestávacío. Bien,podemosactualizarnuestrapáginayversi PageNotFoundsehaido. ¡Yay!¡Funciona! Unacosamás:¡Tiempodeimplementación! SeríabuenoverificarquetusitiowebaúnfuncionaráenPythonAnywhere,¿cierto? Intentemosdesplegardenuevo. Amplíatuaplicación 114 DjangoGirlsTutorial $gitstatus $gitadd-A. $gitstatus $gitcommit-m"Addedviewstocreate/editblogpostinsidethesite." $gitpush Luego,enunaconsolaBashdePythonAnywhere $cdmy-first-blog$gitpull[...] Finalmente,vealapestañaWebyhazclickenReload. ¡Yesodeberíasertodo!Felicidades:) Amplíatuaplicación 115 DjangoGirlsTutorial FormulariosenDjango Loúltimoqueharemosennuestrowebsiteescrearunapartadoparaagregaryeditarposts enelblog.Django adminestábien,peroesbastantedifícildepersonalizaryhacerlo bonito.Con formstendremosunpoderabsolutosobrenuestrainterfaz-podemoshacer casicualquiercosaquepodamosimaginar! LobuenodeDjangoformsesquepodemosdefinirlodesdeceroocreandoun ModelFormy seguardaráelresultadodelformularioenelmodelo. Estoesexactamenteloquequeremoshacer:crearemosunformularioparanuestromodelo Post. ComocadaparteimportantedeDjango,formstienensupropioarchivo: forms.py. Tenemosquecrearunarchivoconestenombreeneldirectorio blog. blog └──forms.py Ok,vamosabrirloyvamosaescribirelsiguientecódigo: fromdjangoimportforms from.modelsimportPost classPostForm(forms.ModelForm): classMeta: model=Post fields=('title','text',) PrimeronecesitamosimportarDjangoforms( fromdjangoimportforms)y,obviamente, nuestromodelo Post( from.modelsimportPost). PostForm,comoprobablementesospechas,eselnombredelformulario.Necesitamos decirleaDjangoqueesteformularioesun ModelForm(asíDjangoharáalgodemagiapara nosotros)- forms.ModelFormesresponsabledeello. Acontinuación,tenemos classMeta,dondeledecimosaDjangoquémodelodebeutilizar paracrearesteformulario( model=Post). FormulariosenDjango 116 DjangoGirlsTutorial Finalmente,podemosdecirquecampo(s)queremosennuestroformulario.Eneste escenariosóloqueremos titley text- authorserálapersonaquesehaautenticado (¡tú!)y created_datesedefiniráautomáticamentecuandocreemosunpost(esdecirenel código),¿si? ¡Yesoestodo!Todoloquenecesitamoshacerahoraesusarelformularioenunaviewy mostrarlaenunaplantilla. Unavezmásvamosacrear:unenlacealapágina,unadirecciónURL,unavistayuna plantilla. Enlaceaunapáginaconelformulario Eshoradeabrir blog/templates/blog/base.html.Vamosaañadirunenlaceen div llamado page-header: <ahref="{%url'blog.views.post_new'%}"class="top-menu"><spanclass="glyphiconglyphicon-plus" Tenencuentaquequeremosllamaranuestranuevavista post_new. Despuésdeagregarlalínea,tuarchivohtmldeberíateneresteaspecto: FormulariosenDjango 117 DjangoGirlsTutorial {%loadstaticfiles%} <html> <head> <title>DjangoGirlsblog</title> <linkrel="stylesheet"href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min. <linkrel="stylesheet"href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-them <linkhref='//fonts.googleapis.com/css?family=Lobster&subset=latin,latin-ext' <linkrel="stylesheet"href="{%static'css/blog.css'%}"> </head> <body> <divclass="page-header"> <ahref="{%url'blog.views.post_new'%}"class="top-menu"><spanclass="glyphicongly <h1><ahref="/">DjangoGirlsBlog</a></h1> </div> <divclass="contentcontainer"> <divclass="row"> <divclass="col-md-8"> {%blockcontent%} {%endblock%} </div> </div> </div> </body> </html> Luegodeguardaryactualizarlapágina http://127.0.0.1:8000obviamenteverásunerror NoReverseMatchfamiliar,¿verdad? URL Abrimos blog/urls.pyyañadimosunalínea: url(r'^post/new/$',views.post_new,name='post_new'), Yelcódigofinaltendráesteaspecto: fromdjango.conf.urlsimportinclude,url from.importviews urlpatterns=[ url(r'^$',views.post_list), url(r'^post/(?P<pk>[0-9]+)/$',views.post_detail), url(r'^post/new/$',views.post_new,name='post_new'), ] FormulariosenDjango 118 DjangoGirlsTutorial Despuésdeactualizarelsitio,veremosun AttributeError,puestoquenotenemoslavista post_newimplementada.Vamosaañadirlaahora. Vistapost_new Eselmomentodeabrirelarchivo blog/views.pyyagregarlassiguienteslíneasalrestode lasfilas from: from.formsimportPostForm ynuestravista: defpost_new(request): form=PostForm() returnrender(request,'blog/post_edit.html',{'form':form}) Paracrearunnuevoformulario Post,tenemosquellamara PostForm()ypasarloala plantilla.Volveremosaestavistapero,porahora,vamosacrearrápidamenteunaplantilla paraelformulario. Plantilla Tenemosquecrearunarchivo post_edit.htmleneldirectorio blog/templates/blog.Para hacerqueunformulariofuncionenecesitamosvariascosas: tenemosquemostrarelformulario.Podemoshacerlo,porejemplo,conunsimple``. lalíneaanteriortienequeestardentrodeunaetiquetadeformularioHTML: <form method="POST">...</form> necesitamosunbotón Guardar.LohacemosconunbotónHTML: <button type='submit'>Save</button> yfinalmentejustodespuésdelaaperturade <form...>necesitamosagregar {% csrf_token%}.¡Estoesmuyimportanteyaquehacequetusformulariosseanseguros! Djangosequejarásiteolvidasdeestapartecuandointenteguardarelformulario. FormulariosenDjango 119 DjangoGirlsTutorial Bueno,vamosavercómoquedaráelHTMLen 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%} ¡Eshoradeactualizar!¡Si!¡Tuformulariosemuestra! FormulariosenDjango 120 DjangoGirlsTutorial Pero,¡unmomento!Siescribesalgoenloscampos títley textytratasdeguardarlos cambios-¿quépasará? ¡Nada!Unavezmásestamosenlamismapáginayeltextosehaido...noseañadeningún postnuevo.Entonces,¿quéhaidomal? Larespuestaes:nada.Tenemosquetrabajarunpocomásennuestravista. Guardarelformulario Abre blog/views.pyunavezmás.Actualmente,loquetenemosenlavista post_newes: defpost_new(request): form=PostForm() returnrender(request,'blog/post_edit.html',{'form':form}) Cuandoenviamoselformulariosomosredirigidosalamismavista,peroestaveztenemos algunosdatosadicionalesen request,másespecíficamenteen request.POST(elnombre notienenadaqueverconunpostdelblog,serefiereaqueestamos"publicando"-en inglés,posting-datos).¿RecuerdasqueenelarchivoHTMLladefiniciónde <form>tenía FormulariosenDjango 121 DjangoGirlsTutorial lavariable method="POST"?Todosloscamposdelformularioestanahoraen request.POST. Nodeberíasrenombrarlavariable POST(elúniconombrequetambiénesválidoparala variable methodes GET,peronotenemostiempoparaexplicarcuálesladiferencia). Ennuestraviewtenemosdosposiblessituacionesacontemplar.Primero:cuando accedemosalapáginaporprimeravezyqueremosunformularioenblanco.Segundo: cuandovolvemosalaviewconlosdatosdelformularioqueacabamosdeescribir.Asíque tenemosqueañadirunacondición(utilizaremos ifparaeso). ifrequest.method=="POST": [...] else: form=PostForm() Eshoradellenarlospuntos [...].Siel methodes POSTqueremosconstruirel PostFormconlosdatosdelformulario,¿no?Loharemoscon: form=PostForm(request.POST) Fácil!Losiguienteesverificarsielformularioescorrecto(todosloscamposnecesarios estándefinidosynohayvaloresincorrectos).Lohacemoscon form.is_valid(). Comprobamosqueelformularioesválidoy,siesasí,¡lopodemossalvar! ifform.is_valid(): post=form.save(commit=False) post.author=request.user post.save() Básicamente,tenemosquehacerdoscosasaquí:guardamoselformulariocon form.save yañadimosunautor(yaquenohabíaningúncampode authorenel PostFormyeste campoesobligatorio). commit=Falsesignificaquenoqueremosguardarelmodelo Post todavía-queremosañadirelautorprimero.Lamayoríadelasvecesutilizarás form.save(), sin commit=False,peroenestecaso,tenemosquehacerlo. post.save()conservarálos cambios(añadiendoaautor)ysecrearáunanuevopostenelblog. Porúltimo,seríagenialsipodemosinmediatamenteiralapágina post_detaildelnuevo postdeblog,¿no?Parahacerlonecesitamosimportaralgomás: fromdjango.shortcutsimportredirect FormulariosenDjango 122 DjangoGirlsTutorial Agrégaloalprincipiodelarchivo.Yahorapodemosdecir:véalapágina post_detaildel postreciéncreado. returnredirect('blog.views.post_detail',pk=post.pk) blog.views.post_detaileselnombredelavistaalaquequeremosir.¿Recuerdasque estaviewrequiereunavariable pk?Parapasarloalasvistasutilizamos pk=post.pk, donde posteselpostreciéncreado. Bien,hablamosmucho,peroprobablementequeremosvercomoseveahoralavista, ¿verdad? defpost_new(request): ifrequest.method=="POST": form=PostForm(request.POST) ifform.is_valid(): post=form.save(commit=False) post.author=request.user post.save() returnredirect('blog.views.post_detail',pk=post.pk) else: form=PostForm() returnrender(request,'blog/post_edit.html',{'form':form}) Vamosaversifunciona.Vealapáginahttp://127.0.0.1:8000/post/new/,añadeun titley un text,guardalo...¡yvoilà!Seañadeelnuevopostalblogysenosredirigealapágina de post_detail. Probablementehasvistoquenohemosdefinidolafechadepublicación.Vamosaintroducir unbotónpublicarenDjangoGirlsTutorial:Extensions. ¡Esoesgenial! Validacióndeformularios Ahora,vamosaenseñartequétanbuenoesDjangoforms.Unpostdelblogdebetenerlos campos titley text.Ennuestromodelo Postnodijimos(adiferenciade published_date)queestoscampossonrequeridos,asíqueDjango,pordefecto,espera queesténdefinidos. Tratadeguardarelformulariosin titley text.¡Adivinaquépasará! FormulariosenDjango 123 DjangoGirlsTutorial Djangoseencargadevalidarquetodosloscamposenelformularioesténcorrectos.¿No esgenial? ComorecientementehemosutilizadolainterfazdeadministradordeDjango,elsistema piensaqueestamosconectadas.Hayalgunassituacionesquepodríanllevarnosa desconectarnos(cerrandoelnavegador,reiniciandolabasededatos,etc.).Siestás recibiendoerroresalcrearunpostqueindicanlafaltadeiniciodesesióndeusuario, dirígetealapáginadeadministración http://127.0.0.1:8000/admineiniciasesión nuevamente.Estoresolveráelproblematemporalmente.Hayunarreglopermanente esperándoteenelcapítuloTarea:¡Añadirseguridadatusitioweb!despuésdel tutorialprincipal. FormulariosenDjango 124 DjangoGirlsTutorial Editarformulario Ahorasabemoscómoagregarunnuevoformulario.Pero,¿quépasasiqueremoseditaruno existente?Esmuysimilaraloqueacabamosdehacer.Vamosacrearalgunascosas importantesrápidamente(sinoentiendesalgo,pregúntaleatututororevisalocapítulos anteriores,sontemasqueyahemoscubierto). Abreelarchivo blog/templates/blog/post_detail.htmlyañadeestalínea: <aclass="btnbtn-default"href="{%url'post_edit'pk=post.pk%}"><spanclass="glyphiconglyphic paraquelaplantillaquede: {%extends'blog/base.html'%} {%blockcontent%} <divclass="date"> {%ifpost.published_date%} {{post.published_date}} {%endif%} <aclass="btnbtn-default"href="{%url'post_edit'pk=post.pk%}"><spanclass="glyphicongly </div> <h1>{{post.title}}</h1> <p>{{post.text|linebreaks}}</p> {%endblock%} Enelarchivo blog/urls.pyañadimosestalínea: url(r'^post/(?P<pk>[0-9]+)/edit/$',views.post_edit,name='post_edit'), Vamosareusarlaplantilla blog/templates/blog/post_edit.html,asíqueloúltimoquenos faltaesunaview. Abramoselarchivo blog/views.pyyañadamosalfinalestalínea: FormulariosenDjango 125 DjangoGirlsTutorial 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.save() returnredirect('blog.views.post_detail',pk=post.pk) else: form=PostForm(instance=post) returnrender(request,'blog/post_edit.html',{'form':form}) Estosevecasiexactamenteigualanuestraview post_new,¿no?Peronodeltodo. Primero:pasamosunparámetroextra pkdelosurls.Luego:obtenemoselmodelo Post quequeremoseditarcon get_object_or_404(Post,pk=pk)ydespués,alcrearelformulario pasamosestepostcomouna instanciatantoalguardarelformulario: form=PostForm(request.POST,instance=post) comoalabrirunformularioconestepostparaeditarlo: form=PostForm(instance=post) Ok,¡vamosaprobarsifunciona!Dirígetealapágina post_detail.Debehaberahíun botónparaeditarenlaesquinasuperiorderecha: FormulariosenDjango 126 DjangoGirlsTutorial Aldarclickahí,debesverelformularioconnuestropostdelblog: ¡Siéntetelibredecambiareltítulooeltextoyguardaloscambios! ¡Felicitaciones!¡Tuaplicaciónestácadavezmáscompleta! SinecesitasmásinformaciónsobrelosformulariosdeDjango,debesleerla documentación:https://docs.djangoproject.com/en/1.8/topics/forms/ Unacosamás:¡Tiempodeimplementación! VeamossitodoestofuncionaenPythonAnywhere.¡Tiempodehacerotrodespliegue! Primero,hazuncommitcontunuevocódigoysúbeloaGitHub $gitstatus$gitadd-A.$gitstatus$gitcommit-m"Addedviewstocreate/editblog postinsidethesite."$gitpush Luego,enunaconsolaBashdePythonAnywhere $cdmy-first-blog$gitpull[...] Finalmente,vealapestañaWebyhazclickenReload. FormulariosenDjango 127 DjangoGirlsTutorial ¡Yesodeberíasertodo!Felicidades:) FormulariosenDjango 128 DjangoGirlsTutorial Dominio PythonAnywheretehadadoundominiogratuito,perotalveznoquierastener ".pythonanywhere.com"alfinaldelaURLdetublog.Quizásquieraquetublogvivaen "www.infinite-kitten-pictures.org"o"www.3d-printed-steam-engine-parts.com"o "www.antique-buttons.com"o"www.mutant-unicornz.net",oloquequierasquesea. Aquíhablaremosbrevementesobrecómoobtenerundominioyveremoscómovincularloa tuaplicaciónwebenPythonAnywhere.Sinembargo,deberíassaberquelamayoríadelos dominiossonpagos,yPythonAnywheretecobraráunvaloradicionalparausartupropio nombrededominio--noesdemasiadodineroentotal,peroesprobablementealgoque quierashacersólosiestásmuycomprometidaconlacausa. ¿Donderegistrarundominio? Undominiotípicocuestaalrededorde15dólaresestadounidensesanuales.Hayopciones másbaratasymáscaras,dependiendodelproveedor.Hayunagrancantidaddeempresas alasquepuedescomprarundominio:unasimplebúsquedaengoogledarácientosde opciones. NuestraopciónfavoritaesIwantmyname.Ellossepromocionancomounaopción "indoloraparaelmanejodedominios"yrealmenteloson. Tambiénpuedesobtenerdominiosgratuitos.dot.tkesunlugarparaobteneruno,pero deberíamosadvertirtequelosdominiosgratuitosavecessesientenalgo"baratos"--situ sitiovaaserunsitioparaunnegocioprofesional,seguramentequierasconsiderarcomprar undominio"apropiado"quetermineen .com. CómoapuntartudominioaPythonAnywhere Sielegistelaopcióndeiwantmyname.com,hazclicken Domainsenelmenúyelijetu nuevodominio.Luegoencuentraelvínculoa manageDNSrecords: Ahoranecesitasencontraresteformulario: Dominio 129 DjangoGirlsTutorial Ycompletarloconlossiguientesdetalles:-Hostname:www-Type:CNAME-Value:tu dominiodePythonAnywhere(porejemplo,djangogirls.pythonanywhere.com)-TTL:60 Enlaparteinferior,hazclickenelbotón"Agregar"yguardaloscambios. NotaSiutilizasteunproveedordedominiosdiferente,lainterfazexactaparaencontrar tusconfiguracionesdeDNS/CNAMEserádiferente,perotuobjetivoeselmismo: configurarunCNAMEqueapunteatunuevodominioen yourusername.pythonanywhere.com. Puedetomarunosminutosparatudominioparaempezarafuncionar,¡sépaciente! Configuraeldominioatravésdelaaplicación webenPythonAnywhere TambiénnecesitarásdecirleaPythonAnywherequequieresusartudominiopersonalizado. VealapáginaPythonAnywhereAccountsyhazunaactualizacióndeltipodecuenta.La opciónmáseconómica(elplan"Hacker")estábienparaempezar.Siemprepuedeselegirun planconmayoresprestacionesdespuéscuandotevuelvassuper-famosaytengasmillones devisitas. Luego,vealapestañaWebyanotaunpardecosas: Copialarutaatuvirtualenvyponlaenalgúnlugarseguro AbretuarchivodeconfiguraciónWSGI,copiaelcontenido,ypégaloenalgúnlugar seguro Luego,hazclickenDeletedetuviejaaplicaciónweb.Notepreocupes,estonoeliminarátu código,solamentecambiaráeldominioyourusername.pythonanywhere.com.Luego,crea unanuevaaplicaciónwebysigueestospasos: Ingresatunombrededominio Elije"configuraciónmanual" ElijePython3.4 ¡Ylisto! Cuandoseasredirigidaalapestañaweb. Pegalarutaalvirtualenvqueguardasteanteriormente AbretunuevoarchivodeconfiguraciónWSGIypegaelcontenidodetuviejoarchivode configuración Dominio 130 DjangoGirlsTutorial Hazclickenactualizar,¡deberíasverquetusitioestáenlíneaenelnuevodominio! Siteencuentrasconalgúnproblema,hazclickenelvínculo"Sendfeedback"enelsitiode PythonAnywhere,yunodesusamigablesadministradorestecontactaráparaayudarteen breve. Dominio 131 DjangoGirlsTutorial ¿Quésigue? ¡Datemuchasfelicitaciones!¡Eresincreíble!.¡Estamosorgullosos!<3 ¿Quéhacerahora? Tomaundescansoyrelájate.Acabasdehaceralgorealmentegrande. Despuésdeeso,asegúratede: SeguiraDjangoGirlsenFacebookoTwitterparaestaraldía ¿Mepuedesrecomendarrecursosadicionales? ¡Sí!Enprimerlugar,sigueadelanteypruebanuestrolibrollamadoDjangoGirlsTutorial: Extensiones. Másadelante,puedesintentarlosrecursoslistadosacontinuación.¡Estántodosmuy recomendados! Django'sofficialtutorial NewCodertutorials CodeAcademyPythoncourse CodeAcademyHTML&CSScourse DjangoCarrotstutorial LearnPythonTheHardWaybook GettingStartedWithDjangovideolessons TwoScoopsofDjango:BestPracticesforDjangobook ¿Quésigue? 132