# Rédaction de votre première appli Django, partie 2 : Exploration de l'interface d'admin auto-générée

Ce tutoriel commence là où le Tutoriel 1 s'achève. Nous continuons l'application de sondage Web et allons nous focaliser sur le site d'administration généré automatiquement par Django.

# Activer le site d'admin

Le site d'admin de Django n'est pas activé par défaut -- c'est une fonctionnalité optionnelle. Pour activer le site d'admin dans votre installation, suivez ces trois points:

> - Ajoutez `"django.contrib.admin"` dans votre option `INSTALLED_APPS`.
> - Lancez `python manage.py syncdb`. Puisque vous avez ajouté une nouvelle application à `INSTALLED_APPS`, les tables de la base de données ont besoin d'être mises à jour.
> - Éditez votre fichier `monsite/urls.py` et décommentez la ligne en dessous de « Uncomment this for admin: ». Ce fichier est une URLconf; nous creuserons le sujet des URLconfs dans le prochain tutoriel. Pour l'instant, tout ce que vous avez besoin de savoir est qu'il définit la racine des URLs de vos applications.

# Démarrer le serveur de développement

Démarrons le serveur de développement et explorons le site d'administration.

Rappel du Tutoriel 1 : vous démarrez le serveur de développement comme ceci:

    python manage.py runserver

À présent, ouvrez un navigateur Web et allez au « /admin/ » de votre domaine local -- par exemple, http://127.0.0.1:8000/admin/. Vous devriez voir l'écran de connexion à l'interface d'admin:

# Entrez dans le site d'admin

Maintenant, essayez de vous identifier (vous avez créé un compte superutilisateur dans la première partie du tutoriel vous vous rappelez ?). Vous devriez voir la page d'index de l'interface d'admin de Django:

Par défaut, vous deviez voir deux types de contenu éditable : groupes et utilisateurs. Ce sont des caractéristiques du noyau que Django intègre par défaut.

# Rendre l'appli de sondage modifiable via l'interface d'admin

Mais où est notre appli de sondage ? Il n'est pas affiché sur la page d'index de l'interface d'admin.

Juste une chose à faire : Nous avons besoin de spécifier dans le modèle `Poll` que les objets `Poll` ont une interface d'admin. Éditez le fichier `monsite/polls/models.py` et faites les modifications suivantes pour ajouter une classe interne `Admin`:

    class Poll(models.Model):
        # ...
        class Admin:
            pass

La `classe Admin` contiendra les paramètres qui contrôle comment ce modèle apparaît dans l'interface d'admin de Django. Tous les paramètres sont optionnels, cependant, créer ainsi une classe vide signifie « donne une interface d'administration à cet objet en utilisant les options par défaut ».

Maintenant rechargez la page d'admin de Django pour voir les changements. Notez que vous n'avez pas à redémarrer le serveur de développement -- le serveur auto-recharge votre projet, du coup toute modification dans le code peut être visualisé immédiatement dans votre navigateur.

# Explorer les fonctionnalités libres de l'interface d'admin

Maintenant que `Poll` a sa classe interne `Admin`, Django sait qu'il devrait être affiché sur la page d'index du site d'admin:

Cliquez sur « Polls ». À présent, vous êtes sur la page « de listage pour modification » des sondages. Cette page affiche tous les sondages de la base de données et vous permet d'en choisir un pour l'éditer. Il y a le sondage « Quoi de neuf ? » que nous avons créé dans le premier tutoriel:

Cliquez sur le sondage « Quoi de neuf ? » pour l'éditer :

Choses à noter ici:

- Le formulaire est généré automatiquement depuis le modèle Poll.
- Les différents types de champs du modèle (`models.DateTimeField`, `models.CharField`) correspondent au widget d'entrée HTML approprié. Chaque type de champ sait comment s'afficher dans l'interface d'admin de Django.
- Chaque `DateTimeField` reçoit des raccourcis Javascript libre. Les dates obtiennent un raccourci « Aujourd'hui » et un calendrier en popup, et les heures obtiennent un raccourci « Maintenant » et une popup pratique qui liste les heures couramment saisies.

La partie inférieur de la page vous propose une série d'opérations:

- Sauver -- Sauvegarde les modifications et retourne à la page de listage pour modification pour ce type d'objet.
- Sauver et continuer les modifications -- Sauvegarde les modifications et recharge la page d'administration de cet objet.
- Sauver et ajouter un nouveau -- Sauvegarde les modifications et charge un nouveau formulaire vierge pour ce type d'objet.
- Supprimer -- Affiche la page de confirmation de la suppression.

Changez la « Date de publication » en cliquant sur les raccourcis « Aujourd'hui » et « Maintenant ». Puis cliquez sur « Sauver et continuer les modifications ». Ensuite, cliquez sur « Historique » en haut à droite de la page. Vous verrez une page listant toutes les modifications effectuées sur cet objet via l'interface d'administration de Django, accompagnées des date et heure, ainsi que du nom de l'utilisateur qui a fait ce changement:

# Personnaliser le formulaire d'administration

Prenez quelques minutes pour vous émerveiller devant le code que vous n'avez pas dû écrire.

Personnalisons un peu tout ça. Nous pouvons réordonner les champs en ajoutant explicitement un paramètre `fields` à `Admin`:

    class Admin:
        fields = (
            (None, {'fields': ('pub_date', 'question')}),
        )

Cela fait que la « Date de publication » apparaît en premier au lieu d'être en second:

Ce n'est pas spécialement impressionnant avec seulement deux champs, mais pour un formulaire d'administration avec des douzaines de champs, choisir un ordre intuitif est un détail d'utilisation important.

Et en parlant de formulaires avec des douzaines de champs, vous voudriez sûrement séparer le formulaire en plusieurs sous-ensembles:

    class Admin:
        fields = (
            (None, {'fields': ('question',)}),
            ('Informations calendaires', {'fields': ('pub_date',)}),
        )

Le premier élément de chaque tuple dans `fields` est le titre de la sous-partie. Voici ce à quoi notre formulaire ressemble à présent:

Vous pouvez assigner des classes HTML arbitraires à chaque sous-ensemble. Django fournit une classe `"collapse"` qui affiche un sous-ensemble particulier, initialement replié. C'est une fonctionnalité utile lorsque vous avez un long formulaire qui contient un certain nombre de champs qui ne sont pas couramment utilisés:

    class Admin:
        fields = (
            (None, {'fields': ('question',)}),
            ('Informations calendaires', {'fields': ('pub_date',), 'classes': 'collapse'}),
        )

# Ajout d'objets liés

OK, nous avons notre page d'administration de sondages Poll. Mais un sondage `Poll` possède plusieurs choix `Choices`, et la page d'admin n'affiche aucun choix.

Pour le moment.

Il y a deux façon de résoudre ce problème. Le premier et de donner au modèle `Choice` sa propre classe interne `Admin`, tout comme nous l'avons fait pour `Poll`. Voici ce que ça donnerait:

    class Choice(models.Model):
        # ...
        class Admin:
            pass

Maintenant les choix sont une option disponible dans l'interface d'admin de Django. Le formulaire « Add choice » ressemble à ceci:

Dans ce formulaire, le champ « Poll » est une boîte de sélection contenant tous les sondages de la base de données. Django sait qu'une instance de `ForeignKey` devrait être représentée dans l'interface d'admin par une boîte `<select>`. Dans notre cas, seul un sondage existe à ce point.

Notez également le lien « Add another » à côté de « Poll ». Chaque objet avec une relation ForeignKey vers un autre reçoit ce lien gratuitement. Quand vous cliquez sur « Add another », vous obtiendrez une fenêtre en popup quand le formulaire « Add poll ». Si vous ajoutez un sondage dans cette fenêtre et que vous cliquez sur « Sauver », Django sauvegardera le sondage dans la base de données et l'ajoutera dynamiquement comme choix sélectionné dans le formulaire « Add choice » que vous étiez en train de remplir.

Mais, franchement, c'est une manière inefficace d'ajouter des objets « Choice » dans le système. Ça serait mieux si vous pouviez ajouter un groupe de choix « Choices » directement lorsque vous créez l'objet « Poll ». Faisons de cette façon.

Retirez la classe `Admin` du modèle Choice. Puis, éditez le champ `ForeignKey(Poll)` comme ceci:

    poll = models.ForeignKey(Poll, edit_inline=models.STACKED, num_in_admin=3)

Ça dit à Django: « Les objets Choice sont édités dans la page d'administration de Poll. Par défaut, fournir assez de champs pour 3 choix ».

Ensuite, modifiez les autres champs dans `Choice` pour les mettre à `core=True`:

    choice = models.CharField(maxlength=200, core=True)
    votes = models.IntegerField(core=True)

Ça dit à Django: « Quand tu édites un Choice dans la page d'admin de Poll, les champs 'choice' et 'votes' sont requis. La présence d'au moins un d'eux signifie que l'ajout d'un nouvel objet Choice, et que la mise à blanc de tous ces champs signifie la suppression de cet objet Choice existant ».

Charger la page « Add poll » pour voir à quoi ça ressemble:

Ça marche comme ceci : Il y a trois compartiments pour les choix « Choices » liés -- comme spécifié par `num_in_admin` -- mais chaque fois que vous revenez sur la page « Changement » d'un objet déjà créé, vous obtenez un compartiment supplémentaire (cela signifie que vous n'avez pas mis en dur de limite sur le nomble d'objets liés qui peuvent être ajoutés). Si vous aviez voulu de la place pour trois choix « Choices » supplémentaire à chaque fois que vous modifiez le sondage, vous auriez utilisé `num_extra_on_change=3`.

Un petit problème cependant. Ça prend beaucoup de place d'afficher tous les champs pour saisir les objets « Choice » liés. C'est pour cette raison que Django offre une alternative d'affichage en ligne des objets liés:

    poll = models.ForeignKey(Poll, edit_inline=models.TABULAR, num_in_admin=3)

Avec ce `edit_inline=models.TABULAR` (au lieu de `models.STACKED`), les objets liés sont affichés dans un format plus compact, comme un tableau:

# Personnaliser la liste pour modification de l'interface d'admin

Maintenant que la page d'admin des sondage « Poll » a un bon look, arrangeons un peu la page de « listage pour modification » -- celle qui affiche tous les sondages du système.

Voici à quoi ça ressemble à ce point:

Par défaut, Django affiche le `str()` de chaque objet. Mais parfois, ça serait plus utile si nous pouvions afficher des champs individuels. Dans ce but, utilisez l'option `list_display`, qui est un tuple de nom de champs à afficher, en colonnes, sur la page de listage pour modification de l'objet:

    class Poll(models.Model):
        # ...
        class Admin:
            # ...
            list_display = ('question', 'pub_date')

Juste pour la démonstration, incluons également la méthode perso `was_published_today` du Tutoriel 1:

    list_display = ('question', 'pub_date', 'was_published_today')

À présent la page de listage pour modification des sondage ressemble à ceci:

Vous pouvez cliquer sur les en-têtes de colonne pour trier selon ces valeurs -- sauf dans le cas de l'en-tête `was_published_today`, parce que le tri selon le résultat d'une méthode arbitraire n'est pas supporté. Notez aussi que l'en-tête de la colonne pour `was_published_today` est, par défaut, le nom de la méthode (avec les underscores remplacés par des espaces. Mais vous pouvez changer cela en donnant à cette méthode un attribut `short_description`:

    def was_published_today(self):
        return self.pub_date.date() == datetime.date.today()
    was_published_today.short_description = u'Publié aujourd\'hui ?'

Ajoutons une nouvelle amélioration à la page de listage pour modification de sondages : des filtres. Ajoutez la ligne suivante à `Poll.Admin`:

    list_filter = ['pub_date']

Cela ajoute une sidebar « Filter » qui permet aux gens de filtrer la liste pour modification selon le champ `pub_date`:

Le type de filtre affiché dépend du type de champs que vous êtes en train de filtrer. Parce que `pub_date` est un DateTimeField, Django sait donner les options de filtrage par défaut pour les DateTimeFields: « Toutes les dates », « Aujourd'hui », « Les 7 derniers jours », « Ce mois-ci », « Cette année ».

Ça a meilleure forme. Ajoutons une fonctionnalité de recherche:

    search_fields = ['question']

Cela ajoute une boîte de recherche en haut de la liste pour modification. Quand quelqu'un saisit des termes de recherche, Django va rechercher dans le champ `question`. Vous pouvez indiquer autant de champs que vous le désirez -- bien qu'il utilise un requête `LIKE` derrière, restez raisonnable pour garder votre base de données performante.

Enfin, parce que les objets « Poll » ont des dates, il serait pratique d'effectuer un classement par date. Ajoutez cette ligne:

    date_hierarchy = 'pub_date'

Cela ajoute une navigation hiérarchique, par date, en haut de la page de listage pour modification. Au premier niveau, il affiche toutes les années disponibles. Puis il affine le classement en mois et, finalement, en jours.

C'est maintenant le bon moment de notez que les listes pour modification vous laissent une grande liberté de pagination. Par défaut, 50 items sont affichés par page. La pagination de listes pour modification, les boîtes de recherche, les filtres, les hiérarchies calendaires et le tri selon l'en-tête de colonne, tout fonctionne ensemble comme vous pensez qu'ils le devraient.

# Personnaliser l'apparence de l'interface d'administration

C'est clair, avoir « Django administration » et « example.com » en haut de chaque page d'administration est ridicule. C'est juste du texte de substitution.

C'est facile à modifier en utilisant le système de template de Django. Le site d'administration de Django est fait en Django lui-même, et ses interfaces utilisent le système de template propre à Django. (Ça devient métaphysique !)

Ouvrez votre fichier de configuration (`monsite/settings.py`, souvenez-vous) et examinez l'option `TEMPLATE_DIRS`. `TEMPLATE_DIRS` est un tuple de répertoires du système de fichiers pour vérifier d'où les templates Django sont chargées. C'est un chemin de recherche.

Par défaut, `TEMPLATE_DIRS` est vide. Donc, ajoutons-lui une ligne pour dire à Django où nos templates sont situées:

    TEMPLATE_DIRS = (
        "/home/mestemplates", # Remplacez par votre propre répertoire.
    )

À présent, copiez la template `admin/base_site.html` depuis le répertoire par défaut des templates de l'interface d'admin de Django (`django/contrib/admin/templates`) vers un sous-répertoire `admin` se trouvant dans le répertoire que vous avez défini dans `TEMPLATE_DIRS`. Par exemple, si votre `TEMPLATE_DIRS` contient `"/home/mestemplates"`, comme ci-dessus, copiez `django/contrib/admin/templates/admin/base_site.html` vers `/home/mytemplates/admin/base_site.html`. N'oubliez pas de créer au préalable ce sous-répertoire `admin`.

Ensuite, éditez simplement le fichier et remplacez le texte générique de Django par le nom et l'URL de votre propre site.

Notez que tous les templates de l'interface d'admin par défaut de Django peuvent être remplacés. Pour remplacer un template, faites simplement la même chose qu'avec `base_site.html` -- copiez le depuis le répertoire par défaut dans votre répertoire personnel, et faites les modifications.

Les lecteurs avisés pourront demander : Mais si `TEMPLATE_DIRS` était vide par défaut, comment Django trouvait-il les templates par défaut de l'interface d'admin ? La réponse est que, par défaut, Django regarde automatiquement dans un éventuel sous-répertoire `templates/` à l'intérieur de chaque paquetage d'appli, pour l'utiliser en dernier recours. Lisez la documentation sur les types de chargeur pour des informations complètes.

# Personnaliser la page d'index de l'interface d'admin

De la même manière, vous voudriez sûrement personnaliser l'apparence de la page d'index de l'interface d'admin de Django.

Par défaut, il affiche toutes les applis disponibles, selon la configuration de votre `INSTALLED_APPS`. Mais l'ordre dans lequel il affiche les éléments est aléatoire, et vous voudriez peut-être faire des modifications significatives sur la mise en page. Après tout, la page d'index est probablement la page la plus importante du site d'administration, donc autant qu'elle soit facile à utiliser.

Le template à personnaliser est `admin/index.html`. (Faites la même chose qu'avec `admin/base_site.html` dans la précédente section -- copiez le depuis le répertoire par défaut vers votre répertoire de templates personnels.) Éditez le fichier, et vous verrez qu'il est utilisé une balise de template appelé `{% get_admin_app_list as app_list %}`. C'est l'instruction magique qui retrouve chaque appli Django installée. Au lieu d'utiliser ça, vous pouvez écrire en dur les liens vers les pages d'administration spécifiques aux objets de la meilleure manière que vous pensez.

Django offre un autre raccourci dans cette procédure. Lancez la commande `python manage.py adminindex polls` pour obtenir un extrait de code de template à inclure dans la template de la page d'index de l'interface d'admin. C'est un point de départ plutôt utile.

Pour des détails complets au sujet de la personnalisation de l'apparence du site d'administration de Django de manière générale, lisez le guide CSS de l'interface d'admin de Django.

Lorsque vous vous serez familiarisé avec le site d'administration, lisez la partie 3 de ce tutoriel pour commencer à travailler avec les vues publiques du sondage.

Vous pouvez maintenant retourner à la page d'accueil des traductions de la documentation de Django.

Cette traduction correspond à la révision 3589 (post 0.95).

### Articles peut-être en rapport

- Le langage de template Django : Pour les auteurs de templates
- Comparaison de TurboGears et Django, deux frameworks web Python
- Rédaction de votre première appli Django, partie 4 : Conception d'un formulaire et vues génériques

### Commentaires

Flipper le 05/08/2006 :

Très bonne traduction, merci!
Il y a un "u" qui traine dans l'attribut short_description...

David, biologeek le 05/08/2006 :

Le « u » est pour le passage en unicode histoire de gérer les accents français.

Eric le 11/09/2006 :

Bonjour,
was_published_today.short_description = u'Publié aujourd\'hui ?'
Créé une erreur d'Unicode :
UnicodeEncodeError at /admin/polls/poll/
'ascii' codec can't encode character u'\xe9' in position 5: ordinal not in range(128)
novice en Python, le seul moyen que j'ai trouvé pour contourner ce probléme c'est d'utiliser la synthaxe suivante :
was_published_today.short_description = 'Publi&eacute; aujourd\'hui ?'
Ca fonctionne ... mais il y a certainement mieux à faire ... :)
Eric

David, biologeek le 11/09/2006 :

Vérifie l'encodage de caractère (ou charset) de ton fichier source. S'il est en utf-8 ça devrait passer (auquel cas, il faudra déclarer la page de template en utf-8 aussi).

Eric le 12/09/2006 :

Entre temps je me suis documenté, j'utilise donc Django avec Ptyhon 2.4, sur windows Xp et ...
A partir de la version 2.3 de Python il est fortement conseillé aux francophones d'inclure un pseudo-commentaire au début de tous leurs scripts Python (obligatoirement à la 1ere ou à la 2ème ligne)
# -*- coding : Latin-1 -*-
Ou bien
# -*- coding : Utf-8 -*-
ce qui est tout de même une meilleure solution que la précédente ;)
Eric

Hafid le 24/08/2010 :

Bonjour,

Tout d'abord je vous remercie pour ce site et pour tout ce travail fourni

Je suis en train d'apprendre à travailler avec django en suivant ce tutorial Mais je rencontre un bug au niveau:
"Rendre l'appli de sondage modifiable via l'interface d'admin". J'ajoute la classe interne Admin dans le fichiers polls/models.py mais rien ne change dans mon admin. Pensez-vous que j'ai oublié un détail de configuration?
NB: je suis sous windows, j'utilise la dernière version de django et python 2.6

Merci d'avance pour votre réponse