Escribiendo su primera aplicación en Django, parte 2¶
Este tutorial comienza donde quedó el Tutorial 1. Vamos a configurar la base de datos, crear su primer modelo y recibir una introducción rápida al sitio administrativo generado automáticamente de Django.
Dónde obtener ayuda:
If you’re having trouble going through this tutorial, please head over to the Getting Help section of the FAQ.
Configuración de la base de datos¶
Ahora, abra el archivo mysite/settings.py
. Es un módulo normal de Python con variables de nivel de módulo que representan la configuración de Django.
Por defecto la configuración utiliza SQLite. Si tiene poca experiencia con bases de datos o su interés es solo probar Django esta es la opción más fácil. SQLite está incluido en Python por lo que no tendrá que instalar nada más para soportar su base de datos. Sin embargo, al iniciar su primer proyecto real, es posible que desee utilizar una base de datos más potente como PostgreSQL para evitar dolores de cabeza en el futuro al tener que cambiar entre base de datos.
Si desea utilizar otra base de datos, instale los conectores de base de datos apropiados, y cambie las siguientes claves en el ítem DATABASES
'default'
para que se ajusten a la configuración de conexión de la base de datos:
ENGINE
– bien sea'django.db.backends.sqlite3'
,'django.db.backends.postgresql_psycopg2'
,'django.db.backends.mysql'
, o'django.db.backends.oracle'
. Otros backends también están disponibles.NAME
–el nombre de su base de datos. Si está utilizando SQLite, la base de datos será un archivo en su computadora; en ese casoNAME
debe ser la ruta absoluta completa, incluyendo el respectivo nombre del archivo. El valor predeterminado,os.path.join(BASE_DIR, 'db.sqlite3')
, guardará el archivo en el directorio de su proyecto.
Si no está utilizando SQLite como su base de datos, se deben añadir ajustes adicionales tales como USER
, PASSWORD
, y HOST
se deben añadir. Para más información, vea la documentación de referencia para DATABASES
.
Para bases de datos distintas a SQLite
Si está utilizando una base de datos SQLite, asegúrese de que ha creado ya la base de datos en este punto. Hágalo con el comando «CREATE DATABASE database_name;
» en la consola interactiva de la base de datos.
Del mismo modo asegúrese de que la base de datos proporcionada en el archivo mysite/settings.py
tiene permisos de tipo «create database». Esto permitirá la creación automática de test database que será necesaria en un tutorial posterior.
Si estás usando SQLite, no es necesario crear nada de antemano - el archivo de base de datos se creará automáticamente cuando sea necesario.
Mientras que usted está editando el archivo mysite/settings.py
, configure TIME_ZONE
a su zona horaria.
Además, observe que la configuración de INSTALLED_APPS
se encuentra en la parte superior del archivo. Esta contiene los nombres de todas las aplicaciones Django que están activadas en esta instancia de Django. Las aplicaciones se pueden usar en diversos proyectos y usted puede empaquetar y distribuirlas para que otras personas las puedan utilizar en sus proyectos.
Por defecto, INSTALLED_APPS
contiene las siguientes aplicaciones y Django viene equipado con todas ellas:
django.contrib.admin
- El sitio administrativo. Usted lo utilizará dentro de poco.django.contrib.auth
– Un sistema de autenticación.django.contrib.contenttypes
– Un framework para los tipos de contenido.django.contrib.sessions
– Un framework de sesión.django.contrib.messages
– Un framework de mensajería.django.contrib.staticfiles
– Un framework para la gestión de archivos estáticos.
Estas aplicaciones se incluyen de forma predeterminada como una conveniencia para el caso común.
Algunas de estas aplicaciones utilizan al menos una tabla de base de datos, por lo que necesitamos crear las tablas en la base de datos antes de poder utilizarlas. Para ello, ejecute el siguiente comando:
$ python manage.py migrate
...\> py manage.py migrate
The migrate
command looks at the INSTALLED_APPS
setting
and creates any necessary database tables according to the database settings
in your mysite/settings.py
file and the database migrations shipped
with the app (we’ll cover those later). You’ll see a message for each
migration it applies. If you’re interested, run the command-line client for your
database and type \dt
(PostgreSQL), SHOW TABLES;
(MariaDB, MySQL),
.schema
(SQLite), or SELECT TABLE_NAME FROM USER_TABLES;
(Oracle) to
display the tables Django created.
Para los minimalistas
Como dijimos anteriormente, las aplicaciones predeterminadas se incluyen para el caso común, pero no todos las necesitan. Si usted no necesita alguna o ninguna de ellas, no dude en dejar fuera o borrar las línea(s) correspondientes desde INSTALLED_APPS
antes de ejecutar el comando migrate
. El comando migrate
solo ejecutará migraciones para aplicaciones en INSTALLED_APPS
.
Creando modelos¶
A continuación definiremos sus modelos, sobre todo su estructura de base de datos, con metadatos adicionales.
Filosofía
Un modelo es la fuente única y definitiva de información sobre sus datos. Contiene los campos esenciales y los comportamientos de los datos que usted guarda. Django sigue el Principio DRY. El objetivo es definir el modelo de datos en un solo lugar y derivar cosas de este automáticamente.
This includes the migrations - unlike in Ruby On Rails, for example, migrations are entirely derived from your models file, and are essentially a history that Django can roll through to update your database schema to match your current models.
In our poll app, we’ll create two models: Question
and Choice
. A
Question
has a question and a publication date. A Choice
has two
fields: the text of the choice and a vote tally. Each Choice
is associated
with a Question
.
These concepts are represented by Python classes. Edit the
polls/models.py
file so it looks like this:
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
Here, each model is represented by a class that subclasses
django.db.models.Model
. Each model has a number of class variables,
each of which represents a database field in the model.
Cada campo está representado por una instancia de una clase Field
, por ejemplo, CharField
para campos de caracteres y DateTimeField
para variables de tiempo y fecha. Esto le dice a Django qué tipo de datos cada campo contiene.
El nombre de cada instancia Field
(por ejemplo, question_text
o pub_date
)` es el nombre del campo, en formato adaptado al lenguaje de la máquina. Va a usar este valor en el código Python y su base de datos va a usarlo como el nombre de la columna.
Puede emplear un primer argumento posicional opcional a una Field
para designar un nombre legible por humanos. Ese se utiliza en varias partes introspectivas de Django y sirve al mismo tiempo como documentación. Si no se proporciona este campo, Django usará el nombre legible por la máquina. En este ejemplo, sólo hemos definido un nombre legible para Question.pub_date
. Para el resto de los campos en este modelo, el nombre del campo legible por la máquina servirá como el nombre legible por humanos.
Algunas clases Field
precisan argumentos. La clase CharField
, por ejemplo, requiere que usted le asigne un max_length
. Esta se utiliza no sólo en el esquema de la base de datos, sino también en la validación como veremos dentro de poco.
Una clase Field
también puede tener varios argumentos opcionales; en este caso, le hemos fijado al default
el valor de votes
en 0.
Por último, tenga en cuenta que una relación se define usando ForeignKey
. Eso le indica a Django que cada Choice
se relaciona con un sola Question
. Django es compatible con todas las relaciones de bases de datos comunes: varias a una, varias a varias y una a una.
Activando los modelos¶
Ese pequeño fragmento de código de modelo le proporciona a Django mucha información. Con él Django es capaz de:
- Crear un esquema de base de datos para esta aplicación (oraciones
CREATE TABLE
). - Crear una API de acceso a la base datos Python para acceder a los objetos
Question
yChoice
.
Pero primero tenemos que indicarle a nuestro proyecto que la aplicación polls
está instalada.
Filosofía
Las aplicaciones Django son «conectables»: Usted puede utilizar una aplicación en diversos proyectos y puede distribuir aplicaciones porque ellas no necesitan estar atadas a una determinada instalación de Django.
Para incluir la aplicación en nuestro proyecto necesitamos agregar una referencia a su clase de configuración en la configuración INSTALLED_APPS
. La clase PollsConfig
está en el archivo polls/apps.py
por lo que su ruta punteada es 'polls.apps.PollsConfig'
. Edite el archivo mysite/settings.py y agregue la ruta punteada a la configuración INSTALLED_APPS
. Se verá así:
INSTALLED_APPS = [
'polls.apps.PollsConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
Ahora Django sabe incluir la aplicación polls
. Vamos a ejecutar otro comando:
$ python manage.py makemigrations polls
...\> py manage.py makemigrations polls
Usted debe ver algo similar a lo siguiente:
Migrations for 'polls':
polls/migrations/0001_initial.py
- Create model Question
- Create model Choice
Al ejecutar makemigrations
, usted le indica a Django que ha realizado algunos cambios a sus modelos (en este caso, ha realizado cambios nuevos) y que le gustaría que los guarde como una migración.
Migrations are how Django stores changes to your models (and thus your
database schema) - they’re files on disk. You can read the migration for your
new model if you like; it’s the file polls/migrations/0001_initial.py
.
Don’t worry, you’re not expected to read them every time Django makes one, but
they’re designed to be human-editable in case you want to manually tweak how
Django changes things.
Hay un comando que ejecutará las migraciones para usted y gestionará el esquema de base de datos automáticamente; este se denomina migrate
, y hablaremos de ello en un momento, pero primero, vamos a ver cuál SQL esa migración ejecutaría . El comando sqlmigrate
recibe nombres de migración y devuelve su SQL:
$ python manage.py sqlmigrate polls 0001
...\> py manage.py sqlmigrate polls 0001
Debería ver algo similar a lo siguiente (lo hemos reformateado para facilitar la lectura):
BEGIN;
--
-- Create model Question
--
CREATE TABLE "polls_question" (
"id" serial NOT NULL PRIMARY KEY,
"question_text" varchar(200) NOT NULL,
"pub_date" timestamp with time zone NOT NULL
);
--
-- Create model Choice
--
CREATE TABLE "polls_choice" (
"id" serial NOT NULL PRIMARY KEY,
"choice_text" varchar(200) NOT NULL,
"votes" integer NOT NULL,
"question_id" integer NOT NULL
);
ALTER TABLE "polls_choice"
ADD CONSTRAINT "polls_choice_question_id_c5b4b260_fk_polls_question_id"
FOREIGN KEY ("question_id")
REFERENCES "polls_question" ("id")
DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id");
COMMIT;
Tenga en cuenta lo siguiente:
- La salida exacta variará dependiendo de la base de datos que esté utilizando. El ejemplo anterior se genera para PostgreSQL.
- Los nombres de las tablas se generan automáticamente combinando el nombre de la aplicación (
polls
) y el nombre del modelo en minúscula;question
ychoice
. (Usted puede anular este comportamiento) - Las claves primarias (IDs) se agregan automáticamente. (Usted también puede anular esto)
- Convencionalmente, Django añade
"_id"
al nombre del campo de la clave externa (sí, usted también puede anular esto) - The foreign key relationship is made explicit by a
FOREIGN KEY
constraint. Don’t worry about theDEFERRABLE
parts; it’s telling PostgreSQL to not enforce the foreign key until the end of the transaction. - Se adapta a la base de datos que está utilizando, así que los tipos de campos específicos de la bases de datos como
auto_increment
(MySQL),serial
(PostgreSQL) ointeger primary key autoincrement
(SQLite) se gestionan de forma automática. Lo mismo se aplica para la cita de nombres de campos, por ejemplo, el uso de comillas dobles o comillas simples. - The
sqlmigrate
command doesn’t actually run the migration on your database - instead, it prints it to the screen so that you can see what SQL Django thinks is required. It’s useful for checking what Django is going to do or if you have database administrators who require SQL scripts for changes.
Si le interesa, usted también puede ejecutar el comando python manage.py check
; este revisa cualquier problema en su proyecto sin hacer migraciones o modificar la base de datos.
A continuación, ejecute de nuevo el comando migrate
para crear esas tablas modelos en su base de datos:
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
Rendering model states... DONE
Applying polls.0001_initial... OK
...\> py manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
Rendering model states... DONE
Applying polls.0001_initial... OK
El comando migrate
toma todas las migraciones que no han sido aplicadas (Django rastrea cuales se aplican utilizando una tabla especial en su base de datos llamada django_migrations
) y las ejecuta contra su base de datos; básicamente, sincronizando los cambios que usted ha realizado a sus modelos con el esquema en la base de datos.
Las migraciones son muy potentes y le permiten modificar sus modelos con el tiempo, a medida que desarrolla su proyecto, sin necesidad de eliminar su base de datos o las tablas y hacer otras nuevas. Este se especializa en la actualización de su base de datos en vivo, sin perder datos. Vamos a hablar de ellas en mayor profundidad más tarde en el tutorial, pero por ahora, recuerde la guía de tres pasos para hacer cambios de modelo:
- Cambie sus modelos (en
models.py
). - Ejecute el comando
python manage.py makemigrations
para crear migraciones para esos cambios - Ejecute el comando
python manage.py migrate
para aplicar esos cambios a la base de datos.
The reason that there are separate commands to make and apply migrations is because you’ll commit migrations to your version control system and ship them with your app; they not only make your development easier, they’re also usable by other developers and in production.
Lea la documentación del django-admin para obtener información detallada sobre lo que puede hacer la utilidad manage.py
.
Jugando con la API¶
Ahora vayamos al shell interactivo de Python y juguemos con la API gratuita que Django le proporciona. Para llamar el shell de Python, utilice este comando:
$ python manage.py shell
...\> py manage.py shell
Estamos usando esto en vez de simplemente escribir «python», porque el archivo manage.py
establece la variable de entorno DJANGO_SETTINGS_MODULE
, que le suministra a Django la ruta de importación de Python para su archivo mysite/settings.py
.
Una vez que esté en el shell, explore la API de base de datos:
>>> from polls.models import Choice, Question # Import the model classes we just wrote.
# No questions are in the system yet.
>>> Question.objects.all()
<QuerySet []>
# Create a new Question.
# Support for time zones is enabled in the default settings file, so
# Django expects a datetime with tzinfo for pub_date. Use timezone.now()
# instead of datetime.datetime.now() and it will do the right thing.
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())
# Save the object into the database. You have to call save() explicitly.
>>> q.save()
# Now it has an ID.
>>> q.id
1
# Access model field values via Python attributes.
>>> q.question_text
"What's new?"
>>> q.pub_date
datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>)
# Change values by changing the attributes, then calling save().
>>> q.question_text = "What's up?"
>>> q.save()
# objects.all() displays all the questions in the database.
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>
Un segundo. <Question: Question object (1)>
no es una representación útil de este objeto. Arreglemos esto modificando el modelo Question
(en el archivo polls/models.py
) y agregando un metodo __str__()
a los dos modelos, Question
y Choice
:
from django.db import models
class Question(models.Model):
# ...
def __str__(self):
return self.question_text
class Choice(models.Model):
# ...
def __str__(self):
return self.choice_text
Es importante añadir los métodos __str__()
a sus modelos, no solo para su conveniencia al lidiar con la línea de comandos interactiva, sino también porque las representaciones de objetos se usan en todo el sitio administrativo generado automáticamente de Django.
Let’s also add a custom method to this model:
import datetime
from django.db import models
from django.utils import timezone
class Question(models.Model):
# ...
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
Tenga en cuenta la adición de import datetime
y from django.utils import timezone
, para hacer referencia el módulo estándar de Python datetime
y las herramientas relacionadas con el huso horario de Django django.utils.timezone
respectivamente. Si usted no está familiarizado con el manejo de la zona horaria en Python, usted puede aprender más en la documentación de soporte de zona horaria.
Guarde estos cambios e inicie un nuevo shell interactivo Python ejecutando de nuevo python manage.py shell
:
>>> from polls.models import Choice, Question
# Make sure our __str__() addition worked.
>>> Question.objects.all()
<QuerySet [<Question: What's up?>]>
# Django provides a rich database lookup API that's entirely driven by
# keyword arguments.
>>> Question.objects.filter(id=1)
<QuerySet [<Question: What's up?>]>
>>> Question.objects.filter(question_text__startswith='What')
<QuerySet [<Question: What's up?>]>
# Get the question that was published this year.
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Question.objects.get(pub_date__year=current_year)
<Question: What's up?>
# Request an ID that doesn't exist, this will raise an exception.
>>> Question.objects.get(id=2)
Traceback (most recent call last):
...
DoesNotExist: Question matching query does not exist.
# Lookup by a primary key is the most common case, so Django provides a
# shortcut for primary-key exact lookups.
# The following is identical to Question.objects.get(id=1).
>>> Question.objects.get(pk=1)
<Question: What's up?>
# Make sure our custom method worked.
>>> q = Question.objects.get(pk=1)
>>> q.was_published_recently()
True
# Give the Question a couple of Choices. The create call constructs a new
# Choice object, does the INSERT statement, adds the choice to the set
# of available choices and returns the new Choice object. Django creates
# a set to hold the "other side" of a ForeignKey relation
# (e.g. a question's choice) which can be accessed via the API.
>>> q = Question.objects.get(pk=1)
# Display any choices from the related object set -- none so far.
>>> q.choice_set.all()
<QuerySet []>
# Create three choices.
>>> q.choice_set.create(choice_text='Not much', votes=0)
<Choice: Not much>
>>> q.choice_set.create(choice_text='The sky', votes=0)
<Choice: The sky>
>>> c = q.choice_set.create(choice_text='Just hacking again', votes=0)
# Choice objects have API access to their related Question objects.
>>> c.question
<Question: What's up?>
# And vice versa: Question objects get access to Choice objects.
>>> q.choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
>>> q.choice_set.count()
3
# The API automatically follows relationships as far as you need.
# Use double underscores to separate relationships.
# This works as many levels deep as you want; there's no limit.
# Find all Choices for any question whose pub_date is in this year
# (reusing the 'current_year' variable we created above).
>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
# Let's delete one of the choices. Use delete() for that.
>>> c = q.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()
Para obtener más información sobre las relaciones de modelos, consulte Accediendo a objetos relacionados. Para más información sobre cómo utilizar guiones bajos para realizar búsquedas de campo a través de la API, consulte :ref: Búsquedas de campos <field-lookups-intro>. Para más detalles sobre la API de base de datos, consulte nuestra Referencia de API de base de datos.
Presentando el sitio administrativo de Django¶
Filosofía
La generación de sitios administrativos para su personal o clientes para agregar, modificar y eliminar contenido es un trabajo aburrido que no requiere mucha creatividad. Por esa razón, Django automatiza completamente la creación de interfaces de sitios administrativos para los modelos.
Django fue escrito en un entorno de sala de redacción, con una separación muy clara entre «los editores de contenido» y el sitio «público». Los administradores del sitio utilizan el sistema para agregar noticias, eventos, resultados deportivos, etc., y ese contenido se muestra en el sitio público. Django resuelve el problema de crear una interfaz unificada para los administradores del sitio para editar el contenido.
El sitio admin no está destinado a ser utilizado por los visitantes del sitio. Es para los administradores del sitio.
Creando un usuario del admin¶
Primero tendremos que crear un usuario que pueda iniciar sesión en el sitio administrativo. Ejecute el siguiente comando:
$ python manage.py createsuperuser
...\> py manage.py createsuperuser
Introduzca su nombre de usuario deseado y pulse enter.
Username: admin
A continuación se le solicitará su dirección de correo electrónico deseada:
Email address: admin@example.com
El paso final es introducir su contraseña. Se le pedirá que introduzca su contraseña dos veces, la segunda vez como confirmación de la primera.
Password: **********
Password (again): *********
Superuser created successfully.
Inicie el servidor de desarrollo¶
El sitio administrativo de Django se activa de forma predeterminada. Vamos a iniciar el servidor de desarrollo y a explorarlo.
Si el servidor no está en marcha, inícielo de la siguiente forma:
$ python manage.py runserver
...\> py manage.py runserver
A continuación, abra un navegador Web y vaya a «/admin/» en su dominio local, por ejemplo, http://127.0.0.1:8000/admin/. Usted debe ver la pantalla de inicio de sesión del sitio administrativo:
Since translation is turned on by default, if
you set LANGUAGE_CODE
, the login screen will be displayed in the
given language (if Django has appropriate translations).
Acceda al sitio administrativo¶
Ahora, intente iniciar sesión con la cuenta de superusuario que creó en el paso anterior. Debería ver la página de índice del sitio administrativo de Django:
Usted debería ver algunos tipos de contenidos editables: grupos y usuarios. Ellos son proporcionados por django.contrib.auth
, el framework de autenticación enviado por Django.
Haga que la aplicación encuesta se pueda modificar en el sitio administrativo¶
Pero ¿Dónde está nuestra aplicación encuesta? No se muestra en la página de índice del sitio administrativo.
Only one more thing to do: we need to tell the admin that Question
objects
have an admin interface. To do this, open the polls/admin.py
file, and
edit it to look like this:
from django.contrib import admin
from .models import Question
admin.site.register(Question)
Explore la funcionalidad gratuita del sitio administrativo¶
Ahora que hemos registrado el modelo Question
, Django sabe que se debe desplegar en la página de índice del sitio administrativo:
Haga clic en «Questions». Ahora usted está en la página «lista de cambios» para las preguntas. Esta página muestra todas las preguntas de la base de datos y le permite seleccionar una para modificarla. Ahí está la pregunta «¿Qué pasa?» que creamos anteriormente:
Haga clic en la pregunta «¿Qué pasa?» para editarla:
Cosas a tener en cuenta aquí:
- El formulario se genera automáticamente a partir del modelo
Question
. - Los diferentes tipos de campos del modelo (
DateTimeField
,CharField
) corresponden al widget de entrada HTML adecuado. Cada tipo de campo sabe cómo mostrarse en el sitio administrativo de Django. - Cada
DateTimeField
tiene atajos de JavaScript sin restricciónes. Las fechas tienen un atajo «Hoy» y una ventana emergente del calendario, mientras que las horas tienen un atajo «Ahora» y una ventana emergente conveniente que enumera las horas que se registran comúnmente.
La parte inferior de la página le da un par de opciones:
- Guardar – Guarda los cambios y retorna a la página de la lista de cambios para este tipo de objeto.
- Guardar y continuar editando – Guarda los cambios y recarga la página del sitio administrativo para este objeto.
- Guardar y añadir otro – Guarda los cambios y carga un nuevo formulario vacio para este tipo de objeto.
- Eliminar – Muestra una página de confirmación de eliminación.
Si el valor de «Date published» no coincide con la hora en la que creó la pregunta en el Tutorial 1, probablemente significa que se olvidó de establecer el valor correcto para la configuración de TIME_ZONE
. Modifíquelo, recargue la página y compruebe que aparezca el valor correcto.
Modifique la «Date published» haciendo clic en los atajos «Hoy» y «Ahora». Después, haga clic en «Guardar y continuar editando». Luego haga clic en «Historial» en la parte superior derecha. Usted verá una página que enumera todos los cambios realizados a este objeto a través del sitio administrativo de Django, con el registro de tiempo y el nombre de usuario de la persona que realizó el cambio:
Cuando se sienta cómodo con la API de modelos y se haya familiarizado con el sitio administrativo, lea la parte 3 de este tutorial para aprender sobre cómo agregar más vistas a nuestra aplicación encuestas.