[PYTHON] Implémentez l'API à une vitesse explosive en utilisant Django REST Framework

: christums_tree: Cet article est le jour 14 du calendrier de l'Avent "Python Part 2" 2015.

Je me demandais quoi écrire, mais j'aimerais écrire sur le Framework REST, qui a été la plus bénéfique des technologies liées à Python que j'ai apprises cette année.

: golf: Introduction

Créer une API est assez gênant

Tout d'abord, abordons la difficulté de créer une API. Je pense que beaucoup de gens l'ont fait, mais contrairement aux pages Web, l'API Web peut être développée en se concentrant uniquement sur la logique, donc elle est facile à développer en matière de développement. Il n'est pas nécessaire de s'inquiéter de l'environnement de conception ou d'utilisation, ou d'utiliser pleinement plusieurs langues telles que HTML et Javascript. Recevez simplement la réponse correctement et renvoyez la réponse de manière appropriée.

Cela dit, il y a beaucoup de choses gênantes lorsque vous y parvenez. Par exemple, vérifier si la valeur saisie est correcte, filtrer le résultat selon plusieurs conditions, diviser le nombre d'éléments à renvoyer, etc. De plus, plus vous avez de modèles, plus vous devez simplement créer d'API. Il faut beaucoup d'efforts pour les fabriquer un par un.

Utilisez le Framework Django REST

C'est là que le framework Django REST, sujet de ce numéro, entre en jeu. En le créant avec les propres fonctions de Django, il est possible de créer des API à une vitesse explosive à partir de rien. Et la bonne nouvelle est qu'il est livré avec une console WEB pour essayer l'API. Dans l'API, vous devez compléter et entrer vous-même les paramètres corrects, il est donc difficile de vérifier l'opération, mais avec le Framework Django REST, vous n'avez pas à vous en soucier. Je n'ai pas d'autre choix que de l'utiliser.

: débutant: Qu'est-ce que l'API RESTful?

Pour tirer pleinement parti du Framework Django REST, vous devez connaître les API RESTful. Comme ce n'est pas le sujet principal, je ne l'écrirai pas en détail, mais je le passerai brièvement en revue.

WebAPI ≠ RESTful C'est une idée fausse que toutes les API échangées via HTTP / HTTPS sont RESTful. RESTful est comme une règle de conception pour créer une API, et en créant une API en fonction de cela, vous pouvez créer une API simple et claire.

API one-to-one avec le modèle

La fonctionnalité de l'API RESTful est que vous créez une API afin qu'elle soit connectée au modèle sur une base individuelle. Il est RESTful d'interroger la liste des ressources dans un modèle et d'ajouter / mettre à jour / supprimer chacune d'entre elles, au lieu de récupérer le résultat entier que vous souhaitez faire référence à ce modèle même avec un seul appel d'API. C'est une image. Il ne convient pas aux applications mobiles qui souhaitent obtenir des informations avec un petit nombre de requêtes ou exploiter plusieurs modèles en collant des transactions, mais il est très géré car les règles sont claires et chaque API est très indépendante. C'est facile.

GET, POST, PUT, DELETE Ce sont quatre méthodes HTTP qui peuvent être considérées comme représentatives de RESTful. Examen d'un exemple d'API pour une entrée de blog

Concevez le point de terminaison de l'API de la manière suivante.

La différence entre POST et PUT semble être différente selon qu'il s'agit d'une opération pour une ressource spécifique ou non. Lors de la publication d'un nouvel article, entry_id n'a pas encore été attribué, donc POST est affecté à / entries, et entry_id est affecté pour les mises à jour après la création de l'article, c'est donc comme fonctionner avec PUT.

: cat: Faisons-le pour le moment

Utilisons maintenant le Framework Django REST.

Quoi faire

J'ai donné un exemple de blog, alors créons une API qui exploite l'entrée du blog à titre d'exemple. Je voudrais présenter un modèle de relation, donc je vais considérer un modèle qui représente le propriétaire de l'entrée appelée User.

Environnement de confirmation

Cette fois, je vérifie le fonctionnement avec Python3. Je n'utilise aucune grammaire spécifique, et le framework Django REST prend également en charge Python 2.7, donc je pense qu'il fonctionnera avec Python 2, mais le support prendra fin en 2020 et 2015 est sur le point de se terminer. , Je pense qu'il vaut mieux passer activement à Python3.

Configuration du projet

__Installation des bibliothèques requises __

pip install django
pip install djangorestframework
pip install django-filter 

__ Créer une application de projet __

# django_rest_framework_Créons un projet avec le nom test
django-admin startproject django_rest_framework_test
cd django_rest_framework_test/

python manage.py startapp blog

#Vérification de la structure du répertoire
.
└── django_rest_framework_test
    ├── blog
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── apps.py
    │   ├── migrations
    │   ├── models.py
    │   ├── tests.py
    │   └── views.py
    ├── django_rest_framework_test
    │   ├── __init__.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    └── manage.py

__ Définition du modèle __

blog/models.py



from django.db import models


class User(models.Model):
    name = models.CharField(max_length=32)
    mail = models.EmailField()


class Entry(models.Model):
    STATUS_DRAFT = "draft"
    STATUS_PUBLIC = "public"
    STATUS_SET = (
            (STATUS_DRAFT, "Brouillon"),
            (STATUS_PUBLIC, "Ouvert"),
    )
    title = models.CharField(max_length=128)
    body = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    status = models.CharField(choices=STATUS_SET, default=STATUS_DRAFT, max_length=8)
    author = models.ForeignKey(User, related_name='entries', on_delete=models.CASCADE)

Pour le moment, définissez le modèle de cette manière. Si vous créez réellement un blog, il y a des restrictions, mais cette fois, je vais simplement créer une API et l'essayer, donc je vais le laisser à ce niveau.

Nous définissons User, qui n'a qu'un nom et une adresse e-mail, et Entry, qui représente un article de blog. Depuis Entry, la clé étrangère fait référence à ʻUser` comme étant l'information de la personne qui a écrit le blog. Dans le cas de Django, c'est pratique car vous pouvez également inverser la référence en ajoutant simplement related_name.

__ Construction de la base de données __ Une fois le modèle créé, créez la base de données. Cette fois, j'utiliserai SQlite3 qui ne nécessite pas de préparation.

django_rest_framework_test/settings.py


…
#Ajouter un blog
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog',
]
…

Enregistrez l'application de blog que vous venez de créer dans les paramètres Django et appuyez sur la commande suivante.

#Créer un fichier de migration
python manage.py makemigrations
#Reflect dans DB basé sur le fichier de migration
python manage.py migrate

Cela créera un fichier de base de données appelé db.sqlite3 dans la même hiérarchie que manage.py, et créera automatiquement une table selon le modèle de blog. Cette fonctionnalité est vraiment utile car vous pouvez préparer une base de données sans écrire de SQL. Même lors du remplacement par MySQL ou Postgres pour la production, il est possible de répondre en ne modifiant que les paramètres.

__ Contrôle de fonctionnement __ Vérifions l'opération facilement avant d'incorporer l'API REST.

#Créer un utilisateur pour l'administrateur
python manage.py createsuperuser
   Username (leave blank to use 'kimihiro_n'): dev
   Email address:
   Password:
   Password (again):
   Superuser created successfully.

#Démarrer le serveur de développement
python manage.py runserver

http://localhost:8000/admin Vous pouvez accéder à l'écran de gestion de Django avec.


image


Cet écran de gestion vous permet d'exploiter le modèle Django sur le Web. Cependant, vous pouvez voir qu'il n'y a pas de ʻEntry et ʻUser créés plus tôt. (C'est déroutant, mais les utilisateurs AUTHENTIFICATION ET AUTORISATION servent à gérer les connexions Django.)

Pour ajouter votre propre modèle ici, vous devez en ajouter un peu à la source.

blog/admin.py


from django.contrib import admin

from .models import User, Entry


@admin.register(User)
class UserAdmin(admin.ModelAdmin):
    pass

@admin.register(Entry)
class Entry(admin.ModelAdmin):
    pass

Ouvrez admin.py dans le blog et réécrivez-le comme ci-dessus. Vous pouvez personnaliser l'affichage d'Admin en détail, mais ce sera long, je vais donc l'omettre.


image


python manage.py runserver

Si vous exécutez à nouveau et ouvrez-le dans le navigateur, le modèle défini précédemment apparaîtra. (Si l'indicateur DEBUG est True, vous n'avez pas à réexécuter runserver pour chaque changement de source)


image


Vous pouvez créer et modifier l'entrée ici. Il est pratique de faire une entrée pour essayer l'API de liste plus tard.

Incorporer le framework Django REST

Eh bien, cela fait longtemps, mais jusqu'à présent, c'est le même que le flux Django normal. À partir de là, passons à la création de l'API REST.

__Charger le framework REST __

django_rest_framework_test/settings.py


INSTALLED_APPS = (
    ...
    'blog',
    'rest_framework',
)

Ajoutez rest_framework à INSTALLED_APPS dans le fichier de configuration. Vous pouvez maintenant appeler le Framework REST depuis Django.

Pour créer une API REST, vous devez définir au moins les trois suivants.

En gros, Serializer consiste à "décider comment sérialiser (désérialiser) le modèle", ViewSet "décider comment interpréter les requêtes API", et URL Pattern "décider comment interpréter". Il est destiné à enseigner les modèles d'URL Django. " Nous définirons chacun de ces éléments pour le modèle que nous voulons convertir en API. Cela peut sembler compliqué lorsque vous souhaitez implémenter l'API minimale, mais en la divisant de cette manière, vous pouvez obtenir une extensibilité élevée et une bonne visibilité du code.

Définition du sérialiseur

blog/serializer.py


# coding: utf-8

from rest_framework import serializers

from .models import User, Entry


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('name', 'mail')


class EntrySerializer(serializers.ModelSerializer):
    class Meta:
        model = Entry
        fields = ('title', 'body', 'created_at', 'status', 'author')

Il s'agit de la définition minimale du sérialiseur. Créez un sérialiseur correspondant au modèle en héritant de serializers.ModelSerializer. Ce que vous donnez à fields est un tuple de noms de champs que vous souhaitez générer en tant qu'API. Ceux répertoriés dans les fichiers sont sérialisés et sortis avec l'implémentation par défaut. Par exemple, EntrySerializer essaie de sortir le champ de relation ʻauthor`, mais par défaut, il renvoie l'id (= pk) de l'auteur. Vous pouvez modifier ces comportements en personnalisant le sérialiseur (voir ci-dessous).

Définition de ViewSet

blog/views.py


# coding: utf-8

import django_filters
from rest_framework import viewsets, filters

from .models import User, Entry
from .serializer import UserSerializer, EntrySerializer


class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer


class EntryViewSet(viewsets.ModelViewSet):
    queryset = Entry.objects.all()
    serializer_class = EntrySerializer

Le ViewSet ressemble à ceci. Spécifiez l'ensemble de requêtes du modèle de Django dans query set et le sérialiseur défini précédemment dans serializer_class. L'ensemble de requêtes peut également être filtré à l'avance.

Définition du modèle d'URL

django_rest_framework_test/urls.py


# coding: utf-8

from django.conf.urls import url, include
from django.contrib import admin

from blog.urls import router as blog_router

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # blog.Inclure les URL
    url(r'^api/', include(blog_router.urls)),
]

blog/urls.py


# coding: utf-8

from rest_framework import routers
from .views import UserViewSet, EntryViewSet


router = routers.DefaultRouter()
router.register(r'users', UserViewSet)
router.register(r'entries', EntryViewSet)

Enfin, la définition du modèle d'URL. Nous enregistrerons chaque modèle à l'aide d'un routeur.

Avec les paramètres ci-dessus, / api / est la passerelle vers l'API REST. get /api/users/Dans la liste des utilisateurs, get /api/entries/Liste des entrées Vous pouvez accéder à. Bien entendu, les opérations qui spécifient POST ou ID sont également enregistrées en même temps.

Vérification du fonctionnement de l'API

Maintenant que l'API REST est prête à fonctionner, déplaçons-la.

python manage.py runserver

Démarrez le serveur avec et essayez d'accéder à http: // localhost: 8000 / api /.


image


Ensuite, une console API comme celle-ci apparaîtra. En utilisant la console, vous pouvez afficher le JSON de manière facile à lire, et vous n'avez pas à préparer votre propre formulaire pour tester l'API, alors utilisons-le davantage. Au fait, si vous frappez quelque chose comme curl http: // localhost: 8000 / api / avec une commande, il sera retourné au format JSON au lieu de HTML sur la console, alors ne vous inquiétez pas. Vous pouvez également forcer JSON dans le navigateur en ajoutant ? Format = json.


image


Si vous allez sur http: // localhost: 8000 / api / entries /, cela ressemblera à ceci. Il s'agit d'un point final où vous pouvez obtenir une liste d'entrées. Vous pouvez voir que les données saisies précédemment sur l'écran de gestion sont affichées.

Un formulaire est également joint en bas de l'écran, à partir duquel vous pouvez tester le POST d'une nouvelle entrée. Si vous utilisez des choix, vous pouvez sélectionner à partir de l'étiquette hautement lisible, et si vous utilisez le modèle de relation, vous pouvez sélectionner des éléments existants, ce n'est donc pas un problème.

Tous les choix de l'auteur sont désormais "Objet utilisateur", qui peut être identifié en écrasant de manière appropriée le `` str``` de UserModel.

blog/models.py


class User(models.Model):
    …
    def __repr__(self):
        #Afficher la clé primaire et le nom pour faciliter la lecture
        # ex) 1: Alice
        return "{}: {}".format(self.pk, self.name)

    __str__ = __repr__  # __str__Appliquer la même fonction à

image


Si vous allez sur http: // localhost: 8000 / api / entries / 1, cela ressemblera à ceci. Cette fois, il s'agit d'un GET pour des éléments individuels, donc une entrée avec pk = 1 sera affichée. En outre, le bouton SUPPRIMER est défini dans le coin supérieur droit, la valeur actuelle est définie dans le formulaire et le bouton est installé avec PUT au lieu de POST. Vous pouvez l'utiliser pour supprimer ou mettre à jour l'entrée.

: rocket: personnaliser l'API

L'implémentation et l'utilisation de l'API de base sont terminées, mais pour pouvoir la faire fonctionner et l'utiliser, il est nécessaire d'effectuer un filtrage et une personnalisation du format de sortie. Cette fois, je vais vous présenter quelques méthodes de personnalisation parmi elles. Pour les personnalisations que je n'ai pas publiées, consultez la documentation officielle. Comme il est hautement extensible, c'est un système qui peut répondre à diverses demandes.

Développez le modèle de relation

En regardant le JSON d'entrée dans l'implémentation précédente

{
   "title": "Hello, Django REST API!!",
   "body": "<script>alert(\"hello\");</script>",
   "created_at": "2015-12-09T05:59:46.200277Z",
   "status": "draft",
   "author": 1
}

ʻAuthor` était affiché par l'ID du modèle utilisateur. Si vous souhaitez utiliser le contenu d'une entrée et le nom de son utilisateur, obtenez d'abord l'entrée, puis regardez l'ID de l'auteur et demandez à nouveau `` GET / api / users / 1 '' pour obtenir le nom de l'utilisateur. Je ne peux pas. C'est inefficace en fonctionnement réel, n'est-ce pas? Il est pratique d'étendre le contenu de User en JSON lors de l'obtention de Entry.

{
   "title": "Hello, Django REST API!!",
   "body": "<script>alert(\"hello\");</script>",
   "created_at": "2015-12-09T05:59:46.200277Z",
   "status": "draft",
   "author": {
       "name": "Alice",
       "mail": "[email protected]"
   }
}

Pour être précis, Ceci est facile à utiliser, même du côté qui gère les API.

Pour modifier la réponse de l'API de cette manière, il vous suffit de remplacer la partie auteur avec Serializer.

blog/serializer.py


…
class EntrySerializer(serializers.ModelSerializer):
    #Écraser le sérialiseur de l'auteur
    author = UserSerializer()

    class Meta:
        model = Entry
        fields = ('title', 'body', 'created_at', 'status', 'author')

Tout ce que vous avez à faire est de définir le champ auteur dans EntrySerializer et de définir le UserSerializer que vous y avez défini. J'aime ajouter read_only, mais il peut être préférable de ne pas écraser depuis Entry car les spécifications de l'API deviennent compliquées lors du POST et de la PUT Entry. De plus, il serait plus agréable d'ajouter «id» aux champs de UserSerializer.

http://www.django-rest-framework.org/api-guide/serializers/ Vous pouvez également modifier le format d'affichage et fournir une validation en écrasant le sérialiseur.

Pégination

Ensuite, à propos du pagement. Avec l'implémentation jusqu'à présent, il s'agit d'une spécification pour récupérer tous les modèles et les afficher. Il n'y a pas de problème tant que la quantité de données est petite, mais à mesure que le nombre de cas augmente, la quantité de charge et de transfert deviendra terrible. Par conséquent, comme cela est fait sur la page Web, limitez le nombre d'éléments qui peuvent être acquis avec une seule demande afin que les données de la page suivante puissent être acquises si nécessaire.

django_rest_framework_test/settings.py


REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 2
}

Jusqu'à présent, nous n'avons ajouté aucun paramètre pour Django REST, mais vous devez ajouter des paramètres spéciaux pour utiliser la pagétion.

Définissons un dictionnaire appelé REST_FRAMEWORK et ajoutons DEFAULT_PAGINATION_CLASS et PAGE_SIZE.

Cela seul permet à l'API de prendre en charge les paramètres «offset» (position de départ) et «limit» (limite supérieure d'acquisitions à la fois, par défaut = PAGE_SIZE).

{
    "count": 4,
    "next": "http://localhost:8000/api/entries/?limit=2&offset=2",
    "previous": null,
    "results": [
        {
            "id": 1,
            "title": "Hello, Django REST Framework!!",
            "body": "Hello!",
            "created_at": "2015-12-12T11:55:22.310203Z",
            "status": "draft",
            "author": 1
        },
        {
            "id": 2,
            "title": "The Zen of Python",
            "body": "The Zen of Python, by Tim Peters\r\n\r\nBeautiful is better than ugly.\r\nExplicit is better than implicit.\r\nSimple is better than complex.\r\nComplex is better than complicated.\r\nFlat is better than nested.\r\nSparse is better than dense.\r\nReadability counts.\r\nSpecial cases aren't special enough to break the rules.\r\nAlthough practicality beats purity.\r\nErrors should never pass silently.\r\nUnless explicitly silenced.\r\nIn the face of ambiguity, refuse the temptation to guess.\r\nThere should be one-- and preferably only one --obvious way to do it.\r\nAlthough that way may not be obvious at first unless you're Dutch.\r\nNow is better than never.\r\nAlthough never is often better than *right* now.\r\nIf the implementation is hard to explain, it's a bad idea.\r\nIf the implementation is easy to explain, it may be a good idea.\r\nNamespaces are one honking great idea -- let's do more of those!",
            "created_at": "2015-12-12T11:56:32.854278Z",
            "status": "draft",
            "author": 2
        }
    ]
}

Il est également pratique de connaître les URL des requêtes précédentes et suivantes avec «suivant», «précédent».

En passant, vous pouvez également utiliser la requête par page avec une limite fixe et la méthode d'apprentissage curseur avant et après le démarrage de l'ID de l'objet en modifiant simplement le paramètre. http://www.django-rest-framework.org/api-guide/pagination/

filtre

Enfin, je présenterai le filtre. C'est pour quand vous voulez restreindre les entrées avec ʻauthor`.

django_rest_framework_test/settings.py


INSTALLED_APP = [
...
    'blog',
    'rest_framework',
    'django_filters',  #ajouter à
]

#Postscript
REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
}

Ajoutez django_filters et DEFAULT_FILTER_BACKENDS au fichier de configuration. Si vous souhaitez également utiliser pagement, veuillez intégrer le dictionnaire.

class EntryViewSet(viewsets.ModelViewSet):
    queryset = Entry.objects.all()
    serializer_class = EntrySerializer
    filter_fields = ('author', 'status')

Ensuite, ajoutez filter_fields au ViewSet que vous voulez filtrer. Vous pouvez maintenant filtrer «auteur» et «statut».

Si vous ajoutez ? Author = 1 à la fin de l'API, vous ne pouvez obtenir que les articles écrits par User id = 1. De plus, si vous définissez ? Status = public, vous ne pouvez obtenir que les articles à statut public.

http://www.django-rest-framework.org/api-guide/filtering/ Vous pouvez également rechercher ou créer votre propre méthode de spécification.

: white_flower: Résumé

Avec l'API Django REST, vous pouvez créer une API solide avec peu d'effort. Je pense qu'il n'y a pas de cadre qui s'occupe de tout, de la base de données à la sortie de l'API. Puisqu'il s'agit de Django, il peut être utilisé à des fins telles que la création de pages en plus de l'API. Veuillez essayer d'utiliser le Framework d'API REST de Django. (Et accumulons des connaissances en japonais!)

En fait, je voulais aborder une personnalisation plus détaillée, mais j'ai été submergé par le temps et me suis précipité vers la seconde moitié. Puisqu'il y a un volume qui semble être capable d'écrire plusieurs articles juste en sérialisant et en filtrant, j'aimerais faire un article indépendant et le compléter un jour.

: octocat: source

https://github.com/pistatium/django_rest_framework_test Je vais mettre la source de l'échantillon que j'ai fait cette fois. Même si vous ne comprenez pas le répertoire.

: livre: URL de référence


Demain, c'est @ satoshi03.

Recommended Posts

Implémentez l'API à une vitesse explosive en utilisant Django REST Framework
Créer une API RESTful avec Django Rest Framework
Implémenter la fonctionnalité de connexion JWT dans le framework Django REST
Bases du framework Django REST
Astuces Django Rest Framework
Créer une API autour de l'authentification des utilisateurs avec Django REST Framework
[Django Rest Framework] Personnalisez la fonction de filtre à l'aide de Django-Filter
Essayez de résoudre le Sudoku à une vitesse explosive en utilisant Numpy
Implémenter des URL hiérarchiques avec des routeurs imbriqués drf dans le framework Django REST
Bloc d'achoppement du framework Django REST
Framework Django REST avec Vue.js
Connectez-vous avec Django Rest Framework
Essayez d'utiliser le framework Web Python Django (2) - Regardez setting.py
Comment réinitialiser le mot de passe via l'API à l'aide du framework Rest Django
Essayez l'analyse de corrélation multivariée à l'aide du lasso graphique à une vitesse explosive
Implémentation de la fonction d'authentification JWT dans Django REST Framework à l'aide de djoser
Implémentation de CRUD à l'aide de l'API REST avec Python + Django Rest framework + igGrid
Installer le framework Python django à l'aide de pip
[Django] Utiliser MessagePack avec le framework Django REST
Suppression logique dans Django, DRF (Django REST Framework)
Comprendre la commodité de Django Rest Framework
Un outil administratif qui peut être créé immédiatement avec le framework ng-admin + Django REST
CRUD GET avec Nuxt & Django REST Framework ②
CRUD POST avec Nuxt & Django REST Framework
[TPU] [Transformers] Faites du BERT à une vitesse explosive
CRUD GET avec Nuxt & Django REST Framework ①
Django REST Framework + Considération de conception d'architecture propre
Convertissez instantanément le modèle en dictionnaire avec Django et initialisez Form à une vitesse explosive
CRUD PUT, DELETE avec Nuxt & Django REST Framework
Modèle Python qui effectue une analyse des journaux à une vitesse explosive
J'ai essayé d'utiliser PyCaret à la vitesse la plus rapide
Framework Django REST Un peu utile à savoir.
Implémentation de la fonction d'authentification dans Django REST Framework à l'aide de djoser