Modèles, administrateur et exploitation de la base de données relationnelle (partie 3) – Smashing Magazine


A propos de l’auteur

Philip Kiely est développeur, écrivain et entrepreneur. Il est étudiant au Grinnell College (promotion 2020).
Plus à propos
Philippe
Kiely

Le panneau d’administration est l’une des fonctionnalités les plus puissantes et flexibles fournies par le framework Web Django, combinant des fonctionnalités instantanées standard avec une personnalisation infinie. À l’aide d’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 des 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 la 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 créer et gérer des 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 est basé 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 de 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.

J’offre 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 devrons toujours utiliser l’ORM basé sur des modèles de Django (mapping objet-relationnel) pour spécifier la représentation des données dans notre système.

Configuration

Nous allons travailler avec un exemple de projet dans cet article. Le projet modélise certaines données qu’une bibliothèque pourrait stocker sur ses livres et ses mécènes. 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 l’apparence des données:

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

Veuillez suivre les étapes suivantes pour exécuter l’exemple de code sur votre ordinateur local.

1. Installation de 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 cadre Web avec lequel nous travaillons dans cet article. (django-grappelli est un thème du panneau d’administration que nous aborderons brièvement.)

2. Obtenir le projet

Une fois les packages précédents installés, téléchargez l’exemple de code depuis GitHub. Courir:

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 à travers le processus de création d’un superutilisateur. Votre compte de superutilisateur vous permettra d’accéder au panneau d’administration dans un instant, alors n’oubliez pas le mot de passe que vous avez défini. Utilisation:

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 sur la façon de créer un appareil à la fin de l’article). Utilisez le luminaire pour remplir votre base de données avant de l’explorer dans le panneau d’administration. Courir:

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 pour http://127.0.0.1:8000 pour visualiser le projet. Notez que vous êtes automatiquement redirigé vers le panneau d’administration à l’adresse / admin /. J’ai accompli cela avec la configuration suivante dans bibliothèque / 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 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: Groups et Users sont définis par Django et stockent les informations et les autorisations pour les comptes sur le système. Vous pouvez en savoir plus sur le User modèle dans un article précédent de cette série. Books, Copys, et Patrons sont des tableaux de la base de données que nous avons créés lors de l’exécution des migrations et remplis en chargeant le luminaire. Notez que Django pluralise naïvement les noms de modèle en ajoutant un «s», même dans des cas comme «copys» 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 maintient un Copy de chaque Book, ou éventuellement plusieurs. Chaque Copy peut être vérifié par un Patron, ou pourrait actuellement être enregistré. Patron est une extension de la User qui enregistre leur adresse et leur 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)

Créer 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 de la User menu déroulant. 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 un COPY via le même mécanisme.

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

Actions d’administration

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

Créer un Copy d’un Book, allez à 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 auxquels ajouter une copie de l’inventaire.

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

La création de cette méthode repose sur une méthode de modèle que nous aborderons plus tard. Nous pouvons l’appeler comme une action d’administration en créant un ModelAdmin classe pour le Profile modèle 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)

le list_display La propriété indique les champs utilisés pour représenter le modèle dans la page de présentation du modèle. le actions La propriété répertorie les actions d’administration. Notre action d’administration est définie comme une fonction dans BookAdmin et prend trois arguments: l’objet admin 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 ont été effectuées. Chaque action d’administration nécessite une brève description afin de pouvoir ê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 enregistrer 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 administrateur

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 vérifier La documentation pour toutes ses capacité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 bibliothèque / 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 bibliothèque / 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 votre propre. Je m’en tiendrai à l’aspect 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 parcourir les données, examinons les modèles qui définissent notre structure de 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, y compris 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é sur une seule ligne. La partie «relationnelle» du nom vient de ce qui est sans doute la caractéristique la plus importante de la technologie: créer des relations entre les tables. Un objet (ligne) peut avoir un mappage un-à-un, un-à-plusieurs (clé étrangère) ou plusieurs-à-plusieurs avec des lignes dans d’autres tables. Nous en discuterons plus en détail 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 courez python manage.py migrate. Nous allons continuer avec SQLite3 pour cet article, mais il ne convient pas pour une utilisation en production, principalement parce que les remplacements sont possibles avec des utilisateurs simultanés. En production, ou lors de l’écriture d’un système que vous comptez un jour déployer, utilisez PostgreSQL ou MySQL.

Django utilise des modèles pour s’interfacer avec la base de données. En utilisant une partie de l’ORM de Django, records / models.py Le fichier comprend 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’adopter une architecture «Fat Model», dans des limites raisonnables. Cela signifie que la plus grande partie possible de la validation, de l’analyse, du traitement, de la logique métier, de la gestion des exceptions, de la résolution des cas limites et des tâches similaires doit être traitée 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 largement 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 il est censé être un article d’introduction, pas la documentation complète du framework Django, mais je vais souligner les choix les plus importants que j’ai faits dans la construction de ces modèles simples.

le Book classe est le plus simple des modèles. Ici c’est 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)

Tout CharField les champs nécessitent une spécification max_length attribut. La longueur conventionnelle est de 150 caractères, que j’ai doublé pour title en cas de titres très longs. Bien sûr, il existe toujours une limite arbitraire, qui pourrait être dépassée. Pour une longueur de texte illimitée, utilisez un TextField. le published champ est un DateField. Le moment où 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 sont composés de 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 le models.Model classe et représentent plutôt les livres comme «titre par auteur» à tous les endroits où le modèle serait représenté sous forme de chaîne. Rappelons que nous utilisions auparavant list_display dans BookDe l’administrateur pour déterminer les champs à afficher dans la liste du panneau d’administration. Si ce list_display n’est pas présent, la liste des administrateurs affiche à la place la représentation sous forme de chaîne du modèle, comme c’est le cas pour les deux Patron et Copy.

Enfin, nous avons une méthode sur Book que nous avons appelé dans son action d’administration que nous avons écrit plus tôt. Cette fonction crée un Copy qui est liée à une instance donnée d’un Book dans la base de données.

Passer à Patron, ce modèle introduit le concept d’une relation un-à-un, dans ce cas avec la fonction intégrée User modèle. Découvrez-le depuis 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 user champ n’est pas exactement une fonction bijective. Il PEUT y avoir un User instance sans associé Patron exemple. Cependant, un User NE PEUT PAS être associé à plus d’un Patron par exemple, et un Patron ne peux pas exister sans une seule relation avec un utilisateur. Ceci est appliqué au niveau de la base de données et est garanti par le on_delete=models.CASCADE spécification: si un User est supprimée, un associé Profile sera supprimé.

Les autres domaines et __str__(self) fonction que nous avons vu auparavant. Il convient de noter que vous pouvez atteindre une relation biunivoque pour obtenir des attributs, dans ce cas user.username, dans les fonctions d’un modèle.

Pour développer l’utilité des relations avec les bases de données, tournons notre attention vers Copy de 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 en avons déjà vu la plupart, alors concentrons-nous sur les nouveautés: models.ForeignKey. UNE Copy doit être d’un seul Book, mais la bibliothèque peut avoir plusieurs Copys de chaque Book. UNE Book peut exister dans la base de données sans que la bibliothèque ait un Copy dans son catalogue, mais un Copy ne peut exister sans un sous-jacent Book.

Cette relation complexe s’exprime par la ligne suivante:

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

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

La relation entre un Copy et un Patron est légèrement différent. UNE Copy peut être extrait jusqu’à un Patrons, mais chacun Patron peut vérifier autant Copys comme la bibliothèque le permet. Cependant, ce n’est pas une relation permanente, le Copy n’est parfois pas extrait. Patronle sable Copys 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, ayant blank=True permet aux formulaires d’accepter None comme valeur pour la relation et null=True permet la colonne pour le Patron relation dans CopyLa table de la base de données accepte null comme valeur. Le comportement de suppression, qui serait déclenché sur un Copy si un Patron exemple a été supprimé alors qu’ils avaient que Copy vérifié, est de rompre la relation en quittant le Copy intact en définissant le Patron champ à 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 ne pouvais pas tenir correctement 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 d’entre eux 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 pas, puis elles mangent votre visage. Voici comment garder votre tasse intacte, pour les débutants: découvrez les migrations et comment interagir avec elles, mais essayez d’éviter de modifier manuellement les fichiers de migration. Si vous savez déjà ce que vous faites, ignorez cette section et continuez ce qui fonctionne pour vous.

D’une manière ou d’une autre, consultez la documentation officielle pour un traitement complet du sujet.

Les migrations traduisent les modifications d’un modèle en modifications du schéma de la base de données. Vous n’avez pas à les écrire vous-même, Django les crée avec le python manage.py makemigrations commander. Vous devez exécuter cette commande lorsque vous créez un nouveau modèle ou modifiez les champs d’un modèle existant, mais cela n’est pas nécessaire lors de la création ou de la modification des méthodes de modèle. Il est important de noter que les migrations existent en tant que chaîne, chacune fait référence à la précédente afin de pouvoir 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 seul historique de migration 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 œ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é de présenter le sujet.

Pour une fois, le 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, ce 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, il échouera.

Pour générer un appareil pour toute 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 sujet énorme, et l’utilisation du panneau d’administration en est un autre. En 3 000 mots, je n’ai réussi qu’à les présenter chacun. J’espère que l’utilisation du panneau d’administration vous a donné une meilleure interface pour explorer le fonctionnement et les relations entre les modèles, vous laissant 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 Librarian modèle qui hérite de User comme Profile Est-ce que. Pour plus d’un défi, essayez d’implémenter 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 à approfondir leur compréhension de «l’autre moitié» de la base de code. Ces articles sont principalement conçus pour vous aider à comprendre la théorie et les conventions, mais contiennent des exemples de code qui sont écrits dans Django 3.0.

Lectures complémentaires

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

Smashing Editorial
(dm, yk, il)
Close Menu