Escribiendo su primera aplicación en Django, parte 2

This tutorial begins where Tutorial 1 left off. We’ll set up the database, create your first model, and get a quick introduction to Django’s automatically-generated admin site.

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 – The name of your database. If you’re using SQLite, the database will be a file on your computer; in that case, NAME should be the full absolute path, including filename, of that file. The default value, BASE_DIR / 'db.sqlite3', will store the file in your project directory.

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:

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), .tables (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

A model is the single, definitive source of information about your data. It contains the essential fields and behaviors of the data you’re storing. Django follows the DRY Principle. The goal is to define your data model in one place and automatically derive things from it.

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:

polls/models.py
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 y Choice.

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í:

mysite/settings.py
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" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
    "question_text" varchar(200) NOT NULL,
    "pub_date" timestamp with time zone NOT NULL
);
--
-- Create model Choice
--
CREATE TABLE "polls_choice" (
    "id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
    "choice_text" varchar(200) NOT NULL,
    "votes" integer NOT NULL,
    "question_id" bigint 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 y choice. (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 the DEFERRABLE parts; it’s telling PostgreSQL to not enforce the foreign key until the end of the transaction.
  • It’s tailored to the database you’re using, so database-specific field types such as auto_increment (MySQL), bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (PostgreSQL), or integer primary key autoincrement (SQLite) are handled for you automatically. Same goes for the quoting of field names – e.g., using double quotes or single quotes.
  • 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:

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

We’re using this instead of simply typing «python», because manage.py sets the DJANGO_SETTINGS_MODULE environment variable, which gives Django the Python import path to your mysite/settings.py file.

Once you’re in the shell, explore the database API:

>>> 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=datetime.timezone.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:

polls/models.py
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:

polls/models.py
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.

Save these changes and start a new Python interactive shell by running python manage.py shell again:

>>> 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 (defined as "choice_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

Now, open a web browser and go to «/admin/» on your local domain – e.g., http://127.0.0.1:8000/admin/. You should see the admin’s login screen:

Django admin login screen

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:

Django admin index page

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 de 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:

polls/admin.py
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:

Django admin index page, now with polls displayed

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:

Polls change list page

Haga clic en la pregunta «¿Qué pasa?» para editarla:

Editing form for question object

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:

History page for question object

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.

Back to Top