---
title: "Escribiendo su primera aplicación Django, parte 7"
version: 6.1
locale: es
source: https://docs.djangoproject.com/es/6.1/intro/tutorial07/
canonical: https://djangodocs.dev/es/6.1/intro/tutorial07/
---
# Escribiendo su primera aplicación Django, parte 7

This tutorial begins where [Tutorial 6](/es/6.1/intro/tutorial06/) left off.
We’re continuing the web-poll application and will focus on customizing
Django’s automatically-generated admin site that we first explored in
[Tutorial 2](/es/6.1/intro/tutorial02/).

> **Dónde obtener ayuda:**
>
> Si tiene problemas para seguir este tutorial, diríjase a la sección [Obteniendo ayuda](/es/6.1/faq/help/) del FAQ.

## Personalice el formulario del sitio administrativo

Al registrar el modelo `Question` con admin.site.register (Question)\`, Django pudo construir una representación del formulario predeterminado. A menudo, usted querrá personalizar el aspecto y el funcionamiento del formulario del sitio administrativo. Usted hará esto indicándole a Django las opciones que desee cuando registre el objeto.

Veamos cómo funciona esto reordenando los campos en el formulario de edición. Reemplace la línea `admin.site.register(Question)` con:

*`polls/admin.py`*

```python
from django.contrib import admin

from .models import Question

class QuestionAdmin(admin.ModelAdmin):
    fields = ["pub_date", "question_text"]

admin.site.register(Question, QuestionAdmin)
```

Usted seguirá este patrón - cree una clase de modelo del sitio administrativo y luego pásela como el segundo argumento a `admin.site.register()` \- cada vez que necesite cambiar las opciones del sitio administrativo para un modelo.

Esta modificación concreta citada anteriormente hace que la «Publication date» se anteponga al campo «Question»:

![Los campos han sido reordenados](intro/_images/admin07.png)

Esto no es tan impresionante con sólo dos campos, pero para los formularios del sitio administrativo con docenas de campos, elegir un orden intuitivo es un detalle importante de usabilidad.

Y hablando de formularios con docenas de campos, es posible que desee dividir el formulario en grupos de campos:

*`polls/admin.py`*

```python
from django.contrib import admin

from .models import Question

class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        (None, {"fields": ["question_text"]}),
        ("Date information", {"fields": ["pub_date"]}),
    ]

admin.site.register(Question, QuestionAdmin)
```

El primer elemento de cada tupla en [`fieldsets`](/es/6.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.fieldsets) es el título del grupo de campos. Así es como se ve el formulario ahora:

![Form has fieldsets now](intro/_images/admin08t.png)

## Agregando objetos relacionados

OK, tenemos nuestra página de administración Question, pero una `Question` tiene varias `Choice`s, y la página de administración no muestra opciones.

Sin embargo.

There are two ways to solve this problem. The first is to register `Choice`
with the admin just as we did with `Question`:

*`polls/admin.py`*

```python
from django.contrib import admin

from .models import Choice, Question

# ...
admin.site.register(Choice)
```

Ahora «Choices» es una opción disponible en el sitio administrativo de Django. El formulario «Add choice» se ve así:

![Choice admin page](intro/_images/admin09.png)

En ese formulario, el campo «Question» es un cuadro de selección que contiene cada pregunta de la base de datos. Django sabe que una [`ForeignKey`](/es/6.1/ref/models/fields/#django.db.models.ForeignKey) debería estar representada en el sitio administrativo como un cuadro `<select>`. En nuestro caso, solo existe una pregunta en este momento.

Also note the «Add another question» button (displayed as a plus sign) to the
right of the «Question» field. Every `ForeignKey` relationship gets this
button for free. When you click this button, you’ll get a popup window with the
«Add question» form. If you add a question in that window and click «Save»,
Django will save the question to the database and dynamically add it as the
selected choice on the «Add choice» form you’re looking at.

Pero, en realidad, se trata de una forma ineficiente de añadir objetos `Choice` al sistema. Sería mejor si usted pudiese agregar muchas Choices directamente cuando crea el objeto `Question`. Hagámoslo realidad.

Elimine la llamada `register()` para el modelo `Choice`. Después, edite el código de registro de `Question` para que diga:

*`polls/admin.py`*

```python
from django.contrib import admin

from .models import Choice, Question

class ChoiceInline(admin.StackedInline):
    model = Choice
    extra = 3

class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        (None, {"fields": ["question_text"]}),
        ("Date information", {"fields": ["pub_date"], "classes": ["collapse"]}),
    ]
    inlines = [ChoiceInline]

admin.site.register(Question, QuestionAdmin)
```

Esto le indica a Django: «Los objetos `Choice` se editan en la página de administración `Question`. De forma predeterminada, proporciona suficientes campos para 3 opciones.»

Cargue la página «Add question» para ver cómo se ve:

![Add question page now has choices on it](intro/_images/admin10t.png)

Funciona así: hay tres espacios para las opciones relacionadas – como indica `extra` – y cada vez que usted vuelva a la página «Change» para un objeto ya creado, usted obtiene otros tres espacios adicionales.

At the end of the three current slots you will find an «Add another Choice»
link. If you click on it, a new slot will be added. If you want to remove the
added slot, you can click on the X to the top right of the added slot. This
image shows an added slot:

![Additional slot added dynamically](intro/_images/admin14t.png)

One small problem, though. It takes a lot of screen space to display all the
fields for entering related `Choice` objects. For that reason, Django offers
a tabular way of displaying inline related objects. To use it, change the
`ChoiceInline` declaration to read:

*`polls/admin.py`*

```python
class ChoiceInline(admin.TabularInline): ...
```

Los objetos relacionados se muestran en un formato más compacto basado en tablas con esa `TabularInline` (en lugar de `StackedInline`):

![Add question page now has more compact choices](intro/_images/admin11t.png)

Note that there is an extra «Delete?» column that allows removing rows added
using the «Add another Choice» button and rows that have already been saved.

## Personalice la lista de cambios del sitio administrativo

Ahora que la página de administración Question se ve bien, vamos a hacer algunos ajustes a la página «change list» – la que muestra todas las preguntas en el sistema.

Así es como se ve en este momento:

![Polls change list page](intro/_images/admin04t.png)

By default, Django displays the `str()` of each object. But sometimes it’d be
more helpful if we could display individual fields. To do that, use the
[`list_display`](/es/6.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_display) admin option, which is a
list of field names to display, as columns, on the change list page for the
object:

*`polls/admin.py`*

```python
class QuestionAdmin(admin.ModelAdmin):
    # ...
    list_display = ["question_text", "pub_date"]
```

For good measure, let’s also include the `was_published_recently()` method
from [Tutorial 2](/es/6.1/intro/tutorial02/):

*`polls/admin.py`*

```python
class QuestionAdmin(admin.ModelAdmin):
    # ...
    list_display = ["question_text", "pub_date", "was_published_recently"]
```

Ahora la página de lista de cambios de preguntas se ve así:

![Polls change list page, updated](intro/_images/admin12t.png)

Usted puede hacer clic en las cabeceras de las columnas para ordenar por esos valores ,excepto en el caso de la cabecera `was_published_recently`, porque ordenar por la salida de un método arbitrario no está soportado. También tenga en cuenta que la cabecera de la columna `was_published_recently` es, por defecto, el nombre del método (con guiones bajos reemplazado con espacios), y que cada línea contiene la representación de cadena de la salida.

You can improve that by using the [`display()`](/es/6.1/ref/contrib/admin/#django.contrib.admin.display)
decorator on that method (extending the `polls/models.py` file that was
created in [Tutorial 2](/es/6.1/intro/tutorial02/)), as follows:

*`polls/models.py`*

```python
from django.contrib import admin

class Question(models.Model):
    # ...
    @admin.display(
        boolean=True,
        ordering="pub_date",
        description="Published recently?",
    )
    def was_published_recently(self):
        now = timezone.now()
        return now - datetime.timedelta(days=1) <= self.pub_date <= now
```

For more information on the properties configurable via the decorator, see
[`list_display`](/es/6.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_display).

Edite de nuevo su archivo `polls/admin.py` y añada una mejora a la página `Question` en la lista de cambios: filtre utilizando el [`list_filter`](/es/6.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter). Agregue la siguiente línea a `QuestionAdmin`:

```
list_filter = ["pub_date"]
```

Eso añade una barra lateral «Filter» que le permite a los usuarios filtrar la lista de cambios mediante el campo `pub_date`:

![Polls change list page, updated](intro/_images/admin13t.png)

El tipo de filtro que se muestra depende del tipo de campo que está filtrando. Debido a que la `pub_date` es una [`DateTimeField`](/es/6.1/ref/models/fields/#django.db.models.DateTimeField), Django sabe dar opciones de filtro apropiadas: «Any date», «Today», «Past 7 days», «This month», «This year».

Esto se perfila bien. Vamos a añadir un poco de capacidad de búsqueda:

```
search_fields = ["question_text"]
```

Eso añade un cuadro de búsqueda en la parte superior de la lista de cambios. Cuando alguien introduce los términos de búsqueda, Django buscará el campo `question_text`. Usted puede utilizar tantos campos como desee, sin embargo, ya que utiliza una petición `LIKE` detrás de bastidores, limitar el número de campos de búsqueda a un número razonable le facilitará la búsqueda a su base de datos.

Now’s also a good time to note that change lists give you free pagination. The
default is to display 100 items per page.

> **See also**
>
> The following [`ModelAdmin`](/es/6.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin) options allow
> further customization of change lists:
> [`list_per_page`](/es/6.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_per_page),
> [`search_fields`](/es/6.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_fields),
> [`date_hierarchy`](/es/6.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.date_hierarchy), and
> [`list_display`](/es/6.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_display).

## Personalice el aspecto del sitio administrativo

Está claro que tener «Django administration» en la parte superior de cada página del sitio administrativo es ridículo. Es sólo texto del marcador de posición.

You can change it, though, using Django’s template system. The Django admin is
powered by Django itself, and its interfaces use Django’s own template system.

### Personalizando las plantillas de su *proyecto*

Create a `templates` directory in your `djangotutorial` directory.
Templates can live anywhere on your filesystem that Django can access. (Django
runs as whatever user your server runs.) However, keeping your templates within
the project is a good convention to follow.

Abra el archivo de configuraciones (`mysite/settings.py`, recuerde) y añada una opción [`DIRS`](/es/6.1/ref/settings/#std-setting-TEMPLATES-DIRS) en la opción [`TEMPLATES`](/es/6.1/ref/settings/#std-setting-TEMPLATES):

*`mysite/settings.py`*

```python
TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "templates"],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
            ],
        },
    },
]
```

[`DIRS`](/es/6.1/ref/settings/#std-setting-TEMPLATES-DIRS) es una lista de directorios del sistema de archivos utilizada para comprobar cuando se cargan las plantillas de Django; es una ruta de búsqueda.

> **Organizando las plantillas**
>
> Al igual que los archivos estáticos *podríamos* tener todas nuestras plantillas juntas en un gran directorio de plantillas y funcionaría perfectamente. Sin embargo, las plantillas que pertenecen a una determinada aplicación deberían ser puestas en el directorio de plantillas de la aplicación (por ejemplo, `polls/templates`) en lugar del directorio (`templates`) del proyecto. Hablaremos con más detalle *por qué* hacemos esto en el [tutorial de aplicaciones reutilizables](/es/6.1/intro/reusable-apps/).

Now create a directory called `admin` inside `templates`, and copy the
template `admin/base_site.html` from within the default Django admin
template directory in the source code of Django itself
([django/contrib/admin/templates](https://github.com/django/django/blob/stable/6.1.x/django/contrib/admin/templates)) into that directory.

> **¿Dónde están los archivos fuente de Django?**
>
> Si tiene dificultad para encontrar donde están localizados los archivos fuente de Django en su sistema, ejecute el siguiente comando:
>
> ```console
> $ python -c "import django; print(django.__path__)"
> ```
>
> *Windows*
>
> ```doscon
> ...\> py -c "import django; print(django.__path__)"
> ```

Then, edit the file and replace
`{{ site_header|default:_('Django administration') }}` (including the curly
braces) with your own site’s name as you see fit. You should end up with
a section of code like:

```html+django
{% block branding %}
<div id="site-name"><a href="{% url 'admin:index' %}">Polls Administration</a></div>
{% if user.is_anonymous %}
  {% include "admin/color_theme_toggle.html" %}
{% endif %}
{% endblock %}
```

Utilizamos este método para enseñarle cómo reemplazar las plantillas. En un proyecto real, usted probablemente utilizaría el atributo [`django.contrib.admin.AdminSite.site_header`](/es/6.1/ref/contrib/admin/#django.contrib.admin.AdminSite.site_header) para hacer este ajuste particular con mayor facilidad.

Este archivo de plantillas contiene una gran cantidad de texto como `{% block branding%}` y `{{title}}`. Las etiquetas `{%` y `{{` son parte del lenguaje de plantillas de Django. Cuando Django crea `admin/base_site.html`, este lenguaje de plantilla será evaluado para generar la página HTML definitiva, como vimos en el [Tutorial 3](/es/6.1/intro/tutorial03/).

Note that any of Django’s default admin templates can be overridden. To
override a template, do the same thing you did with `base_site.html` – copy
it from the default directory into your custom directory, and make changes.

### Personalizando las plantillas de su *aplicación*

Los lectores agudos preguntarán: «Pero si [`DIRS`](/es/6.1/ref/settings/#std-setting-TEMPLATES-DIRS) estaba vacío por defecto, ¿cómo estaba buscando Django las plantillas predeterminadas del sitio administrativo? La respuesta es que, como [`APP_DIRS`](/es/6.1/ref/settings/#std-setting-TEMPLATES-APP_DIRS) está fijado como `True`, Django busca automáticamente un subdirectorio `templates/` dentro de cada paquete de la aplicación, para su uso como una solución alternativa (no olvide que `django.contrib.admin` es una aplicación).

Nuestra aplicación de encuestas no es muy compleja y no necesita las plantillas personalizadas del sitio administrativo. Pero si se hiciera más sofisticada y requiriera la modificación de las plantillas convencionales del sitio administrativo de Django para algunas de sus funciones, sería más sensato modificar las plantillas de la *aplicación*, en lugar de las que están en el *proyecto*. De esa manera, usted podría incluir la aplicación encuestas en cualquier nuevo proyecto y estaría seguro de que esta encontraría las plantillas personalizadas que necesitaba.

Consulte la [documentación sobre la carga de plantillas](/es/6.1/topics/templates/#template-loading) para obtener más información sobre cómo Django encuentra sus plantillas.

## Personalice la página del índice del sitio administrativo

En este sentido, es posible que desee personalizar la apariencia de la página de índice del sitio administrativo de Django.

Por defecto, muestra todas las aplicaciones que se encuentran en [`INSTALLED_APPS`](/es/6.1/ref/settings/#std-setting-INSTALLED_APPS) que han sido registradas en la aplicación del sitio administrativo en orden alfabético. Es posible que desee realizar cambios significativos en el diseño. Después de todo, el índice es probablemente la página más importante del sitio administrativo y debería ser fácil de usar.

The template to customize is `admin/index.html`. (Do the same as with
`admin/base_site.html` in the previous section – copy it from the default
directory to your custom template directory). Edit the file, and you’ll see it
uses a template variable called `app_list`. That variable contains every
installed Django app. Instead of using that, you can hardcode links to
object-specific admin pages in whatever way you think is best.

When you’re comfortable with the admin, read [part 8 of this
tutorial](/es/6.1/intro/tutorial08/) to learn how to use third-party packages.
