Faits saillants de Django : modèles, administration et exploitation de la base de données relationnelle (partie 3)

Publié: 2022-03-10
Résumé rapide ↬ Le panneau d'administration est l'une des fonctionnalités les plus puissantes et les plus flexibles fournies par le framework Web Django, combinant des fonctionnalités instantanées prêtes à l'emploi avec une personnalisation infinie. En utilisant un exemple de projet basé sur un système d'inventaire de bibliothèque, nous utiliserons le panneau d'administration pour en savoir plus sur la création de modèles et l'interaction avec les bases de données relationnelles dans Django.

Avant de commencer, je tiens à noter que les capacités administratives intégrées de Django, même après personnalisation, ne sont pas destinées aux utilisateurs finaux. Le panneau d'administration existe en tant qu'outil de développement, d'opérateur et d'administrateur pour la création et la maintenance de logiciels. Il n'est pas destiné à être utilisé pour donner aux utilisateurs finaux des capacités de modération ou toute autre capacité d'administrateur sur la plate-forme que vous développez.

Cet article repose sur une hypothèse en deux parties :

  1. Le panneau d'administration de Django est si intuitif que vous savez déjà comment l'utiliser.
  2. Le panneau d'administration Django est si puissant que nous pouvons l'utiliser comme un outil pour apprendre à représenter des données dans une base de données relationnelle à l'aide d'un modèle Django.

Je propose ces idées avec la mise en garde que nous aurons encore besoin d'écrire du code de configuration pour activer les capacités plus puissantes du panneau d'administration, et nous aurons toujours besoin d'utiliser l'ORM (mappage objet-relationnel) basé sur les modèles de Django pour spécifier la représentation des données dans notre système.

lecture recommandée

"Django Highlights" est une série présentant des concepts importants du développement web dans Django. Vous voudrez peut-être lire sur la fourniture de flux d'authentification utilisateur sécurisés et suivre une démonstration sur l'utilisation des modèles Django pour écrire des pages complexes.

Plus après saut! Continuez à lire ci-dessous ↓

Mise en place

Nous allons travailler avec un exemple de projet dans cet article. Le projet modélise certaines données qu'une bibliothèque stockerait sur ses livres et ses usagers. L'exemple devrait être assez applicable à de nombreux types de systèmes qui gèrent les utilisateurs et/ou l'inventaire. Voici un aperçu de ce à quoi ressemblent les données :

Modèle de données. ( Grand aperçu )

Veuillez suivre les étapes suivantes pour que l'exemple de code s'exécute sur votre machine locale.

1. Installer des packages

Avec Python 3.6 ou supérieur installé, créez un répertoire et un environnement virtuel. Ensuite, installez les packages suivants :

 pip install django django-grappelli

Django est le framework Web avec lequel nous travaillons dans cet article. ( django-grappelli est un thème de panneau d'administration que nous aborderons brièvement.)

2. Obtenir le projet

Avec les packages précédents installés, téléchargez l'exemple de code à partir de GitHub. Cours:

 git clone https://github.com/philipkiely/library_records.git cd library_records/library

3. Création d'un superutilisateur

À l'aide des commandes suivantes, configurez votre base de données et créez un superutilisateur. L'interface de ligne de commande vous guidera tout au long du processus de création d'un superutilisateur. Votre compte superutilisateur vous permettra d'accéder au panneau d'administration dans un instant, alors assurez-vous de vous souvenir du mot de passe que vous avez défini. Utiliser:

 python manage.py migrate python manage.py createsuperuser

4. Chargement des données

Pour notre exploration, j'ai créé un ensemble de données appelé un appareil que vous pouvez charger dans la base de données (plus d'informations sur la création d'un appareil à la fin de l'article). Utilisez le projecteur pour remplir votre base de données avant de l'explorer dans le panneau d'administration. Cours:

 python manage.py loaddata ../fixture.json

5. Exécution de l'exemple de projet

Enfin, vous êtes prêt à exécuter l'exemple de code. Pour exécuter le serveur, utilisez la commande suivante :

 python manage.py runserver

Ouvrez votre navigateur sur https://127.0.0.1:8000 pour afficher le projet. Notez que vous êtes automatiquement redirigé vers le panneau d'administration à /admin/ . J'ai accompli cela avec la configuration suivante dans library/urls.py :

 from django.contrib import admin from django.urls import path from records import views urlpatterns = [ path('admin/', admin.site.urls), path('', views.index), ]

combiné avec la redirection simple suivante dans records/views.py :

 from django.http import HttpResponseRedirect def index(request): return HttpResponseRedirect('/admin/')

Utilisation du panneau d'administration

Nous l'avons fait! Lorsque vous chargez votre page, vous devriez voir quelque chose comme ceci :

Page principale du panneau d'administration de Django. ( Grand aperçu )

Cette vue est réalisée avec le code passe-partout suivant dans records/admin.py :

 from django.contrib import admin from .models import Book, Patron, Copy admin.site.register(Book) admin.site.register(Copy) admin.site.register(Patron)

Cette vue devrait vous donner une première compréhension des données que le système stocke. Je vais supprimer une partie du mystère : les Groups et les Users sont définis par Django et stockent les informations et les autorisations des comptes sur le système. Vous pouvez en savoir plus sur le modèle User dans un article précédent de cette série. Books , Copys et Patrons sont des tables de la base de données que nous avons créées lors de l'exécution des migrations et remplies en chargeant l'appareil. Notez que Django pluralise naïvement les noms de modèles en ajoutant un "s", même dans des cas comme "copies" où l'orthographe est incorrecte.

Modèle de données. ( Grand aperçu )

Dans notre projet, un Book est un enregistrement avec un titre, un auteur, une date de publication et un ISBN (International Standard Book Number). La bibliothèque conserve une Copy de chaque Book , ou éventuellement plusieurs. Chaque Copy peut être extraite par un Patron , ou pourrait être actuellement archivée. Un Patron est une extension de l' User qui enregistre son adresse et sa date de naissance.

Créer, lire, mettre à jour, détruire

Une fonctionnalité standard du panneau d'administration consiste à ajouter des instances de chaque modèle. Cliquez sur "livres" pour accéder à la page du modèle, puis cliquez sur le bouton "Ajouter un livre" dans le coin supérieur droit. Cela affichera un formulaire que vous pourrez remplir et enregistrer pour créer un livre.

Créer un livre ( Grand aperçu )

La création d'un Patron révèle une autre capacité intégrée du formulaire de création de l'administrateur : vous pouvez créer le modèle connecté directement à partir du même formulaire. La capture d'écran ci-dessous montre la fenêtre contextuelle déclenchée par le signe plus vert à droite du menu déroulant User . Ainsi, vous pouvez créer les deux modèles sur la même page d'administration.

Créez un mécène. ( Grand aperçu )

Vous pouvez créer une COPY via le même mécanisme.

Pour chaque enregistrement, vous pouvez cliquer sur la ligne pour la modifier à l'aide du même formulaire. Vous pouvez également supprimer des enregistrements à l'aide d'une action d'administration.

Actions d'administration

Bien que les fonctionnalités intégrées du panneau d'administration soient très utiles, vous pouvez créer vos propres outils à l'aide d'actions d'administration. Nous allons en créer deux : un pour créer des copies de livres et un pour archiver les livres qui ont été rendus à la bibliothèque.

Pour créer une Copy d'un Book , accédez à l'URL /admin/records/book/ et utilisez le menu déroulant "Action" pour sélectionner "Ajouter une copie de livre(s)" puis utilisez les cases à cocher dans la colonne de gauche du tableau pour sélectionner le ou les livres dont ajouter une copie à l'inventaire.

Créer une action de copie. ( Grand aperçu )

La création de ceci repose sur une méthode de modèle que nous aborderons plus tard. Nous pouvons l'appeler en tant qu'action d'administration en créant une classe ModelAdmin pour le modèle Profile comme suit dans records/admin.py :

 from django.contrib import admin from .models import Book, Patron, Copy class BookAdmin(admin.ModelAdmin): list_display = ("title", "author", "published") actions = ["make_copys"] def make_copys(self, request, queryset): for q in queryset: q.make_copy() self.message_user(request, "copy(s) created") make_copys.short_description = "Add a copy of book(s)" admin.site.register(Book, BookAdmin)

La propriété list_display indique quels champs sont utilisés pour représenter le modèle dans la page de présentation du modèle. La propriété actions répertorie les actions d'administration. Notre action d'administration est définie comme une fonction dans BookAdmin et prend trois arguments : l'objet d'administration lui-même, la requête (la requête HTTP réelle envoyée par le client) et le jeu de requêtes (la liste des objets dont les cases ont été cochées). Nous effectuons la même action sur chaque élément du jeu de requêtes, puis informons l'utilisateur que les actions sont terminées. Chaque action d'administration nécessite une courte description afin qu'elle puisse être correctement identifiée dans le menu déroulant. Enfin, nous ajoutons maintenant BookAdmin lors de l'enregistrement du modèle.

L'écriture d'actions d'administration pour définir des propriétés en bloc est assez répétitive. Voici le code pour archiver un Copy , notez sa quasi-équivalence avec l'action précédente.

 from django.contrib import admin from .models import Book, Patron, Copy class CopyAdmin(admin.ModelAdmin): actions = ["check_in_copys"] def check_in_copys(self, request, queryset): for q in queryset: q.check_in() self.message_user(request, "copy(s) checked in") check_in_copys.short_description = "Check in copy(s)" admin.site.register(Copy, CopyAdmin)

Thème d'administration

Par défaut, Django fournit des styles assez simples pour le panneau d'administration. Vous pouvez créer votre propre thème ou utiliser un thème tiers pour donner un nouveau look au panneau d'administration. Un thème open source populaire est grappelli, que nous avons installé plus tôt dans l'article. Vous pouvez consulter la documentation pour connaître toutes ses fonctionnalités.

L'installation du thème est assez simple, elle ne nécessite que deux lignes. Tout d'abord, ajoutez grappelli à INSTALLED_APPS comme suit dans library/settings.py :

 INSTALLED_APPS = [ 'grappelli', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'records', ]

Ensuite, ajustez library/urls.py :

 from django.contrib import admin from django.urls import path, include from records import views urlpatterns = [ path('grappelli/', include('grappelli.urls')), path('admin/', admin.site.urls), path('', views.index), ]

Une fois ces modifications en place, le panneau d'administration devrait ressembler à ceci :

Panneau d'administration avec thème. ( Grand aperçu )

Il existe un certain nombre d'autres thèmes, et encore une fois, vous pouvez développer le vôtre. Je m'en tiendrai au look par défaut pour le reste de cet article.

Comprendre les modèles

Maintenant que vous êtes à l'aise avec le panneau d'administration et que vous l'utilisez pour naviguer dans les données, examinons les modèles qui définissent la structure de notre base de données. Chaque modèle représente une table dans une base de données relationnelle.

Une base de données relationnelle stocke les données dans une ou plusieurs tables. Chacune de ces tables a une structure de colonnes spécifiée, comprenant une clé primaire (un identifiant unique pour chaque élément) et une ou plusieurs colonnes de valeurs, qui sont de différents types comme des chaînes, des entiers et des dates. Chaque objet stocké dans la base de données est représenté par une seule ligne. La partie « relationnelle » du nom vient de ce qui est sans doute la caractéristique la plus importante de la technologie : la création de relations entre les tables. Un objet (ligne) peut avoir un mappage un-à-un, un-à-plusieurs (clé étrangère) ou plusieurs-à-plusieurs vers des lignes d'autres tables. Nous en discuterons plus loin dans les exemples.

Django, par défaut, utilise SQLite3 pour le développement. SQLite3 est un moteur de base de données relationnelle simple et votre base de données est automatiquement créée en tant que db.sqlite3 la première fois que vous exécutez python manage.py migrate . Nous continuerons avec SQLite3 pour cet article, mais il n'est pas adapté à une utilisation en production, principalement parce que des écrasements sont possibles avec des utilisateurs simultanés. En production, ou lors de l'écriture d'un système que vous avez l'intention de déployer un jour, utilisez PostgreSQL ou MySQL.

Django utilise des modèles pour s'interfacer avec la base de données. Utilisant une partie de l'ORM de Django, le fichier records/models.py inclut plusieurs modèles, ce qui permet de spécifier des champs, des propriétés et des méthodes pour chaque objet. Lors de la création de modèles, nous nous efforçons d'avoir une architecture "Fat Model", dans la limite du raisonnable. Cela signifie qu'une grande partie de la validation des données, de l'analyse, du traitement, de la logique métier, de la gestion des exceptions, de la résolution des cas extrêmes et des tâches similaires doivent être gérées dans la spécification du modèle lui-même. Sous le capot, les modèles Django sont des objets très complexes et fonctionnels avec un comportement par défaut très utile. Cela rend l'architecture "Fat Model" facile à réaliser même sans écrire une quantité substantielle de code.

Passons en revue les trois modèles de notre exemple d'application. Nous ne pouvons pas tout couvrir, car ceci est censé être un article d'introduction, pas la documentation complète du framework Django, mais je soulignerai les choix les plus importants que j'ai faits dans la construction de ces modèles simples.

La classe Book est le plus simple des modèles. Le voici de records/models.py :

 from django.db import models class Book(models.Model): title = models.CharField(max_length=300) author = models.CharField(max_length=150) published = models.DateField() isbn = models.IntegerField(unique=True) def __str__(self): return self.title + " by " + self.author def make_copy(self): Copy.objects.create(book=self)

Tous les champs CharField nécessitent un attribut max_length spécifié. La longueur conventionnelle est de 150 caractères, que je double pour title en cas de titres très longs. Bien sûr, il y a toujours une limite arbitraire, qui pourrait être dépassée. Pour une longueur de texte illimitée, utilisez un TextField . Le champ published est un DateField . L'heure à laquelle le livre a été publié n'a pas d'importance, mais si c'était le cas, j'utiliserais un DateTimeField . Enfin, l'ISBN est un entier (les ISBN ont 10 ou 13 chiffres et correspondent donc tous à la valeur maximale de l'entier) et nous utilisons unique=True car deux livres ne peuvent pas avoir le même ISBN, qui est ensuite appliqué au niveau de la base de données.

Tous les objets ont une méthode __str__(self) qui définit leur représentation sous forme de chaîne. Nous remplaçons l'implémentation par défaut fournie par la classe models.Model et représentons à la place les livres comme "titre par auteur" à tous les endroits où le modèle serait représenté sous forme de chaîne. Rappelez-vous que précédemment nous list_display dans l'objet d'administration de Book pour déterminer quels champs seraient affichés dans la liste du panneau d'administration. Si ce list_display n'est pas présent, la liste d'administration affiche à la place la représentation sous forme de chaîne du modèle, comme c'est le cas pour Patron et Copy .

Enfin, nous avons une méthode sur Book que nous avons appelée dans son action d'administration que nous avons écrite précédemment. Cette fonction crée une Copy liée à une instance donnée d'un Book dans la base de données.

Passant à Patron , ce modèle introduit le concept d'une relation un à un, dans ce cas avec le modèle User intégré. Découvrez-le sur records/models.py :

 from django.db import models from django.contrib.auth.models import User class Patron(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) address = models.CharField(max_length=150) dob = models.DateField() def __str__(self): return self.user.username

Le champ user n'est pas exactement une fonction bijective. Il PEUT y avoir une instance User sans instance Patron associée. Cependant, un User NE PEUT PAS être associé à plus d'une instance de Patron , et un Patron ne peut exister sans exactement une relation avec un utilisateur. Ceci est appliqué au niveau de la base de données et est garanti par la on_delete=models.CASCADE : si une instance User est supprimée, un Profile associé sera supprimé.

Les autres champs et __str__(self) que nous avons déjà vus. Il convient de noter que vous pouvez accéder via une relation un-à-un pour obtenir des attributs, dans ce cas user.username , dans les fonctions d'un modèle.

Pour développer l'utilité des relations de base de données, tournons notre attention vers Copy from records/models.py :

 from django.db import models class Copy(models.Model): book = models.ForeignKey(Book, on_delete=models.CASCADE) out_to = models.ForeignKey(Patron, blank=True, null=True, on_delete=models.SET_NULL) def __str__(self): has_copy = "checked in" if self.out_to: has_copy = self.out_to.user.username return self.book.title + " -> " + has_copy def check_out(self, p): self.out_to = p self.save() def check_in(self): self.out_to = None self.save()

Encore une fois, nous avons vu la plupart de ces éléments auparavant, alors concentrons-nous sur les nouveautés : models.ForeignKey . Une Copy doit être d'un seul Book , mais la bibliothèque peut avoir plusieurs Copy de chaque Book . Un Book peut exister dans la base de données sans que la bibliothèque ait une Copy dans son catalogue, mais une Copy ne peut pas exister sans un Book sous-jacent.

Cette relation complexe est exprimée par la ligne suivante :

 book = models.ForeignKey(Book, on_delete=models.CASCADE)

Le comportement de suppression est le même que celui de Patron en référence à User .

La relation entre une Copy et un Patron est légèrement différente. Une Copy peut être empruntée par un seul Patron , mais chaque Patron peut emprunter autant de Copy que la bibliothèque le lui permet. Cependant, ce n'est pas une relation permanente, la Copy n'est parfois pas extraite. Patron s et Copy s existent indépendamment les uns des autres dans la base de données ; la suppression d'une instance de l'un ne doit supprimer aucune instance de l'autre.

Cette relation est toujours un cas d'utilisation pour la clé étrangère, mais avec des arguments différents :

 out_to = models.ForeignKey(Patron, blank=True, null=True, on_delete=models.SET_NULL)

Ici, avoir blank=True permet aux formulaires d'accepter None comme valeur pour la relation et null=True permet à la colonne de la relation Patron dans la table de Copy de la base de données d'accepter null comme valeur. Le comportement de suppression, qui serait déclenché sur une Copy si une instance Patron était supprimée alors que cette Copy était extraite, consiste à rompre la relation tout en laissant la Copy intacte en définissant le champ Patron sur null.

Le même type de champ, models.ForeignKey , peut exprimer des relations très différentes entre les objets. La seule relation que je n'ai pas pu intégrer proprement dans l'exemple est un champ plusieurs-à-plusieurs, qui est comme un champ un-à-un, sauf que, comme le suggère son nom, chaque instance peut être liée à de nombreuses autres instances et tous les autres et chacun de ceux-ci peuvent être liés à de nombreux autres, comme la façon dont un livre peut avoir plusieurs auteurs, chacun ayant écrit plusieurs livres.

Migrations

Vous vous demandez peut-être comment la base de données sait ce qui est exprimé dans le modèle. D'après mon expérience, les migrations sont l'une de ces choses qui sont assez simples jusqu'à ce qu'elles ne le soient plus, puis elles vous rongent le visage. Voici comment garder votre tasse intacte, pour les débutants : découvrez les migrations et comment interagir avec elles, mais essayez d'éviter d'apporter des modifications manuelles aux fichiers de migration. Si vous savez déjà ce que vous faites, sautez cette section et continuez ce qui fonctionne pour vous.

Dans tous les cas, consultez la documentation officielle pour un traitement complet du sujet.

Les migrations traduisent les changements dans un modèle en changements dans le schéma de la base de données. Vous n'avez pas à les écrire vous-même, Django les crée avec la commande python manage.py makemigrations . Vous devez exécuter cette commande lorsque vous créez un nouveau modèle ou modifiez les champs d'un modèle existant, mais il n'est pas nécessaire de le faire lors de la création ou de la modification de méthodes de modèle. Il est important de noter que les migrations existent en tant que chaîne, chacune faisant référence à la précédente afin qu'elle puisse apporter des modifications sans erreur au schéma de la base de données. Ainsi, si vous collaborez sur un projet, il est important de conserver un historique de migration unique et cohérent dans le contrôle de version. Lorsqu'il y a des migrations non appliquées, exécutez python manage.py migrate pour les appliquer avant d'exécuter le serveur.

L'exemple de projet est distribué avec une seule migration, records/migrations/0001_initial.py . Encore une fois, il s'agit d'un code généré automatiquement que vous ne devriez pas avoir à modifier, donc je ne le copierai pas ici, mais si vous voulez avoir une idée de ce qui se passe dans les coulisses, allez-y et jetez-y un coup d'œil.

Agencements

Contrairement aux migrations, les fixtures ne sont pas un aspect courant du développement de Django. Je les utilise pour distribuer des exemples de données avec des articles, et je ne les ai jamais utilisés autrement. Cependant, parce que nous en avons utilisé un plus tôt, je me sens obligé d'introduire le sujet.

Pour une fois, la documentation officielle est un peu mince sur le sujet. Dans l'ensemble, ce que vous devez savoir, c'est que les appareils sont un moyen d'importer et d'exporter des données de votre base de données dans une variété de formats, y compris JSON, que j'utilise. Cette fonctionnalité existe principalement pour aider avec des choses comme les tests automatisés, et n'est pas un système de sauvegarde ou un moyen de modifier des données dans une base de données en direct. De plus, les appareils ne sont pas mis à jour avec les migrations, et si vous essayez d'appliquer un appareil à une base de données avec un schéma incompatible, cela échouera.

Pour générer un appareil pour l'ensemble de la base de données, exécutez :

 python manage.py dumpdata --format json > fixture.json

Pour charger un appareil, exécutez :

 python manage.py loaddata fixture.json

Conclusion

L'écriture de modèles dans Django est un vaste sujet, et l'utilisation du panneau d'administration en est un autre. En 3 000 mots, je n'ai réussi qu'à présenter chacun. Espérons que l'utilisation du panneau d'administration vous a donné une meilleure interface pour explorer le fonctionnement et les relations des modèles, vous laissant ainsi la confiance nécessaire pour expérimenter et développer vos propres représentations relationnelles des données.

Si vous cherchez un point de départ facile, essayez d'ajouter un modèle de Librarian qui hérite de User comme le fait le Profile . Pour plus de défi, essayez de mettre en place un historique de paiement pour chaque Copy et/ou Patron (il existe plusieurs façons d'accomplir celui-ci).

Django Highlights est une série présentant des concepts importants du développement Web dans Django. Chaque article est écrit comme un guide autonome sur une facette du développement de Django destiné à aider les développeurs et les concepteurs frontaux à mieux comprendre «l'autre moitié» de la base de code. Ces articles sont principalement construits pour vous aider à comprendre la théorie et les conventions, mais contiennent des exemples de code écrits en Django 3.0.

Lectures complémentaires

Vous pourriez être intéressé par les articles et la documentation suivants.

  • Faits saillants de Django : modèles d'utilisateurs et authentification (partie 1)
  • Faits saillants de Django : la création de modèles permet d'économiser des lignes (partie 2)
  • Faits saillants de Django : se disputer les ressources statiques et les fichiers multimédias (partie 4)
  • Documentation d'administration Django
  • Modèles Django
  • Référence des champs du modèle Django
  • Migration Django