*L’auteur a sélectionné Girls Who Code pour recevoir un don dans le cadre de l’initiative Écrire pour des donations. *
GraphQL est une norme d’API créée et mise à disposition par Facebook comme alternative à API REST. Contrairement aux API REST, GraphQL utilise un système typé pour définir sa structure de données où toutes les informations envoyées et reçues doivent être conformes à un schéma prédéfini. Il expose également un point d’accès unique pour toutes les communications au lieu de multiples URL pour différentes ressources et résout le problème épineux en renvoyant uniquement les données demandées par le client, ce qui génère des réponses plus petites et plus concises.
Dans ce tutoriel, vous allez créer un backend pour un raccourcisseur d’URL-un service qui prend n’importe quelle URL et génère une version plus courte et plus lisible- tout en plongeant dans les concepts de GraphQL, comme les interrogations et les mutations et les outils, comme l’interface GraphiQL. Vous avez peut-être déjà utilisé de tels services auparavant comme bit.ly
.
Comme GraphQL est une technologie non basée sur le langage, elle est appliquée en plus de langages et cadres divers. Ici, vous utiliserez le langage de programmation général Python, le cadre web Django et la bibliothèque Graphene-Django comme implémentation Python GraphQL avec des intégrations spécifiques pour Django.
Pour poursuivre ce tutoriel, vous aurez besoin de la version 3.5 ou supérieure de Python installée sur votre machine de développement. Pour installer Python, suivez notre tutoriel sur Comment installer et mettre en place un environnement de programmation local pour Python 3 pour votre système d’exploitation. Veillez également à créer et à lancer un environnement virtuel ; pour suivre le fil de ce tutoriel, vous pouvez nommer votre répertoire de projets shorty
.
Une connaissance de base de Django est souhaitée, mais pas obligatoire. Si vous êtes curieux, vous pouvez suivre cette série Django Development créée par la communauté DigitalOcean.
Dans cette étape, vous installerez tous les outils nécessaires à l’application et à la mise en place de votre projet Django.
Une fois que vous avez créé le répertoire de votre projet et lancé votre environnement virtuel comme prévu dans les conditions préalables, installez les paquets nécessaires en utilisant pip
le gestionnaire de paquets Python. Ce tutoriel permet d’installer la version 2.1.7 de Django et la version 2.2.0 ou supérieure de Graphene-Django :
- pip install "django==2.1.7" "graphene-django>==2.2.0"
Vous avez maintenant tous les outils nécessaires dans votre ceinture à outils. Ensuite, vous allez créer un projet Django en utilisant la commande django-admin
. Un projet est la plaque tournante par défaut de Django - un ensemble de dossiers et de fichiers avec tout le nécessaire pour démarrer le développement d’une application web. Dans ce cas, vous appellerez votre projet shorty
et le créerez au sein de votre dossier actuel en spécifiant le .
à la fin.
- django-admin startproject shorty .
Après avoir créé votre projet, vous dirigerez les migrations de Django. Ces fichiers contiennent du code Python généré par Django et sont chargés de modifier la structure de l’application selon les modèles de Django. Les changements peuvent comprendre la création d’une table, par exemple. Par défaut, Django est livré avec son propre ensemble de migrations responsables de sous-systèmes comme Django Authentification. Il est donc nécessaire de les exécuter avec la commande suivante :
- python manage.py migrate
Cette commande utilise l’interpréteur Python pour invoquer un script Django appelé manage.py
, chargé de gérer différents aspects de votre projet, comme la création d’applications ou l’exécution de migrations.
Cela donnera un résultat similaire à ce qui suit :
OutputOperations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying sessions.0001_initial... OK
Une fois que la base de données de Django est prête à fonctionner, démarrez son serveur de développement local :
- python manage.py runserver
Il en résultera :
OutputPerforming system checks...
System check identified no issues (0 silenced).
March 18, 2020 - 15:46:15
Django version 2.1.7, using settings 'shorty.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Cette commande supprime l’invite dans votre terminal et démarre le serveur.
Visitez http://127.0.0.1:8000
dans votre navigateur local. Vous verrez cette page :
Pour arrêter le serveur et revenir à votre terminal, appuyez sur CTRL+C
. Chaque fois que vous devez accéder au navigateur, assurez-vous que la commande précédente est exécutée.
Ensuite, vous terminerez cette étape en activant la bibliothèque Django-Graphene dans le projet. Django a le concept d’app
, une application web avec une responsabilité spécifique. Un projet est composé d’une ou plusieurs applications. Pour l’instant, ouvrez le fichiershorty/settings.py
dans l’éditeur de texte de votre choix. Ce tutoriel utilisera vim
:
- vim shorty/settings.py
Le fichier settings.py
gère tous les paramètres de votre projet. A l’intérieur, cherchez le INSTALLED_APPS
et ajoutez l'entrée « graphene_django »
:
...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'graphene_django',
]
...
Cet ajout indique à Django que vous utiliserez une application appelée graphene_django
que vous avez installée à l’étape 1.
Au bas du fichier, ajoutez la variable suivante :
...
GRAPHENE = {
'SCHEMA': 'shorty.schema.schema',
}
Cette dernière variable indique votre schéma principal, que vous créerez plus tard. Dans GraphQL, un schéma contient tous les types d’objets, tels que les ressources, les requêtes et les mutations. Considérez qu’il s’agit d’une documentation représentant toutes les données et fonctionnalités disponibles dans votre système.
Après les modifications, enregistrez et fermez le fichier.
Vous avez maintenant configuré le projet Django. Dans l’étape suivante, vous allez créer une application Django et ses modèles.
Une plateforme Django est généralement composée d’un projet et de nombreuses applications ou apps. Une app ou application décrit un ensemble de fonctionnalités à l’intérieur d’un projet, et, si elle est bien conçue, elle peut être réutilisée à travers les projets Django.
Dans cette étape, vous allez créer une application appelée shortener
responsable de la fonction de raccourcissement des URL. Pour créer son squelette de base, tapez la commande suivante dans votre terminal :
- python manage.py startapp shortener
Ici, vous avez utilisé les paramètres startapp app_name
, en donnant des instructions à manage.py
de créer une application appelée shortener
.
Pour terminer la création de l’application, ouvrez le fichier shorty/settings.py
- vim shorty/settings.py
Ajouter le nom de l’application à la même entrée INSTALLED_APPS
que vous avez modifiée auparavant :
...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'graphene_django'
'shortener',
]
...
Enregistrez et fermez le fichier.
Avec votre raccourcisseur
ajouté à shorty/settings.py
, vous pouvez passer à la création des modèles pour votre projet. Les modèles sont l’un des éléments clés de Django. Ils sont utilisés pour représenter une base de données sous forme « Python », ce qui permet de gérer, d’interroger et de stocker des données à l’aide de code Python.
Avant d’ouvrir le fichier models.py
pour les modifications, ce tutoriel donne un aperçu des modifications que vous allez apporter.
Votre fichier de modèle, models.py
, contiendra le contenu suivant une fois que vous aurez remplacé le code existant :
from hashlib import md5
from django.db import models
Ici, vous importerez les paquets nécessaires à votre code. Vous ajouterez la ligne de hashlib import md5
en haut pour importer la bibliothèque standard Python qui sera utilisée pour créer un hachage de l’URL. La ligne de modèlesfrom django.db
est une aide de Django pour la création de modèles.
Avertissement : Ce tutoriel fait référence au hachage comme le résultat d’une fonction qui prend une entrée et renvoie toujours la même sortie. Ce tutoriel utilisera la fonction de hachage MD5 à des fins de démonstration.
Notez que le MD5 présente des problèmes de collision et doit être évité en production.
Ensuite, vous ajouterez un modèle nommé URL
avec les champs suivants :
full_url
: l’URL à raccourcir.url_hash
: un court hachage représentant l’URL complète.clics
: nombre de fois que l’URL courte a été consultée.created_at
: la date et l’heure auxquelles l’URL a été créée....
class URL(models.Model):
full_url = models.URLField(unique=True)
url_hash = models.URLField(unique=True)
clicks = models.IntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
Vous allez générer l’url_hash
en appliquant l’algorithme de hachage MD5 au champ de saisie full_url
et en utilisant uniquement les 10 premiers caractères renvoyés lors de la méthode save()
du modèle, exécutée à chaque fois que Django enregistre une entrée dans la base de données. En outre, les raccourcisseurs d’URL suivent généralement le nombre de fois qu’un lien a été cliqué. Vous y parviendrez en appelant la méthode clicked()
lorsque l’URL est visitée par un utilisateur.
Les opérations mentionnées seront ajoutées à l’intérieur de votre Modèle d’URL
avec ce code :
...
def clicked(self):
self.clicks += 1
self.save()
def save(self, *args, **kwargs):
if not self.id:
self.url_hash = md5(self.full_url.encode()).hexdigest()[:10]
return super().save(*args, **kwargs)
Maintenant que vous avez examiné le code, ouvrez le fichier shortener/models.py
:
- vim shortener/models.py
Remplacez le code par le contenu suivant :
from hashlib import md5
from django.db import models
class URL(models.Model):
full_url = models.URLField(unique=True)
url_hash = models.URLField(unique=True)
clicks = models.IntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
def clicked(self):
self.clicks += 1
self.save()
def save(self, *args, **kwargs):
if not self.id:
self.url_hash = md5(self.full_url.encode()).hexdigest()[:10]
return super().save(*args, **kwargs)
Veillez à enregistrer et à fermer le fichier.
Pour appliquer ces changements dans la base de données, vous devrez créer les migrations en exécutant la commande suivante :
- python manage.py makemigrations
Cela donnera la sortie suivante :
OutputMigrations for 'shortener':
shortener/migrations/0001_initial.py
- Create model URL
Ensuite, exécutez les migrations :
- python manage.py migrate
Vous verrez la sortie suivante dans votre terminal :
OutputOperations to perform:
Apply all migrations: admin, auth, contenttypes, sessions, shortener
Running migrations:
Applying shortener.0001_initial... OK
Maintenant que vous avez mis en place les modèles, l’étape suivante consiste à créer le point final GraphQL et une requête.
L’architecture REST expose différentes ressources dans différents points finaux, chacun contenant une structure de données bien définie. Par exemple, vous pouvez obtenir une liste d’utilisateurs à l’adresse /api/users
qui s’attendent toujours aux mêmes domaines. GraphQL, en revanche, a un point final unique pour toutes les interactions, et utilise des Queries ou requêtes d’accès aux données. La différence principale - et la plus importante - est que vous pouvez utiliser une requête pour retrouver tous vos utilisateurs en une seule fois.
Commencez par créer une requête pour récupérer toutes les URL. Vous aurez besoin de plusieurs choses :
urls
.Créez un nouveau fichier appelé shortener/schema.py
:
- vim shortener/schema.py
Commencez par ajouter les déclarations d’import
Python :
import graphene
from graphene_django import DjangoObjectType
from .models import URL
La première ligne importe la principale bibliothèque de graphene
qui contient les types de base de GraphQL, comme List
. Le DjangoObjectType
est une aide pour créer une définition de schéma à partir de n’importe quel modèle Django, et la troisième ligne importe votre modèle URL
créé préalablement.
Ensuite, créez un nouveau type de GraphQL pour le modèle d’URL
en ajoutant les lignes suivantes :
...
class URLType(DjangoObjectType):
class Meta:
model = URL
Enfin, ajoutez ces lignes pour créer un type de requête pour le modèle d’URL
:
...
class Query(graphene.ObjectType):
urls = graphene.List(URLType)
def resolve_urls(self, info, **kwargs):
return URL.objects.all()
Ce code crée une classe de Query
avec un champ nommé urls
qui est une liste du URLType
défini précédemment. Lors de la résolution de la requête à l’aide des resolve_urls
vous renvoyez toutes les URL stockées dans la base de données.
Le fichier complet shortener/schema.py
est présenté ici :
import graphene
from graphene_django import DjangoObjectType
from .models import URL
class URLType(DjangoObjectType):
class Meta:
model = URL
class Query(graphene.ObjectType):
urls = graphene.List(URLType)
def resolve_urls(self, info, **kwargs):
return URL.objects.all()
Enregistrez et fermez le fichier.
Toutes les requêtes doivent maintenant être ajoutées au schéma principal. Considérez-le comme le détenteur de toutes vos ressources.
Créez un nouveau dossier dans le shorty/schema.py
et ouvrez-le avec votre éditeur :
- vim shorty/schema
Importez les paquets Python suivants en ajoutant les lignes suivantes. Le premier, comme déjà mentionné, contient les types GraphQL de base. La deuxième ligne importe le fichier Schema précédemment créé.
import graphene
import shortener.schema
Ensuite, ajoutez la principale classe de Query
. Elle conservera, par héritage, toutes les requêtes et les futures opérations créées :
...
class Query(shortener.schema.Query, graphene.ObjectType):
pass
Enfin, créez la variable schema
:
...
schema = graphene.Schema(query=Query)
Le paramètre SCHEMA
que vous avez défini à l’étape 2 renvoie à la variable schéma
que vous venez de créer.
Le shorty/schema.py
est affiché ici en entier :
import graphene
import shortener.schema
class Query(shortener.schema.Query, graphene.ObjectType):
pass
schema = graphene.Schema(query=Query)
Enregistrez et fermez le fichier.
Ensuite, activez le point final GraphQL et l’interface GraphiQLqui est une interface graphique web utilisée pour interagir avec le système GraphQL.
Ouvrez le fichiershorty/urls.py
:
- vim shorty/urls.py
Pour les besoins de ce tutoriel, supprimez le contenu du fichier et enregistrez-le, afin de pouvoir recommencer à zéro.
Les premières lignes que vous ajouterez sont des déclarations d’importation en Python :
from django.urls import path
from django.views.decorators.csrf import csrf_exempt
from graphene_django.views import GraphQLView
La fonction path
est utilisée par Django pour créer une URL accessible pour l’interface GraphiQL. Après, vous importez le csrf_exempt
qui permet aux clients d’envoyer des données au serveur. Une explication complète se trouve dans la documentation du Graphene. À la dernière ligne, vous avez importé le code réel responsable de l’interface via GraphQLView
.
Ensuite, créez une variable nommée urlpatterns
.
...
urlpatterns = [
path('graphql/', csrf_exempt(GraphQLView.as_view(graphiql=True))),
]
Cela permettra d’assembler tout le code nécessaire pour rendre l’interface GraphiQL disponible dans le chemin graphql/
:
L’ensemble shortener/urls.py
est affiché ici :
from django.urls import path
from django.views.decorators.csrf import csrf_exempt
from graphene_django.views import GraphQLView
urlpatterns = [
path('graphql/', csrf_exempt(GraphQLView.as_view(graphiql=True))),
]
Enregistrez le dossier et fermez-le.
De retour dans le terminal, lancez la commande python manage.py runserver
(si elle n’est pas déjà en cours d’exécution) :
- python manage.py runserver
Ouvrez votre navigateur web à l’adresse suivante http://localhost:8000/graphql
l’adresse. Cet écran s’affichera :
GraphiQL est une interface dans laquelle vous pouvez exécuter des instructions GraphQL et voir les résultats. L’une des caractéristiques est la section « Docs
» en haut à droite. Comme tout dans GraphQL est typé, vous obtenez une documentation gratuite sur tous vos types, requêtes, mutations, etc.
Après avoir exploré la page, insérez votre première requête dans la zone de texte principale :
query {
urls {
id
fullUrl
urlHash
clicks
createdAt
}
}
Ce contenu montre comment une query
GraphQL est structurée : tout d’abord, vous utilisez le mot-clé pour indiquer au serveur que vous ne voulez récupérer que certaines données. Ensuite, vous utilisez le champ urls
défini dans le fichier shortener/schema.py
à l’intérieur de la classe Query
. A partir de là, vous demandez explicitement tous les champs définis dans le modèle d’URL
en CamelCase qui est la valeur par défaut pour GraphQL.
Maintenant, cliquez sur le bouton flèche de lecture en haut à gauche.
Vous recevrez la réponse suivante, indiquant que vous n’avez toujours pas d’URLs :
Output{
"data": {
"urls": []
}
}
Cela montre que GraphQL fonctionne. Dans votre terminal, appuyez sur CTRL+C
pour arrêter votre serveur.
Vous avez accompli beaucoup de choses dans cette étape, en créant le point final GraphQL, en effectuant une requête pour récupérer toutes les URL et en activant l’interface GraphiQL. Maintenant, vous allez créer des mutations pour modifier la base de données.
La majorité des applications permettent de modifier l’état de la base de données en ajoutant, mettant à jour ou supprimant des données. Dans GraphQL, ces opérations sont appelées « Mutations ». Elles ressemblent à des requêtes mais utilisent des arguments pour envoyer des données au serveur.
Pour créer votre première Mutation, ouvrez shortener/schema.py
:
- vim shortener/schema.py
A la fin du fichier, commencez par ajouter une nouvelle classe nommée CreateURL
:
...
class CreateURL(graphene.Mutation):
url = graphene.Field(URLType)
Cette classe hérite de l’aide graphène.Mutation
pour avoir les capacités d’une mutation GraphQL. Elle a également un nom de propriété url
qui définit le contenu renvoyé par le serveur une fois la mutation terminée. Dans ce cas, ce sera la structure de données URLType
.
Ensuite, ajoutez une sous-classe nommée Arguments
à la classe déjà définie :
...
class Arguments:
full_url = graphene.String()
Cela définit les données qui seront acceptées par le serveur. Ici, vous attendez un paramètre nommé full_url
avec un contenu String
:
Ajoutez maintenant les lignes suivantes pour créer la méthode mutate
:
...
def mutate(self, info, full_url):
url = URL(full_url=full_url)
url.save()
Cette méthode mutate
fait une grande partie du travail en recevant les données du client et en les enregistrant dans la base de données. À la fin, elle renvoie la classe elle-même contenant l’élément nouvellement créé.
Enfin, créez une Mutation
qui contiendra toutes les Mutations de votre app, en ajoutant ces lignes :
...
class Mutation(graphene.ObjectType):
create_url = CreateURL.Field()
Jusqu’à présent, vous n’aurez qu’une seule mutation nommée create_url
.
Le fichier complet shortener/schema.py
est présenté ici :
import graphene
from graphene_django import DjangoObjectType
from .models import URL
class URLType(DjangoObjectType):
class Meta:
model = URL
class Query(graphene.ObjectType):
urls = graphene.List(URLType)
def resolve_urls(self, info, **kwargs):
return URL.objects.all()
class CreateURL(graphene.Mutation):
url = graphene.Field(URLType)
class Arguments:
full_url = graphene.String()
def mutate(self, info, full_url):
url = URL(full_url=full_url)
url.save()
return CreateURL(url=url)
class Mutation(graphene.ObjectType):
create_url = CreateURL.Field()
Fermez et enregistrez le fichier.
Pour terminer l’ajout de la mutation, modifiez le fichiershorty/schema.py
:
- vim shorty/schema.py
Modifiez le fichier pour y inclure le code surligné suivant :
import graphene
import shortener.schema
class Query(shortener.schema.Query, graphene.ObjectType):
pass
class Mutation(shortener.schema.Mutation, graphene.ObjectType):
pass
schema = graphene.Schema(query=Query, mutation=Mutation)
Enregistrez et fermez le fichier. Si vous n’utilisez pas le serveur local, démarrez-le :
- python manage.py runserver
Naviguez vers http://localhost:8000/graphql
dans votre navigateur web. Exécutez votre première Mutation dans l’interface web GraphiQL en exécutant la déclaration suivante :
mutation {
createUrl(fullUrl:"https://www.digitalocean.com/community") {
url {
id
fullUrl
urlHash
clicks
createdAt
}
}
}
Vous avez composé la Mutation avec le nom createURL
, l’argument fullUrl
, et les données que vous souhaitez voir figurer dans la réponse définie à l’intérieur du champ de l’url
.
La sortie contiendra les informations URL que vous venez de créer dans les données
GraphQL comme indiqué ici :
Output{
"data": {
"createUrl": {
"url": {
"id": "1",
"fullUrl": "https://www.digitalocean.com/community",
"urlHash": "077880af78",
"clicks": 0,
"createdAt": "2020-01-30T19:15:10.820062+00:00"
}
}
}
}
Avec cela, une URL a été ajoutée à la base de données avec sa version hachée, comme vous pouvez le voir dans le champ urlHash
. Essayez d’exécuter la requête que vous avez créée dans la dernière étape pour voir son résultat :
query {
urls {
id
fullUrl
urlHash
clicks
createdAt
}
}
La sortie affichera l’URL enregistrée :
Output{
"data": {
"urls": [
{
"id": "1",
"fullUrl": "https://www.digitalocean.com/community",
"urlHash": "077880af78",
"clicks": 0,
"createdAt": "2020-03-18T21:03:24.664934+00:00"
}
]
}
}
Vous pouvez également essayer d’exécuter la même requête, mais en ne demandant que les champs que vous voulez.
Ensuite, essayez-le encore une fois avec une autre URL :
mutation {
createUrl(fullUrl:"https://www.digitalocean.com/write-for-donations/") {
url {
id
fullUrl
urlHash
clicks
createdAt
}
}
}
Le résultat sera :
Output{
"data": {
"createUrl": {
"url": {
"id": "2",
"fullUrl": "https://www.digitalocean.com/write-for-donations/",
"urlHash": "703562669b",
"clicks": 0,
"createdAt": "2020-01-30T19:31:10.820062+00:00"
}
}
}
}
Le système est maintenant capable de créer des URL courtes et de les répertorier. Dans l’étape suivante, vous permettrez aux utilisateurs d’accéder à une URL par sa version courte en les redirigeant vers la bonne page.
Dans cette étape, vous utiliserez Django Views-une méthode qui prend une demande et renvoie une réponse - pour rediriger toute personne accédant à la http://localhost:8000/url_hash
à son URL complète.
Ouvrez le shortener/views.py
avec votre éditeur :
- vim shortener/views.py
Pour commencer, importez deux paquets en remplaçant le contenu par les lignes suivantes :
from django.shortcuts import get_object_or_404, redirect
from .models import URL
Celles-ci seront expliquées plus en détail ultérieurement.
Ensuite, vous allez créer une Django View nommée root
(racine). Ajoutez ce bout de code responsable de la View à la fin de votre fichier :
...
def root(request, url_hash):
url = get_object_or_404(URL, url_hash=url_hash)
url.clicked()
return redirect(url.full_url)
Il reçoit un argument appelé url_hash
à partir de l’URL demandée par un utilisateur. Dans la fonction, la première ligne essaie de récupérer l’URL de la base de données en utilisant l’argument url_hash
. Si elle n’est pas trouvée, elle renvoie l’erreur HTTP 404 au client, ce qui signifie que la ressource est manquante. Ensuite,elle augmente le la propriété cliquée
de l’entrée de l’URL, en veillant à suivre le nombre de fois où l’URL est consultée. A la fin, elle redirige le client vers l’URL demandée.
L’ensemble shortener/views.py
est affiché ici :
from django.shortcuts import get_object_or_404, redirect
from .models import URL
def root(request, url_hash):
url = get_object_or_404(URL, url_hash=url_hash)
url.clicked()
return redirect(url.full_url)
Enregistrez et fermez le fichier.
Ensuite, ouvrez shorty/urls.py
:
- vim shorty/urls.py
Ajoutez le code surligné suivant pour activer la View racine
.
from django.urls import path
from django.views.decorators.csrf import csrf_exempt
from graphene_django.views import GraphQLView
from shortener.views import root
urlpatterns = [
path('graphql/', csrf_exempt(GraphQLView.as_view(graphiql=True))),
path('<str:url_hash>/', root, name='root'),
]
La View racine
sera accessible dans le /
path de votre serveur, en acceptant un url_hash
comme paramètre de chaîne.
Enregistrez et fermez le fichier. Si vous n’utilisez pas le serveur local, démarrez-le en exécutant la commande python manage.py runserver
.
Pour tester votre nouvel ajout, ouvrez votre navigateur web et accédez à l’URL http://localhost:8000/077880af78
. Notez que la dernière partie de l’URL est le hachage créé par la mutation de l’étape 5. Vous serez redirigé vers la page URL du hachage, dans ce cas, le site web de la communauté DigitalOcean.
Maintenant que la redirection URL fonctionne, vous rendrez l’application plus sûre en mettant en œuvre un traitement des erreurs lors de l’exécution de la mutation.
Le traitement des erreurs est une bonne habitude à avoir dans toutes les applications, car les développeurs ne contrôlent généralement pas ce qui sera envoyé au serveur. Dans ce cas, vous pouvez essayer de prévoir les défaillances et de minimiser leurs impacts. Dans un système complexe tel que GraphQL, beaucoup de choses peuvent mal tourner, du client qui demande des données erronées jusqu’au serveur qui perd l’accès à la base de données.
En tant que système typographique , GraphQL peut vérifier tout ce que le client demande et reçoit dans une opération appelée Schema Validation (Validation du schéma). Vous pouvez le voir en action en effectuant une Query ou (Requête) avec un champ non existant.
Naviguez vers http://localhost:8000/graphql
dans votre navigateur, et exécutez la requête suivante dans l’interface GraphiQL avec le champ iDontExist
:
query {
urls {
id
fullUrl
urlHash
clicks
createdAt
iDontExist
}
}
Comme il n’y a pas de iDontExist
défini dans votre requête, GraphQL renvoie un message d’erreur :
Output
{
"errors": [
{
"message": "Cannot query field \"iDontExist\" on type \"URLType\".",
"locations": [
{
"line": 8,
"column": 5
}
]
}
]
}
Ceci est important car, dans le système typographique GraphQL, l’objectif est d’envoyer et de recevoir uniquement les informations déjà définies dans le schéma.
La présente demande accepte tout type de chaîne arbitraire dans le champ full_url
. Le problème est que si quelqu’un envoyait une URL mal construite, vous redirigeriez l’utilisateur vers nulle part en essayant les informations stockées. Dans ce cas, vous devez vérifier si le full_url
est bien formaté avant de l’enregistrer dans la base de données, et, en cas d’erreur, lever l’exception GraphQLError
avec un message personnalisé.
Mettons en place cette fonctionnalité en deux étapes. Tout d’abord, ouvrez le fichier raccourcis/modèles.py
:
- vim shortener/models.py
Ajoutez les lignes surlignées dans la section d’importation :
from hashlib import md5
from django.db import models
from django.core.validators import URLValidator
from django.core.exceptions import ValidationError
from graphql import GraphQLError
...
L’URLValidator
est une aide de Django pour valider une chaîne d’URL et l'erreur GraphQLE
est utilisée par Graphene pour lever des exceptions avec un message personnalisé.
Ensuite, assurez-vous de valider l’URL reçue par l’utilisateur avant de l’enregistrer dans la base de données. Activez cette opération en ajoutant le code mis en évidence dans le fichier raccourcis/modèles.py
:
class URL(models.Model):
full_url = models.URLField(unique=True)
url_hash = models.URLField(unique=True)
clicks = models.IntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
def clicked(self):
self.clicks += 1
self.save()
def save(self, *args, **kwargs):
if not self.id:
self.url_hash = md5(self.full_url.encode()).hexdigest()[:10]
validate = URLValidator()
try:
validate(self.full_url)
except ValidationError as e:
raise GraphQLError('invalid url')
return super().save(*args, **kwargs)
Premièrement, ce code instancie l’URLValidator
dans la variable validate
. A l’intérieur du bloc try/except
, vous validez()
l’URL reçue et faites apparaître une erreur GraphQLE
avec le message personnalisé d'url invalide
si quelque chose s’est mal passé.
Le fichier complet shortener/models.py
est affiché ici :
from hashlib import md5
from django.db import models
from django.core.validators import URLValidator
from django.core.exceptions import ValidationError
from graphql import GraphQLError
class URL(models.Model):
full_url = models.URLField(unique=True)
url_hash = models.URLField(unique=True)
clicks = models.IntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
def clicked(self):
self.clicks += 1
self.save()
def save(self, *args, **kwargs):
if not self.id:
self.url_hash = md5(self.full_url.encode()).hexdigest()[:10]
validate = URLValidator()
try:
validate(self.full_url)
except ValidationError as e:
raise GraphQLError('invalid url')
return super().save(*args, **kwargs)
Enregistrez et fermez le fichier. Si vous n’utilisez pas le serveur local, démarrez-le avec la commande python manage.py runserver
.
Ensuite, testez votre nouveau traitement des erreurs sur http://localhost:8000/graphql
. Essayez de créer une nouvelle URL avec un full_url
non valide dans l’interface GraphiQL :
mutation {
createUrl(fullUrl:"not_valid_url"){
url {
id
fullUrl
urlHash
clicks
createdAt
}
}
}
Lorsque vous envoyez une URL non valide, votre exception sera signalée par le message personnalisé :
Output
{
"errors": [
{
"message": "invalid url",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"createUrl"
]
}
],
"data": {
"createUrl": null
}
}
Si vous regardez dans votre terminal où lacommande python manage.py runserver
est en cours d’exécution, une erreur apparaîtra :
Output
...
graphql.error.located_error.GraphQLLocatedError: invalid url
[30/Jan/2020 19:46:32] "POST /graphql/ HTTP/1.1" 200 121
Un terminal GraphQL échouera toujours avec un code d’état HTTP 200, ce qui signifie généralement un succès. Rappelez-vous que, même si GraphQL est construit au-dessus de HTTP, il n’utilise pas les concepts de codes d’état HTTP ou de méthodes HTTP comme le fait REST.
Grâce à la mise en place du traitement des erreurs, vous pouvez maintenant mettre en place un mécanisme pour filtrer vos requêtes en minimisant les informations renvoyées par le serveur.
Imaginez que vous avez commencé à utiliser le raccourcisseur d’URL pour ajouter vos propres liens. Au bout d’un certain temps, il y aura tellement d’entrées qu’il sera difficile de trouver la bonne. Vous pouvez résoudre ce problème en utilisant des filtres.
Le filtrage est un concept courant dans les API REST, où généralement un Paramètre d’interrogation avec un champ et une valeur est ajouté à l’URL. Par exemple, pour filtrer tous les utilisateurs nommés jojo, vous pouvez utiliser GET /api/users?name=jojo
.
Dans GraphQL, vous utiliserez les Query Arguments (Arguments de requête) comme filtres. Ils créent une interface agréable et propre.
Vous pouvez résoudre le problème des « URL difficiles à trouver » en permettant au client de filtrer les URL par leur nom en utilisant le champ full_url
. Pour ce faire, il faut ouvrir le fichiershortener/schema.py
dans votre éditeur préféré.
- vim shortener/schema.py
Tout d’abord, importez la méthode Q
dans la ligne surlignée :
import graphene
from graphene_django import DjangoObjectType
from django.db.models import Q
from .models import URL
...
Elle sera utilisée pour filtrer votre recherche dans la base de données.
Ensuite, réécrivez l’ensemble classe de recherche
avec le contenu suivant :
...
class Query(graphene.ObjectType):
urls = graphene.List(URLType, url=graphene.String())
def resolve_urls(self, info, url=None, **kwargs):
queryset = URL.objects.all()
if url:
_filter = Q(full_url__icontains=url)
queryset = queryset.filter(_filter)
return queryset
...
Les modifications que vous apportez sont :
d'url
à l’intérieur de la variable urls
et laméthode resolve_url
.resolve_urls
,si un paramètre nommé est
donné, filtrer les résultats de la base de données aboutit à renvoyer les URL qui contiennent la valeur donnée en utilisant la méthode Q (full_url__icontains=url)
méthode.Le fichier complet shortener/schema.py
est présenté ici :
import graphene
from graphene_django import DjangoObjectType
from django.db.models import Q
from .models import URL
class URLType(DjangoObjectType):
class Meta:
model = URL
class Query(graphene.ObjectType):
urls = graphene.List(URLType, url=graphene.String())
def resolve_urls(self, info, url=None, **kwargs):
queryset = URL.objects.all()
if url:
_filter = Q(full_url__icontains=url)
queryset = queryset.filter(_filter)
return queryset
class CreateURL(graphene.Mutation):
url = graphene.Field(URLType)
class Arguments:
full_url = graphene.String()
def mutate(self, info, full_url)
url = URL(full_url=full_url)
url.save()
return CreateURL(url=url)
class Mutation(graphene.ObjectType):
create_url = CreateURL.Field()
Enregistrez et fermez le fichier. Si vous n’utilisez pas le serveur local, commencez par python manage.py runserver
.
Testez vos derniers changements sur http://localhost:8000/graphql
. Dans l’interface GraphiQL, écrivez la déclaration suivante. Il filtrera toutes les URL avec le mot communauté :
query {
urls(url:"community") {
id
fullUrl
urlHash
clicks
createdAt
}
}
Le résultat n’est qu’une seule entrée puisque vous venez d’ajouter une URL avec la chaîne communautaire
dedans. Si vous avez ajouté d’autres URL auparavant, votre résultat peut varier.
Output
{
"data": {
"urls": [
{
"id": "1",
"fullUrl": "https://www.digitalocean.com/community",
"urlHash": "077880af78",
"clicks": 1,
"createdAt": "2020-01-30T19:27:36.243900+00:00"
}
]
}
}
Vous avez maintenant la possibilité d’effectuer des recherches par le biais de vos URL. Cependant, avec trop de liens, vos clients pourraient se plaindre que la liste d’URL renvoie plus de données que leurs applications ne peuvent traiter. Pour résoudre ce problème, vous allez mettre en place la pagination.
Les clients qui utilisent votre backend peuvent se plaindre que le temps de réponse est trop long ou que sa taille est trop importante s’il y a trop d’entrées d’URL. Même votre base de données peut avoir du mal à rassembler un grand nombre d’informations. Pour résoudre ce problème, vous pouvez permettre au client de préciser le nombre d’éléments qu’il souhaite dans chaque demande en utilisant une technique appelée la pagination.
Il n’y a pas de moyen par défaut de mettre en œuvre cette fonctionnalité. Même dans les API REST, vous pouvez le voir dans les en-têtes HTTP ou les paramètres de requête, avec des noms et des comportements différents.
Dans cette application, vous implémenterez la pagination en activant deux arguments supplémentaires à la requête d’URL : first
et skip
. first
sélectionnera le premier nombre variable d’éléments et skip
spécifiera combien d’éléments doivent être sautés depuis le début. Par exemple, en utilisant d'abord == 10
et en sautant == 5
, on obtient les 10 premières URL, mais on en saute 5, ce qui ne renvoie que les 5 restantes.
La mise en œuvre de cette solution est similaire à l’ajout d’un filtre.
Ouvrez le fichier shortener/schema.py
:
- vim shortener/schema.py
Dans le dossier, modifiez la classe de recherche
en ajoutant les deux nouveaux paramètres dans la variable urls
et la méthode resolve_urls
, mise en évidence dans le code suivant :
import graphene
from graphene_django import DjangoObjectType
from django.db.models import Q
from .models import URL
class Query(graphene.ObjectType):
urls = graphene.List(URLType, url=graphene.String(), first=graphene.Int(), skip=graphene.Int())
def resolve_urls(self, info, url=None, first=None, skip=None, **kwargs):
queryset = URL.objects.all()
if url:
_filter = Q(full_url__icontains=url)
queryset = queryset.filter(_filter)
if first:
queryset = queryset[:first]
if skip:
queryset = queryset[skip:]
return queryset
...
Ce code utilise les premiers
paramètres nouvellement créés et saute
les paramètres à l’intérieur de la méthode resolve_urls
pour filtrer la requête de la base de données.
Enregistrez et fermez le fichier. Si vous n’utilisez pas le serveur local, commencez par python manage.py runserver
.
Pour tester la pagination, lancez la requête suivante dans l’interface GraphiQL à http://localhost:8000/graphql
:
query {
urls(first: 2, skip: 1) {
id
fullUrl
urlHash
clicks
createdAt
}
}
Votre raccourcisseur d’URL renvoie la deuxième URL créée dans votre base de données :
Output
{
"data": {
"urls": [
{
"id": "2",
"fullUrl": "https://www.digitalocean.com/write-for-donations/",
"urlHash": "703562669b",
"clicks": 0,
"createdAt": "2020-01-30T19:31:10.820062+00:00"
}
]
}
}
Cela montre que la fonction de pagination fonctionne. N’hésitez pas à vous amuser en ajoutant d’autres URL et en testant différentes séries de first
et skip
.
L’ensemble de l’écosystème GraphQL se développe chaque jour avec une communauté active derrière lui. Des entreprises comme GitHub et Facebook ont prouvé qu’il était prêt au lancement, et vous pouvez maintenant appliquer cette technologie à vos propres projets.
Dans ce tutoriel, vous avez créé un service de raccourcissement d’URL à l’aide de GraphQL, Python et Django en utilisant des concepts comme les requêtes et les mutations. Mais plus que cela, vous comprenez maintenant comment s’appuyer sur ces technologies pour construire des applications web en utilisant le framework web Django.
Vous pouvez en savoir plus sur GraphQL et les outils utilisés ici dans le site web de GraphQL et les sites web de documentation consacrés à Graphene. De plus, DigitalOcean propose des tutoriels supplémentaires pour Python et Django que vous pouvez utiliser si vous souhaitez en savoir plus sur l’un ou l’autre.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!