Sei sulla pagina 1di 16

This document is for Django's development version, which can be significantly different from previous releases.

Get old docs here: 1.1, 1.


!riting your first Django app, part 1"
Lets learn by example.
Throughout this tutorial, well walk you through the creation of a basic poll application.
Itll consist of two parts:
A public site that lets people view polls an vote in them.
An amin site that lets you a, change an elete polls.
!ell assume you have Django installed alreay. "ou can tell #$ango is installe by running the %ython interactive interpreter an typing import
django. If that comman runs successfully, with no errors, #$ango is installe.
!here to get help:
If youre having trouble going through this tutorial, please post a message to $ango&users or rop by '$ango on irc.freenoe.net to chat with other
#$ango users who might be able to help.
#reating a project"
If this is your first time using #$ango, youll have to take care of some initial setup. (amely, youll nee to auto&generate some coe that establishes a
#$ango project ) a collection of settings for an instance of #$ango, incluing atabase configuration, #$ango&specific options an application&specific
settings.
*rom the comman line, cd into a irectory where you like to store your coe, then run the comman django-admin.py startproject
mysite. This will create a mysite irectory in your current irectory.
+ac ,- . permissions
If youre using +ac ,- ., you may see the message /permission enie0 when you try to run django-admin.py startproject. This is because,
on 1nix&base systems like ,- ., a file must be marke as /executable0 before it can be run as a program. To o this, open Terminal.app an navigate
2using the cd comman3 to the irectory where django-admin.py is installe, then run the comman chmod +x django-admin.py.
(ote
"oull nee to avoi naming pro$ects after built&in %ython or #$ango components. In particular, this means you shoul avoi using names like django
2which will conflict with #$ango itself3 or test 2which conflicts with a built&in %ython package3.
django-admin.py shoul be on your system path if you installe #$ango via python setup.py. If its not on your path, you can fin it in site-
packages/django/bin, where `site-packages` is a irectory within your %ython installation. 4onsier symlinking to django-admin.py from
some place on your path, such as /usr/local/bin.
!here shoul this coe live5
If your backgroun is in %6%, youre probably use to putting coe uner the !eb servers ocument root 2in a place such as /var/www3. !ith #$ango,
you ont o that. Its not a goo iea to put any of this %ython coe within your !eb servers ocument root, because it risks the possibility that people
may be able to view your coe over the !eb. Thats not goo for security.
%ut your coe in some irectory outside of the ocument root, such as /home/mycode.
Lets look at what startproject create:
mysite/
__init__.py
manage.py
settings.py
urls.py
These files are:
__init__.py: An empty file that tells %ython that this irectory shoul be consiere a %ython package. 27ea more about packages in the
official %ython ocs if you8re a %ython beginner.3
manage.py: A comman&line utility that lets you interact with this #$ango pro$ect in various ways. "ou can rea all the etails about
manage.py in django-admin.py and manage.py.
settings.py: -ettings9configuration for this #$ango pro$ect. Django settings will tell you all about how settings work.
urls.py: The 17L eclarations for this #$ango pro$ect: a ;table of contents; of your #$ango&powere site. "ou can rea more about 17Ls in
URL dispatcher.
The development server"
Let8s verify this worke. 4hange into the mysite irectory, if you haven8t alreay, an run the comman python manage.py runserver. "ou8ll
see the following output on the comman line:
Validating models...
errors !ound.
"jango version #.$ using settings %mysite.settings%
"evelopment server is running at http&//#'(...#&)/
*uit the server with +,-./,0-+.
"ou8ve starte the #$ango evelopment server, a lightweight !eb server written purely in %ython. !e8ve inclue this with #$ango so you can evelop
things rapily, without having to eal with configuring a prouction server && such as Apache && until you8re reay for prouction.
(ow8s a goo time to note: #,(8T use this server in anything resembling a prouction environment. It8s intene only for use while eveloping. 2!e8re in
the business of making !eb frameworks, not !eb servers.3
(ow that the server8s running, visit http:99<=>.?.?.<:@???9 with your !eb browser. "ou8ll see a ;!elcome to #$ango; page, in pleasant, light&blue pastel. It
workeA
4hanging the port
By efault, the runserver comman starts the evelopment server on the internal I% at port @???.
If you want to change the server8s port, pass it as a comman&line argument. *or instance, this comman starts the server on port @?@?:
python manage.py runserver ))
If you want to change the server8s I%, pass it along with the port. -o to listen on all public I%s 2useful if you want to show off your work on other
computers3, use:
python manage.py runserver ...&)
*ull ocs for the evelopment server can be foun in the runserver reference.
Database setup"
(ow, eit settings.py. It8s a normal %ython moule with moule&level variables representing #$ango settings. 4hange the following keys in the
"1.121343 %de!ault% item to match your atabases connection settings.
4-56-4 && Cither %django.db.backends.postgres7l_psycopg'%, %django.db.backends.mys7l% or
%django.db.backends.s7lite8%. ,ther backens are also available.
-194 && The name of your atabase. If you8re using -DLite, the atabase will be a file on your computer: in that case, -194 shoul be the full
absolute path, incluing filename, of that file. If the file oesn8t exist, it will automatically be create when you synchroniEe the atabase for the
first time 2see below3.
!hen specifying the path, always use forwar slashes, even on !inows 2e.g. +&/homes/user/mysite/s7lite8.db3.
:34/ && "our atabase username 2not use for -DLite3.
;133<,/" && "our atabase passwor 2not use for -DLite3.
=,3. && The host your atabase is on. Leave this as an empty string if your atabase server is on the same physical machine 2not use for
-DLite3.
If you8re new to atabases, we recommen simply using -DLite 2by setting 4-56-4 to %django.db.backends.s7lite8%3. -DLite is inclue as
part of %ython =.F an later, so you won8t nee to install anything else.
(ote
If you8re using %ostgre-DL or +y-DL, make sure you8ve create a atabase by this point. #o that with ;+/41.4 "1.12134 database_name>;
within your atabase8s interactive prompt.
If you8re using -DLite, you on8t nee to create anything beforehan & the atabase file will be create automatically when it is neee.
!hile you8re eiting settings.py, take note of the 6-3.1004"_1;;3 setting towars the bottom of the file. That variable hols the names of all
#$ango applications that are activate in this #$ango instance. Apps can be use in multiple pro$ects, an you can package an istribute them for use by
others in their pro$ects.
By efault, 6-3.1004"_1;;3 contains the following apps, all of which come with #$ango:
django.contrib.auth && An authentication system.
django.contrib.contenttypes && A framework for content types.
django.contrib.sessions && A session framework.
django.contrib.sites && A framework for managing multiple sites with one #$ango installation.
These applications are inclue by efault as a convenience for the common case.
Cach of these applications makes use of at least one atabase table, though, so we nee to create the tables in the atabase before we can use them. To o
that, run the following comman:
python manage.py syncdb
The syncdb comman looks at the 6-3.1004"_1;;3 setting an creates any necessary atabase tables accoring to the atabase settings in your
settings.py file. "ou8ll see a message for each atabase table it creates, an you8ll get a prompt asking you if you8 like to create a superuser account
for the authentication system. Go ahea an o that.
If you8re intereste, run the comman&line client for your atabase an type ?dt 2%ostgre-DL3, 3=,< .12043> 2+y-DL3, or .schema 2-DLite3 to
isplay the tables #$ango create.
*or the minimalists
Like we sai above, the efault applications are inclue for the common case, but not everyboy nees them. If you on8t nee any or all of them, feel
free to comment&out or elete the appropriate line2s3 from 6-3.1004"_1;;3 before running syncdb. The syncdb comman will only create tables
for apps in 6-3.1004"_1;;3.
#reating models"
(ow that your environment && a ;pro$ect; && is set up, you8re set to start oing work.
Cach application you write in #$ango consists of a %ython package, somewhere on your %ython path, that follows a certain convention. #$ango comes
with a utility that automatically generates the basic irectory structure of an app, so you can focus on writing coe rather than creating irectories.
%ro$ects vs. apps
!hat8s the ifference between a pro$ect an an app5 An app is a !eb application that oes something && e.g., a weblog system, a atabase of public
recors or a simple poll app. A pro$ect is a collection of configuration an apps for a particular !eb site. A pro$ect can contain multiple apps. An app can
be in multiple pro$ects.
In this tutorial, we8ll create our poll app in the mysite irectory, for simplicity. As a conseHuence, the app will be couple to the pro$ect && that is, %ython
coe within the poll app will refer to mysite.polls. Later in this tutorial, we8ll iscuss ecoupling your apps for istribution.
To create your app, make sure you8re in the mysite irectory an type this comman:
python manage.py startapp polls
That8ll create a irectory polls, which is lai out like this:
polls/
__init__.py
models.py
tests.py
views.py
This irectory structure will house the poll application.
The first step in writing a atabase !eb app in #$ango is to efine your moels && essentially, your atabase layout, with aitional metaata.
%hilosophy
A moel is the single, efinitive source of ata about your ata. It contains the essential fiels an behaviors of the ata you8re storing. #$ango follows the
DRY Principle. The goal is to efine your ata moel in one place an automatically erive things from it.
In our simple poll app, we8ll create two moels: polls an choices. A poll has a Huestion an a publication ate. A choice has two fiels: the text of the
choice an a vote tally. Cach choice is associate with a poll.
These concepts are represente by simple %ython classes. Cit the polls/models.py file so it looks like this:
!rom django.db import models
class ;oll@models.9odelA&
7uestion B models.+harCield@max_lengthB'A
pub_date B models."ate.imeCield@%date published%A
class +hoice@models.9odelA&
poll B models.CoreignDey@;ollA
choice B models.+harCield@max_lengthB'A
votes B models.6ntegerCield@A
Crrors about max_length
If #$ango gives you an error message saying that max_length is not a vali argument, you8re most likely using an ol version of #$ango. 2This version
of the tutorial is written for the latest evelopment version of #$ango.3 If you8re using a -ubversion checkout of #$ango8s evelopment version 2see the
installation docs for more information3, you shouln8t have any problems.
If you want to stick with an oler version of #$ango, you8ll want to switch to the #$ango ?.IJ tutorial, because this tutorial covers several features that
only exist in the #$ango evelopment version.
The coe is straightforwar. Cach moel is represente by a class that subclasses django.db.models.9odel. Cach moel has a number of class
variables, each of which represents a atabase fiel in the moel.
Cach fiel is represente by an instance of a Cield class && e.g., +harCield for character fiels an "ate.imeCield for atetimes. This tells
#$ango what type of ata each fiel hols.
The name of each Cield instance 2e.g. 7uestion or pub_date 3 is the fiel8s name, in machine&frienly format. "ou8ll use this value in your %ython
coe, an your atabase will use it as the column name.
"ou can use an optional first positional argument to a Cield to esignate a human&reaable name. That8s use in a couple of introspective parts of
#$ango, an it oubles as ocumentation. If this fiel isn8t provie, #$ango will use the machine&reaable name. In this example, we8ve only efine a
human&reaable name for ;oll.pub_date. *or all other fiels in this moel, the fiel8s machine&reaable name will suffice as its human&reaable
name.
-ome Cield classes have reHuire elements. +harCield, for example, reHuires that you give it a max_length. That8s use not only in the atabase
schema, but in valiation, as we8ll soon see.
*inally, note a relationship is efine, using CoreignDey. That tells #$ango each 4hoice is relate to a single %oll. #$ango supports all the common
atabase relationships: many&to&ones, many&to&manys an one&to&ones.
$ctivating models"
That small bit of moel coe gives #$ango a lot of information. !ith it, #$ango is able to:
4reate a atabase schema 2+/41.4 .1204 statements3 for this app.
4reate a %ython atabase&access A%I for accessing %oll an 4hoice ob$ects.
But first we nee to tell our pro$ect that the polls app is installe.
%hilosophy
#$ango apps are ;pluggable;: "ou can use an app in multiple pro$ects, an you can istribute apps, because they on8t have to be tie to a given #$ango
installation.
Cit the settings.py file again, an change the 6-3.1004"_1;;3 setting to inclue the string %mysite.polls%. -o it8ll look like this:
6-3.1004"_1;;3 B @
%django.contrib.auth%$
%django.contrib.contenttypes%$
%django.contrib.sessions%$
%django.contrib.sites%$
%mysite.polls%
A
(ow #$ango knows mysite inclues the polls app. Let8s run another comman:
python manage.py s7l polls
"ou shoul see something similar to the following 2the +/41.4 .1204 -DL statements for the polls app3:
2456->
+/41.4 .1204 Epolls_pollE @
EidE serial -,. -:00 ;/691/F D4F$
E7uestionE varchar@'A -,. -:00$
Epub_dateE timestamp with time Gone -,. -:00
A>
+/41.4 .1204 Epolls_choiceE @
EidE serial -,. -:00 ;/691/F D4F$
Epoll_idE integer -,. -:00 /4C4/4-+43 Epolls_pollE @EidEA$
EchoiceE varchar@'A -,. -:00$
EvotesE integer -,. -:00
A>
+,996.>
(ote the following:
The exact output will vary epening on the atabase you are using.
Table names are automatically generate by combining the name of the app 2polls3 an the lowercase name of the moel && poll an
choice. 2"ou can overrie this behavior.3
%rimary keys 2I#s3 are ae automatically. 2"ou can overrie this, too.3
By convention, #$ango appens E_idE to the foreign key fiel name. "es, you can overrie this, as well.
The foreign key relationship is mae explicit by a /4C4/4-+43 statement.
It8s tailore to the atabase you8re using, so atabase&specific fiel types such as auto_increment 2+y-DL3, serial 2%ostgre-DL3, or
integer primary key 2-DLite3 are hanle for you automatically. -ame goes for Huoting of fiel names && e.g., using ouble Huotes or
single Huotes. The author of this tutorial runs %ostgre-DL, so the example output is in %ostgre-DL syntax.
The s7l comman oesn8t actually run the -DL in your atabase & it $ust prints it to the screen so that you can see what -DL #$ango thinks is
reHuire. If you wante to, you coul copy an paste this -DL into your atabase prompt. 6owever, as we will see shortly, #$ango provies an
easier way of committing the -DL to the atabase.
If you8re intereste, also run the following commans:
python manage.py validate && 4hecks for any errors in the construction of your moels.
python manage.py s7lcustom polls && ,utputs any custom SQL statements 2such as table moifications or constraints3 that are
efine for the application.
python manage.py s7lclear polls && ,utputs the necessary "/,; .1204 statements for this app, accoring to which tables
alreay exist in your atabase 2if any3.
python manage.py s7lindexes polls && ,utputs the +/41.4 6-"4H statements for this app.
python manage.py s7lall polls && A combination of all the -DL from the s7l, s7lcustom, an s7lindexes commans.
Looking at the output of those commans can help you unerstan what8s actually happening uner the hoo.
(ow, run syncdb again to create those moel tables in your atabase:
python manage.py syncdb
The syncdb comman runs the sHl from 8sHlall8 on your atabase for all apps in 6-3.1004"_1;;3 that on8t alreay exist in your atabase. This
creates all the tables, initial ata an inexes for any apps you have ae to your pro$ect since the last time you ran syncb. syncdb can be calle as
often as you like, an it will only ever create the tables that on8t exist.
7ea the django-admin.py documentation for full information on what the manage.py utility can o.
%laying with the $%&"
(ow, let8s hop into the interactive %ython shell an play aroun with the free A%I #$ango gives you. To invoke the %ython shell, use this comman:
python manage.py shell
!e8re using this instea of simply typing ;python;, because manage.py sets up the pro$ect8s environment for you. ;-etting up the environment; involves
two things:
%utting mysite on sys.path. *or flexibility, several pieces of #$ango refer to pro$ects in %ython otte&path notation 2e.g.
%mysite.polls.models%3. In orer for this to work, the mysite package has to be on sys.path.
!e8ve alreay seen one example of this: the 6-3.1004"_1;;3 setting is a list of packages in otte&path notation.
-etting the "I1-5,_34..6-53_9,":04 environment variable, which gives #$ango the path to your settings.py file.
Bypassing manage.py
If you8 rather not use manage.py, no problem. Kust make sure mysite is at the root level on the %ython path 2i.e., import mysite works3 an set
the "I1-5,_34..6-53_9,":04 environment variable to mysite.settings.
*or more information on all of this, see the django-admin.py documentation.
,nce you8re in the shell, explore the database AP:
JJJ !rom mysite.polls.models import ;oll$ +hoice K 6mport the model classes we just wrote.
K -o polls are in the system yet.
JJJ ;oll.objects.all@A
LM
K +reate a new ;oll.
JJJ import datetime
JJJ p B ;oll@7uestionBE<hat%s upNE$ pub_dateBdatetime.datetime.now@AA
K 3ave the object into the database. Fou have to call save@A explicitly.
JJJ p.save@A
K -ow it has an 6". -ote that this might say E#0E instead o! E#E$ depending
K on which database you%re using. .hat%s no biggie> it just means your
K database backend pre!ers to return integers as ;ython long integer
K objects.
JJJ p.id
#
K 1ccess database columns via ;ython attributes.
JJJ p.7uestion
E<hat%s upNE
JJJ p.pub_date
datetime.datetime@'($ ($ #O$ #'$ $ O8A
K +hange values by changing the attributes$ then calling save@A.
JJJ p.pub_date B datetime.datetime@'($ P$ #$ $ A
JJJ p.save@A
K objects.all@A displays all the polls in the database.
JJJ ;oll.objects.all@A
LQ;oll& ;oll objectJM
!ait a minute. Q;oll& ;oll objectJ is, utterly, an unhelpful representation of this ob$ect. Let8s fix that by eiting the polls moel 2in the
polls/models.py file3 an aing a __unicode__@A metho to both ;oll an +hoice:
class ;oll@models.9odelA&
K ...
de! __unicode__@sel!A&
return sel!.7uestion
class +hoice@models.9odelA&
K ...
de! __unicode__@sel!A&
return sel!.choice
If __unicode__@A oesn8t seem to work
If you a the __unicode__@A metho to your moels an on8t see any change in how they8re represente, you8re most likely using an ol version of
#$ango. 2This version of the tutorial is written for the latest evelopment version of #$ango.3 If you8re using a -ubversion checkout of #$ango8s
evelopment version 2see the installation docs for more information3, you shouln8t have any problems.
If you want to stick with an oler version of #$ango, you8ll want to switch to the #$ango ?.IJ tutorial, because this tutorial covers several features that
only exist in the #$ango evelopment version.
It8s important to a __unicode__@A methos to your moels, not only for your own sanity when ealing with the interactive prompt, but also because
ob$ects8 representations are use throughout #$ango8s automatically&generate amin.
!hy __unicode__@A an not __str__@A5
If you8re familiar with %ython, you might be in the habit of aing __str__@A methos to your classes, not __unicode__@A methos. !e use
__unicode__@A here because #$ango moels eal with 1nicoe by efault. All ata store in your atabase is converte to 1nicoe when it8s
returne.
#$ango moels have a efault __str__@A metho that calls __unicode__@A an converts the result to a 1T*&@ bytestring. This means that
unicode@pA will return a 1nicoe string, an str@pA will return a normal string, with characters encoe as 1T*&@.
If all of this is $ibberish to you, $ust remember to a __unicode__@A methos to your moels. !ith any luck, things shoul Kust !ork for you.
(ote these are normal %ython methos. Let8s a a custom metho, $ust for emonstration:
import datetime
K ...
class ;oll@models.9odelA&
K ...
de! was_published_today@sel!A&
return sel!.pub_date.date@A BB datetime.date.today@A
(ote the aition of import datetime to reference %ython8s stanar datetime moule.
-ave these changes an start a new %ython interactive shell by running python manage.py shell again:
JJJ !rom mysite.polls.models import ;oll$ +hoice
K 9ake sure our __unicode__@A addition worked.
JJJ ;oll.objects.all@A
LQ;oll& <hat%s upNJM
K "jango provides a rich database lookup 1;6 that%s entirely driven by
K keyword arguments.
JJJ ;oll.objects.!ilter@idB#A
LQ;oll& <hat%s upNJM
JJJ ;oll.objects.!ilter@7uestion__startswithB%<hat%A
LQ;oll& <hat%s upNJM
K 5et the poll whose year is '(.
JJJ ;oll.objects.get@pub_date__yearB'(A
Q;oll& <hat%s upNJ
JJJ ;oll.objects.get@idB'A
.raceback @most recent call lastA&
...
"oes-ot4xist& ;oll matching 7uery does not exist.
K 0ookup by a primary key is the most common case$ so "jango provides a
K shortcut !or primary-key exact lookups.
K .he !ollowing is identical to ;oll.objects.get@idB#A.
JJJ ;oll.objects.get@pkB#A
Q;oll& <hat%s upNJ
K 9ake sure our custom method worked.
JJJ p B ;oll.objects.get@pkB#A
JJJ p.was_published_today@A
Calse
K 5ive the ;oll a couple o! +hoices. .he create call constructs a new
K choice object$ does the 6-34/. statement$ adds the choice to the set
K o! available choices and returns the new +hoice object.
JJJ p B ;oll.objects.get@pkB#A
JJJ p.choice_set.create@choiceB%-ot much%$ votesBA
Q+hoice& -ot muchJ
JJJ p.choice_set.create@choiceB%.he sky%$ votesBA
Q+hoice& .he skyJ
JJJ c B p.choice_set.create@choiceB%Iust hacking again%$ votesBA
K +hoice objects have 1;6 access to their related ;oll objects.
JJJ c.poll
Q;oll& <hat%s upNJ
K 1nd vice versa& ;oll objects get access to +hoice objects.
JJJ p.choice_set.all@A
LQ+hoice& -ot muchJ$ Q+hoice& .he skyJ$ Q+hoice& Iust hacking againJM
JJJ p.choice_set.count@A
8
K .he 1;6 automatically !ollows relationships as !ar as you need.
K :se double underscores to separate relationships.
K .his works as many levels deep as you want> there%s no limit.
K Cind all +hoices !or any poll whose pub_date is in '(.
JJJ +hoice.objects.!ilter@poll__pub_date__yearB'(A
LQ+hoice& -ot muchJ$ Q+hoice& .he skyJ$ Q+hoice& Iust hacking againJM
K 0et%s delete one o! the choices. :se delete@A !or that.
JJJ c B p.choice_set.!ilter@choice__startswithB%Iust hacking%A
JJJ c.delete@A
*or full etails on the atabase A%I, see our Database AP re!erence.
!hen you8re comfortable with the A%I, rea part " o! this tutorial to get #$ango8s automatic amin working.
Tutorial de Django
IntroucciLn
7eHuisitos
Cl ecosistema e #$ango
Cstructura e 2una aplicaciLn3 #$ango
(uestra aplicaciLn web: Trivial trivial
4reaciLn el proyecto y la aplicaciLn
#efinieno los moelos
-iguente: implementano la autentificaciLn
*ormulario e preguntas
7esponieno a la pregunta
4onclusiones
(otas
&ntroducci'n
#$ango es un ;framework; para el esarrollo e aplicaciones !eb basao en el lengua$e e programaciLn %ython y Hue sigue el patrLn e iseMo +N4.
Cn los Oltimos tiempos la palabra ;framework; parece Hue se ha convertio en el ;-anto Grial; el esarrollo e aplicaciones !eb.
1no e los ;framework;
2<3
mPs famosos o populares Hue se ha ao a conocer ha sio 7uby ,n 7ails. QDuR aporta esta herramienta frente a otras
alternativas mPs conocias y establecias5.
Lo mPs noveoso era la prevalencia e la convenciLn frente a la configuraciLn. Cn veE e anar eitano ficheros .xml 2como en las herramientas K=CC3
se establecSa una estructura e irectorios estPnar para la aplicaciLn, no era necesario configurar mapeos, plantillas, etc.
7ails tambiRn abstrae completamente la interacciLn con bases e atos relacionales: el sistema es capaE e ;aivinar; los moelos e inferir los ob$etos Hue
intervienen en la aplicaciLn con sLlo examinar la efiniciLn e tablas en el sistema relacional. 6ay un ob$eto, ;Active7ecor; Hue es el Hue relaciona o
hace e puente entre el sistema relacional y el sistema orientao a ob$etos.
BasPnose en algunas e las ieas e 7ails y otras propias han surgio bastantes herramientas e esarrollo e aplicaciones web con una orientaciLn
similar: poco cLigo, reusable, etc.
Cn este artSculo presentaremos #$ango, Hue recoge muchas e las ieas e 7ails y aporta algunas soluciones propias. #esarrollaremos una aplicaciLn
vieno los problemas Hue nos encontramos y cLmo los solucionamos. Cn casi toos los tutoriales Hue hemos leSo, se presenta sLlo lo mPs bPsico, pero a
la hora e avanEar o tratar e hacer algo mPs ;real; nos encontramos con ificultaes. Cste artSculo trata e paliar Rsto.
(e)uisitos
%ara seguir este tutorial es necesario conocer el lengua$e %ython, y haber leSo el tutorial e #$ango. %or supuesto, asumimos Hue el lector sabe 6T+L y
tecnologSas relacionaas 2protocolo 6TT%, 4--, etc.3
*l ecosistema de Django
Cs bastante habitual en este munillo e los ;framework +N4 & Hue te cagas;
2=3
Hue haya una 2sana3 competencia entre las istintas herramientas y
lengua$es. %oemos encontrar numerosas comparativas, tutoriales, guSas, etc, pero too este 2Otil3 material sLlo rasca la superficie e lo Hue supone
traba$ar con estas herramientas.
A veces a la impresiLn e Hue hay un concurso el tipo #$on %%% monto una aplicaci&n del tipo YYY en ''' minutos ( L #)i !rame*or+ la,a m-s
blanco(
(o vamos a entrar en el $uego. (o hemos cronometrao lo Hue se ha tarao en terminar el e$emplo Hue esarrollaremos en este artSculo. -i contamos
lSneas e cLigo el e$emplo, se porSa ;picar; too en apenas ieE minutos, pero no este tiempo es un ato real ni vPlio. Tampoco vamos a ecir Hue
#$ango es me$or Hue otras herramientas 2ni siHuiera lo tenemos claro3, aunHue sS Hue mencionaremos HuR es lo Hue nos ha gustao y lo Hue no nos ha
gustao
2T, U3
.
4omo bien es sabio, en el munillo %ython siempre hay mucho one elegir: QInterfaces grPficas5 Q-erviores web5 Q-istemas e plantillas5 Q...5
Csto puee resultar confuso, puesto Hue antes e empeEar a esarrollar una aplicaciLn el tipo Hue sea hay Hue ocumentarse bastante para elegir la
herramienta aecuaa. Cn cuanto a los ; !eb *rameworks; 2otra veE la palabre$a :&3 tambiRn hay bastantes alternativas. Las mPs conocias son #$ango,
TurboGears y %ylons.
Los programaores el lengua$e 7uby no tienen Hue estru$arse tanto la cabeEa. La herramienta por antonomasia para esarrollo web e 7uby es 7uby ,n
7ails. (o hay mucho mPs one elegir. Csto tiene venta$as 2centraliEaciLn e esfuerEos, uniformia, ...3 e inconvenientes 2menor flexibilia, ...3. 4aa
uno ebe valorar HuR es lo Hue prefiere.
%or Oltimo, un breve apunte sobre los lengua$es y sus moismos. 7uby es un lengua$e con una filosofSa parecia a %erl: .hay m-s de una !orma de
hacerlo., %ython tiene una aproximaciLn iferente: .hacer las cosas de una s&la manera/ la m-s sencilla.. #e nuevo, aHuS intervienen las preferencias
personales e caa uno a la hora e programar. #$ango sigue bastante la ; filosofSa pythonera; y tratan e seguir una serie e principios e iseMo 2me$or
explScito Hue implScito, asumir Hue el esarrollaor sabe lo Hue estP hacieno, ...3.
*structura de +una aplicaci'n, Django
#$ango istingue entre proyectos y aplicaciones. 1n proyecto es un sitio web completo Hue constar e una o varias aplicaciones. Cstas aplicaciones las
proporciona #$ango o las escribe el esarrollaor. Cl comano Hue crea un proyecto es django-admin.py. (o vamos a repetir lo Hue cuenta el tutorial
e #$ango. -implemente, con django-admin.py startproject miweb se crea un irectorio miweb Hue contiene varios ficheros .py:
__init__.py, manage.py, settings.py y urls.py.
__init__.py: #efine nuestro irectorio como un mLulo %ython vPlio.
manage.py: 1tilia para gestionar nuestro proyecto: arrancar servior e pruebas, sincroniEar moelos, etc.
settings.py: 4onfiguraciLn el proyecto.
urls.py: GestiLn e las urls. Cste fichero serSa el controlaor e la aplicaciLn. +apea las url entrantes a funciones %ython efinias en
mLulos.
%ara crear una aplicaciLn nueva entro el proyecto e$ecutamos python manage.py startapp miaplicacion. Cste comano crea el irectorio
miaplicacion y los ficheros __init__.py, views.py, y models.py.
__init__.py: #efine nuestro irectorio como un mLulo %ython vPlio.
models.py: AHuS se efinen los moelos u ob$etos Hue serPn mapeaos a una base e atos relacional.
views.py: #efine las funciones Hue van a responer a las urls entrantes.
Csto es un iseMo +N4: moelo 2models.py3, vista 2views.py3, controlaor2urls.py3.
$claraci'n: los esarrollaores e #$ango llaman a su arHuitectura +NT: +oel & Niew & Template, ya Hue consieran Hue el controlaor es el propio
framework.
-uestra aplicaci'n web: Trivial trivial
Namos a esarrollar una aplicaciLn web un poco mPs completa Hue la Hue proponen en el tutorial e #$ango. Cs un Trivial multiusuario.
Cspecificaciones:
Las preguntas y los usuarios los crea un aministraor.
Cxisten iferentes categorSas e preguntas.
4aa usuario tiene su propio $uego 2esto es, respone a sus preguntas3.
Cs obligatorio estar valiao en el sistema para $ugar.
4omo es una aplicaciLn e prueba, usaremos el servior e esarrollo Hue viene con #$ango. Los ficheros estPticos 24--, imPgenes, etc3 tambiRn los
servirP #$ango, aunHue esta prPctica estP totalmente esaconse$aa en un entorno e proucciLn. 4omo sistema relacional usaremos s)lite 2hay un
;river; para %ython3.
(os encontramos en este punto con un enfoHue totalmente iferente al e 7ails: #$ango mantiene los moelos relacionales 2tablas, relaciones, ...3 a partir
el moelo Hue nosotros efinimos 2en una clase3, mientras Hue 7ails ;aivina; el moelo a partir el esHuema relacional. La venta$a el ;enfoHue 7ails;
es Hue en cuanto Hue el moelo cambia, la herramienta etecta estos cambios y se refle$a automPticmente. Cl inconveniente es Hue hay Hue seguir las
convenciones 7ails para Hue Rsto funcione.
%ersonalmente, preferimos el planteamiento e #$ango: el esarrollaor efine moelos, la herramienta se encarga e traucir estos moelos a -DL, pero
hay un inconveniente: si los moelos cambian, hay Hue hacer el cambio manualmente en el esHuema el sistema relacional. %arece Hue en posteriores
versiones e #$ango tratarPn e implementar esta me$ora, pero a Sa e hoy 2#$ango versiLn ?.IJ3, no es asS.
Tras esta breve isHuisiciLn, pasemos a efinir los moelos. Cncontramos las siguientes entiaes:
1suario, caracteriEao por un nombre, ;login;, contraseMa, ...
4ategorSa
%regunta, con un tStulo, con$unto e respuestas posibles, respuesta correcta, un grPfico asociao, ...
2F3

7espuesta, asociaa a un usuario concreto y una pregunta concreta, guarano el resultao 2acierto9fallo3, etc.
#reaci'n del proyecto y la aplicaci'n
Lo primero es crear el proyecto: django-admin.py startproject .rivial
A$ustamos algunos parPmetros en settings.py y urls.py. 6abilitaremos la interfaE aministrativa 2
J
3, el irectorio ese el Hue se sirven los
contenios estPticos y algunos a$ustes mPs.
settings.py 2extracto3:
94"61_/,,. B %/home/david/desarrollo/.rivial/site_media/%
94"61_:/0 B %http&//localhost&)/site_media/%
1"96-_94"61_;/4C6H B %/media/%
6-3.1004"_1;;3 B @
%django.contrib.auth%$
%django.contrib.contenttypes%$
%django.contrib.sessions%$
%django.contrib.sites%$
%django.contrib.admin%$
%.rivial.juego%$
A
urls.py 2extracto3:
!rom settings import 94"61_/,,.
urlpatterns B patterns@%%$
@r%Radmin/%$ include@%django.contrib.admin.urls%AA$
A
urlpatterns +B patterns@%django.views%$
@r%Rsite_media/@.SAT%$ %static.serve%$ U%document_root%& 94"61_/,,.VA$
A
4uano Hueramos pasar a proucciLn, sLlo tenremos Hue eliminar la Oltima entraa en urls.py y eitar 94"61_:/0 en settings.py.
TambiRn tenemos Hue crear la base e atos sHlite 2comano s7lite data/datos.db3.
Ahora, ese el irectorio Trivial 2irectorio raSE3 creamos la aplicaciLn propiamente icha 2$uego3: python manage.py startapp juego.
Definiendo los modelos
Citamos el fichero juego/models.py para efinir nuestros ob$etos. Las clases Hue representan moelos eben herear e la clase +oel y siguen
una sintaxis muy sencilla e intuitiva.
#$ango incorpora en el paHuete django.contrib.auth too un sistema e autentificaciLn y gestiLn e usuarios, asS Hue no vamos a reinventar la
ruea y utiliEaremos este sistema 2
>
3.
Cstos son nuestros moelos:
!rom django.db import models
!rom django.contrib.auth.models import :ser
class :suario@:serA&
de! __str__@sel!A&
return sel!.username

class 1dmin&
pass
class +ategoria@models.9odelA&
nombre B models.+harCield@E+ategorWaE$ maxlengthB'A

de! __str__@sel!A&
return sel!.nombre

class 1dmin&
pass


class ;regunta@models.9odelA&
categoria B models.CoreignDey@+ategoria$ verbose_nameBE+ategorWa la 7ue perteneceEA
titulo B models.+harCield@E.WtuloE$ maxlengthB'A
texto B models..extCield@E.exto de la preguntaEA
respuesta_# B models.+harCield@maxlengthB'A
respuesta_' B models.+harCield@maxlengthB'A
respuesta_8 B models.+harCield@maxlengthB'A
respuesta_P B models.+harCield@maxlengthB'A
respuesta_correcta B models.+harCield@maxlengthB'A
!oto B models.+harCield@maxlengthB'A

de! __str__@sel!A&
return sel!.titulo

class 1dmin&
pass

class /espuesta@models.9odelA&
tiempo B models.6ntegerCield@E.iempo en segs.EA
resultado B models.6ntegerCield@E -J incorrecto$ # -J correctoEA
pregunta B models.CoreignDey@;regunta$ verbose_nameBE;regunta 7ue se respondeEA
usuario B models.CoreignDey@:ser$ verbose_nameBE:suario 7ue respondeEA

de! __str__@sel!A&
return str@sel!.preguntaA + E @:suario& E + str@sel!.usuarioA + EAE

class 1dmin&
pass
4omo ecSamos antes, .mejor e0pl1cito 2ue impl1cito.. #efinimos un mRtoo __str__ en toas las clases para tener una escripciLn ;humana; e caa
ob$eto, tanto a la hora e esarrollar como e gestionar en la interfaE aministrativa. La clase aniaa 1dmin sirve para Hue la clase mare apareEca en la
interfaE aministrativa.
La clase .suario herea irectamente e la clase .ser e #$ango 2django.contrib.auth.models.User3.
%or Oltimo, haremos Hue #$ango sincronice la informaciLn Hue tiene e los moelos con el sistema relacional 2vamos, Hue cree las tablas necesarias3:
python manage.py syncdb Cste comano tambiRn crearP las tablas necesarias para la aplicaciLn aministrativa y el sistema e gestiLn e usuarios
2e hecho nos peirP los atos necesarios para crear un ;superusuario;3. -i arrancamos el servior y apuntamos nuestro navegaor a
http&//localhost&)/admin/ veremos en marcha la interfaE e aministraciLn:
Lo primero Hue hemos hecho ha sio crear un grupo 24oncursantes3 y asignarle el permiso e ;crear ob$etos el tipo 7espuesta;. #espuRs creamos unos
cuantos usuarios y les hacemos miembros el grupo. "a tenemos un sistema e control e acceso, permisos bastante granulares 2estos usuarios sLlo
porPn crear ob$etos el tipo 7espuesta, pero no moificarlos o borrarlos3 sin escribir naa e cLigo.
/iguente: implementando la autentificaci'n
Cl siguiente paso es relativamente sencillo si utiliEamos las faciliaes Hue #$ango nos proporciona. Cl ecoraor Xlogin_re7uired en el paHuete
django.contrib.auth.decorators funciona e la siguiente manera: si el usuario no estP autentificao, reirige a una plantilla o template e valiaciLn
2registration9login.html por efecto3:
-i estP autentificao, la funciLn ;ecoraa; 2 index en este caso3 se e$ecuta normalmente.
La primera pantalla Hue Hueremos Hue el usuario autenfificao vea es un listao e preguntas clasificao por categorSas. Vste serSa nuestro ;inex.html;,
pero, como hemos icho, Hueremos Hue el usuario se valie antes. Neamos cLmo hacerlo.
Cn urls.py aMaimos una entraa para ;mapear; la irecciLn ;0; 2raSE el sitio3 a la funciLn ;index; en views.py:
urls.py 2extracto3:
!rom settings import 94"61_/,,.
urlpatterns B patterns@%%$
(r'^/?$', 'Trivial.juego.views.index'),
@r%Radmin/%$ include@%django.contrib.admin.urls%AA$
A
urlpatterns +B patterns@%django.views%$
@r%Rsite_media/@.SAT%$ %static.serve%$ U%document_root%& 94"61_/,,.VA$
A
" nuestra funciLn 2
@
3 en views.py serSa algo asS:
Xlogin_re7uired
de! index@re7uestA&
categorias B +ategoria.objects.all@A
preguntas B ;regunta.objects.all@A
respuestas B /espuesta.objects.!ilter@usuarioBre7uest.userA
return render_to_response@%index.html%$
U%categorias%& categorias$
%preguntas%& preguntas$
%respuestas%& respuestas$
%usuario%& re7uest.user$V
A
QDuR hace este index5 7ecoge toas las categorSas, preguntas y respuestas el usuario valiao y se las ;pasa; a una plantilla o template llamaa
;index.html;. TambiRn le pasa los atos el usuario 2re2uest.user3. 4omo hemos especificao Hue hay un login previo, poemos estar seguros e Hue
esta variable ;usuario; tiene atos correctos.
Neamos ahora la plantilla o template index.html, no sin antes repasar cLmo funcionan los templates en #$ango:
UY extends Ebase.htmlE YV
UY block cuerpo YV
QstrongJ0istado de preguntasQ/strongJ
UY i! categorias YV
UY regroup preguntas by categoria as agrupado YV
QulJ
UY !or grupo in agrupado YV
QliJUU grupo.grouper VVQ/liJ
QulJ
UY !or item in grupo.list YV
QliJQa hre!BEpregunta/UUitem.idVV/EJUU item.titulo VVQ/aJQbrJ
UY !or r in respuestas YV
UY i!e7ual item r.pregunta YV
Znbsp>Znbsp>0a pregunta ya ha sido respondida.
UY endi!e7ual YV
UY end!or YV
Q/liJ
UY end!or YV
Q/ulJ
UY end!or YV
Q/ulJ
UY else YV
QpJ-o hay categorWasQ/pJ
UY endi! YV
QpJQa hre!BE/accounts/logout/EJ"esconectarQ/aJQ/pJ
UY endblock YV
Cn este template comprobamos si hay categorSas UY i! categorias YV y mostramos en forma e listas aniaas toas las preguntas e caa
categorSa con la etiHueta UY regroup preguntas by categoria as agrupado YV y lo Hue sigue. %ara caa pregunta comprobamos si tiene
una respuesta asociaa:
UY !or r in respuestas YV
UY i!e7ual item r.pregunta YV
Znbsp>Znbsp>0a pregunta ya ha sido respondida.
UY endi!e7ual YV
UY end!or YV
Csta comprobaciLn es algo ineficiente 2en caa pregunta itera sobre toas las respuestas3 pero no lo hemos refinao por mantener la simplicia. -eguro
Hue se puee hacer me$or :&3
Cn este template tambiRn estamos utiliEano una caracterSstica muy Otil: la herencia e plantillas. Cn una plantilla aparte 2 base.html3 efinimos un
esHueleto con unos bloHues e contenio Hue caa una e las plantillas ;hi$as; se encarga e completar con UY block lo7uesea YV
AsS HuearSa nuestra pantalla inicial:
1ormulario de preguntas
4uano el usuario sigue el enlace 2Qa hre!BEpregunta/UUitem.idVV/EJUU item.titulo VVQ/aJ3 Hue presenta caa pregunta en la
plantilla index.html se le irige a la pPgina Hue llamaremos ;ficha e pregunta;. Cstas son las moificaciones Hue hemos introucio:
urls.py 2extracto3:
urlpatterns B patterns@%%$
@r%R/NT%$ %.rivial.juego.views.index%A$
(r'^pregunta/(\d+)/$', 'Trivial.juego.views.pregunta'),
@r%Radmin/%$ include@%django.contrib.admin.urls%AA$
A
Cn views.py efinimos la funciLn ;pregunta;:
!rom django.shortcuts import render_to_response
!rom django.contrib.auth.decorators import login_re7uired
!rom .rivial.juego.models import ;regunta$ /espuesta
Xlogin_re7uired
de! pregunta@re7uest$ idA&
pregunta B ;regunta.objects.get@idBidA
try&
respuesta B /espuesta.objects.get@preguntaBid$ usuarioBre7uest.userA
except ,bject"oes-ot4xist&
respuesta B -one
return render_to_response@%pregunta.html%$
U%pregunta%& pregunta$
%respuesta%& respuesta$
%tiempo%& str@int@time.time@AAA$
V
A
A la funciLn pregunta le llegan os argumentos: re7uest e id, tal y como se efine en urls.py. Lo primero Hue hacemos es buscar la pregunta
corresponiente 2pregunta B ;regunta.objects.get@idBidA3 y luego buscamos la posible respuesta Hue haya poio hacer el usuario en
una anterior visita 2respuesta B /espuesta.objects.get@preguntaBid$ usuarioBre7uest.userA3. -i no hay respuestas
capturamos la excepciLn, asignamos (one a la respuesta y seguimos.
*inalmente, Rsta es la plantilla Hue muestra los atos e una pregunta, pregunta.html:
UY extends Ebase.htmlE YV
UY block cuerpo YV
Qh'J+ategorWa& UU pregunta.categoria VVQ/h'J
Qh8JUU pregunta.titulo VVQ/h8J
UY i! texto_error YV
Qp classBEerrorEJUU texto_error VVQ/pJ
UY endi! YV
Qimg classBE!otoE srcBE/site_media/UU pregunta.!oto VVEJ
QpJUU pregunta.texto VVQ/pJ
UY i! respuesta YV
QpJFa has respondido antes a la pregunta.Q/pJ
QpJ.iempo empleado& UU respuesta.tiempo VV segundos.Q/pJ
QpJ4l resultado !ue
UY i! respuesta.resultado YV
+,//4+.,
UY else YV
6-+,//4+.,
UY endi! YV
Q/pJ
UY else YV
Q!orm methodBEpostE actionBE/responder/EJ
Qinput typeBEhiddenE nameBEpreguntaE valueBEUU pregunta.id VVEJ
Qinput typeBEhiddenE nameBEtiempoE valueBEUU tiempo VVEJ
Qinput typeBEradioE valueBEUU pregunta.respuesta_# VVE
nameBErespuestaEJUU pregunta.respuesta_# VVQbrJ
Qinput typeBEradioE valueBEUU pregunta.respuesta_' VVE
nameBErespuestaEJUU pregunta.respuesta_' VVQbrJ
Qinput typeBEradioE valueBEUU pregunta.respuesta_8 VVE
nameBErespuestaEJUU pregunta.respuesta_8 VVQbrJ
Qinput typeBEradioE valueBEUU pregunta.respuesta_P VVE
nameBErespuestaEJUU pregunta.respuesta_P VVQbrJ
QbrJ
Qinput typeBEsubmitE valueBE/esponderEJ
Q/!ormJ
UY endi! YV
UY endblock YV
(os encontramos en esta plantilla con una variable 2texto_error3 Hue no hemos asignao ese la funciLn pregunta. Csta variable puee tener un
valor cuano esta plantilla es invocaa ese otra funciLn efinia en views.py 2respuesta3. Lo veremos un poco mPs aelante.
Lo primero Hue comprobamos es si esta pregunta ya ha sio responia. -i es asS, la variable respuesta tenrP un valor istinto a (one. Cn este caso
informamos al usuario el resultao y el tiempo empleao en resolver la pregunta.
-i no hay respuesta, generamos un formulario con las posibles respuestas y os campos ocultos con el i e la pregunta y una marca e tiempo. Cl
formulario apunta a la url /responder/, ahora veremos cLmo lo tratamos.
(espondiendo a la pregunta
#e nuevo, aMaimos una regla al fichero urls.py para procesar las respuestas e los usuarios. Cl fichero HuearSa asS 2versiLn final3:
!rom django.contrib.auth.views import login$ logout
!rom django.con!.urls.de!aults import S
!rom settings import 94"61_/,,.
urlpatterns B patterns@%%$
@r%R/NT%$ %.rivial.juego.views.index%A$
@r%Rpregunta/@?d+A/T%$ %.rivial.juego.views.pregunta%A$
(r'^responder/$', 'Trivial.juego.views.respuesta'),
@r%Raccounts/login/T%$ loginA$
@r%Raccounts/logout/T%$ logout$ U%template_name%& %registration/logout.html% VA$
@r%Radmin/%$ include@%django.contrib.admin.urls%AA$
A
urlpatterns +B patterns@%django.views%$
@r%Rsite_media/@.SAT%$ %static.serve%$ U%document_root%& 94"61_/,,.VA$
A
La funciLn nueva 2respuesta3 se efine en views.py y es asS:
Xlogin_re7uired
de! respuesta@re7uestA&
pregunta B ;regunta.objects.get@idBre7uest.;,3.L%pregunta%MA
i! not re7uest.;,3..has_key@%respuesta%A or re7uest.;,3.L%respuesta%M BB EE&
texto_error B E"ebe elegir una opci[nE
return render_to_response@%pregunta.html%$
U%pregunta%& pregunta$
%texto_error%& texto_error$
%tiempo%& str@int@time.time@AAA$
V
A
else&
opcion B re7uest.;,3.L%respuesta%M>
respuesta B /espuesta@A
respuesta.pregunta B pregunta
respuesta.usuario B re7uest.user
respuesta.tiempo B int@time.time@AA - int@re7uest.;,3.L%tiempo%MA
i! pregunta.respuesta_correcta BB opcion&
respuesta.resultado B #
else&
respuesta.resultado B
respuesta.save@A
return render_to_response@%respuesta.html%$
U%pregunta%& pregunta$
%respuesta%& respuesta$
%opcion%& opcion$
V
A
%rimero localiEamos la pregunta 2nos llega el id en la variable %,-T 8pregunta83. #espuRs comprobamos Hue han pulsao uno e los ;raiobutton; e
respuesta 2re7uest.;,3.L%respuesta%M3. -i no han responio, reirigimos e nuevo a la pPgina e pregunta pasano un mensa$e e error.
-i han responio, creamos un ob$eto 7espuesta asociao a la pregunta y al usuario. TambiRn asignamos a esta respuesta el tiempo Hue se ha tarao en
resolver la pregunta y el resultao. #espuRs, reirigimos a la plantilla %respuesta.html% pasano el ob$eto pregunta, el ob$eto respuesta reciRn
creao y la opciLn Hue habSan seleccionao.
respuesta.html:
UY extends Ebase.htmlE YV
UY block cuerpo YV
Qh'J/esultado de la preguntaQ/h'J
Qh8JUU pregunta.titulo VVQ/h8J
Qimg classBE!otoE srcBE/site_media/UU pregunta.!oto VVEJ
QpJUU pregunta.texto VVQ/pJ
QpJ/espuesta seleccionada& UU opcion VVQ/pJ
QpJ/espuesta correcta& UU pregunta.respuesta_correcta VVQ/pJ
UY i! respuesta.resultado YV
QpJ\]4nhorabuena^Q/pJ
UY else YV
QpJ\]=as !allado^Q/pJ
UY endi! YV
QpJ.iempo empleado& UU respuesta.tiempo VV segundos.Q/pJ
UY endblock YV
-encillo, simplemente mostramos los atos el ob$eto respuesta, la pregunta asociaa y lo Hue el usuario responiL.
#onclusiones
6emos hecho una aplicaciLn web muy bPsica y sencilla, pero emuestra Hue muchas tareas complicaas y9o teiosas e implementar nos la
proporciona la herramienta Django. La interfaE aministrativa y el sistema e autentificaciLn nos han salio ;gratis;. %robablemente, la
implementaciLn e estas os funcionaliaes nos hubiesen llevao bastante tiempo.
,bservamos tambiRn la extrema sencilleE en el esarrollo: con cuatro elementos con unas funciones muy concretas 2urls, moelos, vistas y
plantillas3 tenemos perfectamente separaas la lLgica, los atos y la presentaciLn. 4ompPrese esta simplicia con la tSpica aplicaciLn K=CC, por
sencilla Hue sea. (o hay color. Due las soluciones basaas en K=CC sean mPs ;potentes;, escalables, robustas, etc. no lo voy a negar. %ero,
Qacaso se necesita siempre esa ;potencia;5.
(o hemos esarrollao una aplicaciLn similar con 7ails, por lo Hue no serSa muy $usto ecir Hue #$ango es mPs fPcil o me$or. Lo Hue si Hue
poemos ecir, por lo Hue hemos visto y leSo es Hue #$ango aporta varias caracterSsticas Hue ahorran mucho traba$o, en este caso la interfaE
aministrativa y el sistema e autentificaciLn 2
I
3.
-otas
<. Cstoy un poco cansao e la palabra ;framework;. A partir e ahora y en lo Hue Huea e tutorial voy a emplear el tRrmino ;herramienta;,
aunHue no sea el mPs aecuao :&3
=. #isculpen el lengua$e soeE, a veces se me escapa alguna palabrota :&3
T. La ocumentaciLn Hue he utiliEao son estas os fuentes: la ocumentaciLn e #$ango y el libro ;The #$ango Book;.
U. 1na e las cosas Hue mPs me ha gustao e #$ango es la ocumentaciLn: completa, accesible y, sobre too, centrali2ada. +e gusta mucho mPs
Hue, por e$emplo, la ocumentaciLn e 7ails: tutoriales ispersos por internet, falta e un Snice por tLpicos, ... (o voy a ser malpensao, pero
a la impresiLn Hue lo Hue Huieren es Hue compres el libro Hue ha escrito el autor e 7ails.
F. (o voy a ser muy estricto con cuestiones e normaliEaciLn el iseMo e la base e atos. %robablemente serSa me$or tener una tabla aparte para
las posibles respuestas con una clave apuntano a la pregunta. +antenremos este iseMo 2incorrecto3 por sencilleE.
J. Cs una aplicaciLn web muy bien termina y personaliEable, naa Hue ver con el ;scaffol; bPsico Hue nos puee proporcionar 7uby ,n 7ails. -i
una aplicaciLn reHuiere una interfaE aministrativa para mane$ar los istintos ob$etos en 7ails hay Hue programarla. #$ango nos la a ya hecha.
>. 7ails por efecto no tiene naa similar, aunHue existe un ;plugin; para 7ails Hue implementa un sistema e autentificaciLn. (o lo he utiliEao,
asS Hue no pueo valorarlo.
@. *unciLn, no mRtoo. Intencionaamente, los iseMaores e #$ango han eciio Hue no hay necesia e liiar con ob$etos para atener a una
simple peticiLn.
I. 7ails tambiRn proporciona un montLn e funciones, ata$os y utiliaes Hue ahorran tiempo 2la primera Hue se me viene a la cabeEa es la
maravillosa optionsWfromWcollectionWforWselect3.
<?.7ollo legal: este es un ocumento e libre ifusiLn 2licencia 4reative 4ommons ;by&nc&sa;3 con algunas limitaciones. Leer el texto e la
licencia completo para mPs etalles.

Potrebbero piacerti anche