DjangoとGraphQLを使用してURL短縮サービスを作成する方法
著者は、 Write for DOnations プログラムの一環として、 Girls WhoCodeを選択して寄付を受け取りました。
序章
GraphQL は、 REST APIの代替として、Facebookによって作成およびオープンソース化されたAPI標準です。 REST APIとは対照的に、GraphQLは型付きシステムを使用してデータ構造を定義します。この場合、送受信されるすべての情報は、事前定義されたスキーマに準拠している必要があります。 また、さまざまなリソースの複数のURLではなく、すべての通信に対して単一のエンドポイントを公開し、クライアントから要求されたデータのみを返すことで overfetching の問題を解決し、より小さく簡潔な応答を生成します。
このチュートリアルでは、 URL短縮サービスのバックエンドを作成します。これは、クエリやミューテーションなどのGraphQLの概念を掘り下げながら、任意のURLを取得してより短く読みやすいバージョンを生成するサービスです。 、およびGraphiQLインターフェースなどのツール。 bit.ly のように、以前にそのようなサービスを使用したことがあるかもしれません。
GraphQLは言語に依存しないテクノロジーであるため、さまざまな言語やフレームワークの上に実装されています。 ここでは、汎用の Pythonプログラミング言語、 Django Webフレームワーク、および Graphene-Django ライブラリを、特定の統合を備えたGraphQLPython実装として使用します。ジャンゴ。
前提条件
-
このチュートリアルを続行するには、開発マシンにPythonバージョン3.5以降がインストールされている必要があります。 Pythonをインストールするには、お使いのOS用のPython3のローカルプログラミング環境をインストールしてセットアップする方法に関するチュートリアルに従ってください。 仮想環境も作成して開始してください。 このチュートリアルの先導に従うために、プロジェクトディレクトリに名前を付けることができます
shorty
. -
Djangoの初級レベルの知識が必要ですが、必須ではありません。 興味があれば、DigitalOceanコミュニティによって作成されたこのDjango開発シリーズをフォローできます。
ステップ1—Djangoプロジェクトのセットアップ
このステップでは、アプリケーションに必要なすべてのツールをインストールし、Djangoプロジェクトをセットアップします。
前提条件で説明されているように、プロジェクトディレクトリを作成して仮想環境を開始したら、次を使用して必要なパッケージをインストールします。 pip
、Pythonパッケージマネージャー。 このチュートリアルでは、Djangoバージョン2.1.7とGraphene-Djangoバージョン2.2.0以降をインストールします。
- pip install "django==2.1.7" "graphene-django>==2.2.0"
これで、ツールベルトに必要なすべてのツールが揃いました。 次に、を使用してDjangoプロジェクトを作成します django-admin
指図。 プロジェクトは、デフォルトのDjangoボイラープレートであり、Webアプリケーションの開発を開始するために必要なすべてのものを含むフォルダーとファイルのセットです。 この場合、プロジェクトを呼び出します shorty
を指定して、現在のフォルダ内に作成します .
最後に:
- django-admin startproject shorty .
プロジェクトを作成したら、Django移行を実行します。 これらのファイルには、Djangoによって生成されたPythonコードが含まれており、Djangoモデルに従ってアプリケーションの構造を変更する役割を果たします。 たとえば、変更にはテーブルの作成が含まれる場合があります。 デフォルトでは、Djangoには Django Authentication などのサブシステムを担当する独自の移行セットが付属しているため、次のコマンドでそれらを実行する必要があります。
- python manage.py migrate
このコマンドは、Pythonインタープリターを使用して、次のDjangoスクリプトを呼び出します。 manage.py
、アプリの作成や移行の実行など、プロジェクトのさまざまな側面を管理する責任があります。
これにより、次のような出力が得られます。
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
Djangoのデータベースの準備ができたら、ローカル開発サーバーを起動します。
- python manage.py runserver
これにより、次のようになります。
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.
このコマンドは、ターミナルのプロンプトを取り除き、サーバーを起動します。
訪問 http://127.0.0.1:8000
ローカルブラウザのページ。 このページが表示されます:
サーバーを停止してターミナルに戻るには、を押します。 CTRL+C
. ブラウザにアクセスする必要があるときはいつでも、前のコマンドが実行されていることを確認してください。
次に、プロジェクトでDjango-Grapheneライブラリを有効にして、この手順を完了します。 Djangoには次の概念があります app
、特定の責任を持つWebアプリケーション。 プロジェクトは、1つまたは複数のアプリで構成されます。 今のところ、 shorty/settings.py
選択したテキストエディタでファイルを作成します。 このチュートリアルでは、vimを使用します。
- vim shorty/settings.py
The settings.py
ファイルは、プロジェクトのすべての設定を管理します。 その中を検索します INSTALLED_APPS
エントリを追加し、 'graphene_django'
ライン:
...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'graphene_django',
]
...
この追加により、Djangoは次のアプリを使用することになります。 graphene_django
、ステップ1でインストールしました。
ファイルの最後に、次の変数を追加します。
...
GRAPHENE = {
'SCHEMA': 'shorty.schema.schema',
}
この最後の変数は、後で作成するメインのSchemaを指します。 GraphQLでは、スキーマには、リソース、クエリ、ミューテーションなどのすべてのオブジェクトタイプが含まれます。 これは、システムで使用可能なすべてのデータと機能を表すドキュメントと考えてください。
変更後、ファイルを保存して閉じます。
これで、Djangoプロジェクトが構成されました。 次のステップでは、Djangoアプリとそのモデルを作成します。
ステップ2—Djangoアプリとモデルのセットアップ
Djangoプラットフォームは通常、1つのプロジェクトと多くのアプリケーションまたはアプリで構成されています。 アプリはプロジェクト内の一連の機能を記述し、適切に設計されていれば、Djangoプロジェクト全体で再利用できます。
このステップでは、というアプリを作成します shortener
、実際のURL短縮機能を担当します。 基本的なスケルトンを作成するには、ターミナルで次のコマンドを入力します。
- python manage.py startapp shortener
ここではパラメータを使用しました startapp app_name
、指示 manage.py
名前の付いたアプリを作成するには shortener
.
アプリの作成を完了するには、 shorty/settings.py
ファイル
- vim shorty/settings.py
同じにアプリの名前を追加します INSTALLED_APPS
以前に変更したエントリ:
...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'graphene_django'
'shortener',
]
...
ファイルを保存して閉じます。
あなたと shortener
に追加 shorty/settings.py
、プロジェクトのモデルの作成に進むことができます。 モデルはDjangoの重要な機能の1つです。 これらは「Pythonic」方式でデータベースを表すために使用され、Pythonコードを使用してデータを管理、クエリ、および保存できるようにします。
開く前に models.py
変更のファイル。このチュートリアルでは、行う変更の概要を説明します。
モデルファイル—shortener/models.py
-既存のコードを置き換えると、次のコンテンツが含まれます。
from hashlib import md5
from django.db import models
ここでは、コードに必要なパッケージをインポートします。 行を追加します from hashlib import md5
上部に、URLのhashを作成するために使用されるPython標準ライブラリをインポートします。 The from django.db import models
lineは、モデルを作成するためのDjangoヘルパーです。
警告:このチュートリアルでは、入力を受け取り、常に同じ出力を返す関数の結果として、hashを参照します。 このチュートリアルでは、デモンストレーションの目的でMD5ハッシュ関数を使用します。
MD5には衝突の問題があり、本番環境では回避する必要があることに注意してください。
次に、という名前のモデルを追加します URL
次のフィールドを使用します。
full_url
:短縮するURL。url_hash
:完全なURLを表す短いハッシュ。clicks
:短縮URLにアクセスした回数。created_at
:URLが作成された日時。
...
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)
を生成します url_hash
MD5ハッシュアルゴリズムをに適用することによって full_url
フィールドとモデルの間に返された最初の10文字だけを使用 save()
Djangoがエントリをデータベースに保存するたびに実行されるメソッド。 さらに、URL短縮サービスは通常、リンクがクリックされた回数を追跡します。 これは、メソッドを呼び出すことで実現できます clicked()
ユーザーがURLにアクセスしたとき。
上記の操作は、 URL
このコードのモデル:
...
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)
コードを確認したので、 shortener/models.py
ファイル:
- vim shortener/models.py
コードを次のコンテンツに置き換えます。
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)
必ずファイルを保存して閉じてください。
これらの変更をデータベースに適用するには、次のコマンドを実行して移行を作成する必要があります。
- python manage.py makemigrations
これにより、次の出力が得られます。
OutputMigrations for 'shortener':
shortener/migrations/0001_initial.py
- Create model URL
次に、移行を実行します。
- python manage.py migrate
端末に次の出力が表示されます。
OutputOperations to perform:
Apply all migrations: admin, auth, contenttypes, sessions, shortener
Running migrations:
Applying shortener.0001_initial... OK
モデルを設定したので、次のステップでGraphQLエンドポイントとクエリを作成します。
ステップ3—クエリの作成
RESTアーキテクチャは、さまざまなエンドポイントでさまざまなリソースを公開し、各エンドポイントには明確に定義されたデータ構造が含まれています。 たとえば、次の場所でユーザーのリストを取得できます。 /api/users
、常に同じフィールドを期待します。 一方、GraphQLは、すべての対話に対して単一のエンドポイントを持ち、クエリを使用してデータにアクセスします。 主な(そして最も価値のある)違いは、クエリを使用して1つのリクエスト内ですべてのユーザーを取得できることです。
すべてのURLをフェッチするクエリを作成することから始めます。 いくつか必要なものがあります。
- 以前に定義したモデルにリンクされたURLタイプ。
- 名前の付いたQueryステートメント
urls
. - クエリを解決するメソッド。つまり、データベースからすべてのURLをフェッチし、それらをクライアントに返します。
と呼ばれる新しいファイルを作成します shortener/schema.py
:
- vim shortener/schema.py
Pythonを追加することから始めます import
ステートメント:
import graphene
from graphene_django import DjangoObjectType
from .models import URL
最初の行はメインをインポートします graphene
ライブラリ。ベースのGraphQLタイプが含まれています。 List
. The DjangoObjectType
は任意のDjangoモデルからスキーマ定義を作成するためのヘルパーであり、3行目は以前に作成したものをインポートします URL
モデル。
その後、新しいGraphQLタイプを作成します。 URL
次の行を追加してモデル化します。
...
class URLType(DjangoObjectType):
class Meta:
model = URL
最後に、これらの行を追加して、 URL
モデル:
...
class Query(graphene.ObjectType):
urls = graphene.List(URLType)
def resolve_urls(self, info, **kwargs):
return URL.objects.all()
このコードは Query
1つのフィールドという名前のクラス urls
、これは以前に定義されたリストです URLType
. を介してクエリを解決する場合 resolve_urls
メソッドでは、データベースに保存されているすべてのURLを返します。
フル shortener/schema.py
ファイルはここに表示されます:
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()
ファイルを保存して閉じます。
これで、すべてのクエリをメインスキーマに追加する必要があります。 それをあなたのすべてのリソースの保有者と考えてください。
に新しいファイルを作成します shorty/schema.py
パスを作成し、エディターで開きます。
- vim shorty/schema
次の行を追加して、次のPythonパッケージをインポートします。 最初のものは、すでに述べたように、基本のGraphQLタイプを含んでいます。 2行目は、以前に作成したスキーマファイルをインポートします。
import graphene
import shortener.schema
次に、メインを追加します Query
クラス。 継承を介して、作成されたすべてのクエリと将来の操作を保持します。
...
class Query(shortener.schema.Query, graphene.ObjectType):
pass
最後に、を作成します schema
変数:
...
schema = graphene.Schema(query=Query)
The SCHEMA
手順2で定義した設定は、 schema
作成した変数。
フル shorty/schema.py
ファイルはここに表示されます:
import graphene
import shortener.schema
class Query(shortener.schema.Query, graphene.ObjectType):
pass
schema = graphene.Schema(query=Query)
ファイルを保存して閉じます。
次に、GraphQLエンドポイントと GraphiQL インターフェースを有効にします。これは、GraphQLシステムとの対話に使用されるグラフィカルWebインターフェースです。
を開きます shorty/urls.py
ファイル:
- vim shorty/urls.py
学習のために、ファイルの内容を削除して保存し、最初から始められるようにします。
追加する最初の行は、Pythonインポートステートメントです。
from django.urls import path
from django.views.decorators.csrf import csrf_exempt
from graphene_django.views import GraphQLView
The path
関数は、GraphiQLインターフェースのアクセス可能なURLを作成するためにDjangoによって使用されます。 その後、インポートします csrf_exempt
、これにより、クライアントはサーバーにデータを送信できます。 完全な説明は、グラフェンドキュメントにあります。 最後の行では、インターフェースを担当する実際のコードを次の方法でインポートしました。 GraphQLView
.
次に、という名前の変数を作成します urlpatterns
.
...
urlpatterns = [
path('graphql/', csrf_exempt(GraphQLView.as_view(graphiql=True))),
]
これにより、GraphiQLインターフェースをで利用できるようにするために必要なすべてのコードがつなぎ合わされます。 graphql/
道:
フル shortener/urls.py
ファイルはここに表示されます:
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))),
]
ファイルを保存して閉じます。
ターミナルに戻り、 python manage.py runserver
コマンド(まだ実行されていない場合):
- python manage.py runserver
でWebブラウザを開きます http://localhost:8000/graphql
住所。 次の画面が表示されます。
GraphiQLは、GraphQLステートメントを実行して結果を確認できるインターフェースです。 1つの機能は Docs
右上のセクション。 GraphQLのすべてが型指定されているため、すべての型、クエリ、ミューテーションなどに関する無料のドキュメントを入手できます。
ページを探索した後、メインテキスト領域に最初のクエリを挿入します。
query {
urls {
id
fullUrl
urlHash
clicks
createdAt
}
}
このコンテンツは、GraphQLクエリの構造を示しています。まず、キーワードを使用します query
一部のデータのみを戻したいことをサーバーに通知します。 次に、 urls
で定義されたフィールド shortener/schema.py
内部のファイル Query
クラス。 それから、で定義されたすべてのフィールドを明示的に要求します URL
GraphQLのデフォルトであるキャメルケーススタイルを使用したモデル。
次に、左上の再生矢印ボタンをクリックします。
まだURLがないことを示す次の応答が表示されます。
Output{
"data": {
"urls": []
}
}
これは、GraphQLが機能していることを示しています。 ターミナルで、 CTRL+C
サーバーを停止します。
このステップでは、GraphQLエンドポイントを作成し、すべてのURLをフェッチするクエリを作成し、GraphiQLインターフェースを有効にすることで多くのことを達成しました。 次に、データベースを変更するためのミューテーションを作成します。
ステップ4—ミューテーションの作成
ほとんどのアプリケーションには、データを追加、更新、または削除することでデータベースの状態を変更する方法があります。 GraphQLでは、これらの操作はMutationsと呼ばれます。 これらはクエリのように見えますが、引数を使用してサーバーにデータを送信します。
最初のミューテーションを作成するには、 shortener/schema.py
:
- vim shortener/schema.py
ファイルの最後に、名前の付いた新しいクラスを追加することから始めます CreateURL
:
...
class CreateURL(graphene.Mutation):
url = graphene.Field(URLType)
このクラスは、 graphene.Mutation
GraphQLミューテーションの機能を持つヘルパー。 プロパティ名もあります url
、ミューテーションの完了後にサーバーから返されるコンテンツを定義します。 この場合、 URLType
データ構造。
次に、という名前のサブクラスを追加します Arguments
すでに定義されているクラスへ:
...
class Arguments:
full_url = graphene.String()
これは、サーバーが受け入れるデータを定義します。 ここでは、という名前のパラメータを期待しています full_url
とともに String
コンテンツ:
次に、次の行を追加して、 mutate
方法:
...
def mutate(self, info, full_url):
url = URL(full_url=full_url)
url.save()
これ mutate
メソッドは、クライアントからデータを受信してデータベースに保存することにより、多くの作業を実行します。 最後に、新しく作成されたアイテムを含むクラス自体を返します。
最後に、 Mutation
次の行を追加して、アプリのすべてのミューテーションを保持するクラス:
...
class Mutation(graphene.ObjectType):
create_url = CreateURL.Field()
これまでのところ、名前が付けられたミューテーションは1つだけです。 create_url
.
フル shortener/schema.py
ファイルはここに表示されます:
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()
ファイルを閉じて保存します。
ミューテーションの追加を完了するには、 shorty/schema.py
ファイル:
- vim shorty/schema.py
次の強調表示されたコードを含むようにファイルを変更します。
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)
ファイルを保存して閉じます。 ローカルサーバーを実行していない場合は、次のように起動します。
- python manage.py runserver
案内する http://localhost:8000/graphql
Webブラウザで。 次のステートメントを実行して、GraphiQLWebインターフェースで最初のミューテーションを実行します。
mutation {
createUrl(fullUrl:"https://www.digitalocean.com/community") {
url {
id
fullUrl
urlHash
clicks
createdAt
}
}
}
あなたはミューテーションを createURL
名前、 fullUrl
引数、および内部で定義された応答に必要なデータ url
分野。
出力には、GraphQL内で作成したURL情報が含まれます。 data
ここに示すように、フィールド:
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"
}
}
}
}
これにより、ハッシュ化されたバージョンのURLがデータベースに追加されました。 urlHash
分野。 最後のステップで作成したクエリを実行して、結果を確認してください。
query {
urls {
id
fullUrl
urlHash
clicks
createdAt
}
}
出力には、保存されているURLが表示されます。
Output{
"data": {
"urls": [
{
"id": "1",
"fullUrl": "https://www.digitalocean.com/community",
"urlHash": "077880af78",
"clicks": 0,
"createdAt": "2020-03-18T21:03:24.664934+00:00"
}
]
}
}
同じクエリを実行してみることもできますが、必要なフィールドのみを要求します。
次に、別のURLでもう一度試してください。
mutation {
createUrl(fullUrl:"https://www.digitalocean.com/write-for-donations/") {
url {
id
fullUrl
urlHash
clicks
createdAt
}
}
}
出力は次のようになります。
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"
}
}
}
}
これで、システムは短いURLを作成して一覧表示できるようになりました。 次のステップでは、ユーザーが短いバージョンでURLにアクセスできるようにし、正しいページにリダイレクトします。
ステップ5—アクセスエンドポイントを作成する
このステップでは、 Django Views (リクエストを受け取り、レスポンスを返すメソッド)を使用して、にアクセスするすべてのユーザーをリダイレクトします。 http://localhost:8000/url_hash
完全なURLへのエンドポイント。
を開きます shortener/views.py
エディターにファイルする:
- vim shortener/views.py
まず、内容を次の行に置き換えて2つのパッケージをインポートします。
from django.shortcuts import get_object_or_404, redirect
from .models import URL
これらについては、後で詳しく説明します。
次に、という名前のDjangoビューを作成します root
. ファイルの最後に、ビューを担当する次のコードスニペットを追加します。
...
def root(request, url_hash):
url = get_object_or_404(URL, url_hash=url_hash)
url.clicked()
return redirect(url.full_url)
これはと呼ばれる引数を受け取ります url_hash
ユーザーが要求したURLから。 関数内で、最初の行はデータベースからURLを取得しようとします。 url_hash
口論。 見つからない場合は、HTTP 404エラーをクライアントに返します。これは、リソースが欠落していることを意味します。 その後、それは増分します clicked
URLエントリのプロパティ。URLがアクセスされた回数を追跡するようにしてください。 最後に、クライアントを要求されたURLにリダイレクトします。
フル shortener/views.py
ファイルはここに表示されます:
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)
ファイルを保存して閉じます。
次に、開く shorty/urls.py
:
- vim shorty/urls.py
次の強調表示されたコードを追加して、 root
意見。
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'),
]
The root
ビューはでアクセス可能になります /
サーバーのパス、受け入れます url_hash
文字列パラメータとして。
ファイルを保存して閉じます。 ローカルサーバーを実行していない場合は、ローカルサーバーを実行して起動します。 python manage.py runserver
指図。
新しい追加をテストするには、Webブラウザを開いて、 http://localhost:8000/077880af78
URL。 URLの最後の部分は、ステップ5のミューテーションによって作成されたハッシュであることに注意してください。 ハッシュのURLページ(この場合はDigitalOcean Community Webサイト)にリダイレクトされます。
URLリダイレクトが機能するようになったので、Mutationの実行時にエラー処理を実装することで、アプリケーションをより安全にします。
ステップ6—エラー処理の実装
開発者は通常、サーバーに送信される内容を制御しないため、エラーの処理はすべてのアプリケーションのベストプラクティスです。 この場合、障害を予測し、その影響を最小限に抑えることができます。 GraphQLのような複雑なシステムでは、クライアントが間違ったデータを要求したり、サーバーがデータベースにアクセスできなくなったりするなど、多くのことがうまくいかない可能性があります。
型付きシステムとして、GraphQLは、 SchemaValidationと呼ばれる操作でクライアントが要求および受信するすべてのものを検証できます。 存在しないフィールドを使用してクエリを作成することで、これが実際に動作していることを確認できます。
案内する http://localhost:8000/graphql
ブラウザでもう一度、GraphiQLインターフェイス内で次のクエリを実行します。 iDontExist
分野:
query {
urls {
id
fullUrl
urlHash
clicks
createdAt
iDontExist
}
}
ないので iDontExist
クエリで定義されたフィールド、GraphQLはエラーメッセージを返します:
Output
{
"errors": [
{
"message": "Cannot query field \"iDontExist\" on type \"URLType\".",
"locations": [
{
"line": 8,
"column": 5
}
]
}
]
}
GraphQLタイプのシステムでは、スキーマですでに定義されている情報だけを送受信することが目的であるため、これは重要です。
現在のアプリケーションは、任意の文字列を受け入れます full_url
分野。 問題は、誰かが不適切に構成されたURLを送信した場合、保存された情報を試すときにユーザーをどこにもリダイレクトしないことです。 この場合、次のことを確認する必要があります full_url
データベースに保存する前に適切にフォーマットされており、エラーが発生した場合は、 GraphQLError
カスタムメッセージの例外。
この機能を2つのステップで実装しましょう。 まず、 shortener/models.py
ファイル:
- vim shortener/models.py
インポートセクションに強調表示された行を追加します。
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
...
The URLValidator
URL文字列とを検証するためのDjangoヘルパーです GraphQLError
グラフェンは、カスタムメッセージで例外を発生させるために使用します。
次に、データベースに保存する前に、ユーザーが受信したURLを必ず検証してください。 強調表示されたコードをに追加して、この操作を有効にします shortener/models.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)
まず、このコードはインスタンス化します URLValidator
の中に validate
変数。 内部 try/except
ブロック、あなた validate()
受信したURLを上げて GraphQLError
とともに invalid url
何か問題が発生した場合のカスタムメッセージ。
フル shortener/models.py
ファイルはここに表示されます:
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)
ファイルを保存して閉じます。 ローカルサーバーを実行していない場合は、 python manage.py runserver
指図。
次に、で新しいエラー処理をテストします。 http://localhost:8000/graphql
. 無効なURLで新しいURLを作成してみてください full_url
GraphiQLインターフェースの場合:
mutation {
createUrl(fullUrl:"not_valid_url"){
url {
id
fullUrl
urlHash
clicks
createdAt
}
}
}
無効なURLを送信すると、カスタムメッセージで例外が発生します。
Output
{
"errors": [
{
"message": "invalid url",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"createUrl"
]
}
],
"data": {
"createUrl": null
}
}
あなたがあなたのターミナルを見れば python manage.py runserver
コマンドが実行されている場合、エラーが表示されます:
Output
...
graphql.error.located_error.GraphQLLocatedError: invalid url
[30/Jan/2020 19:46:32] "POST /graphql/ HTTP/1.1" 200 121
GraphQLエンドポイントは常にHTTP200ステータスコードで失敗します。これは通常、成功を意味します。 GraphQLはHTTPの上に構築されていますが、RESTのようにHTTPステータスコードやHTTPメソッドの概念を使用していないことに注意してください。
エラー処理が実装されたので、クエリをフィルタリングするメカニズムを導入して、サーバーから返される情報を最小限に抑えることができます。
ステップ7—フィルターの実装
URL短縮サービスを使用して独自のリンクを追加し始めたと想像してください。 しばらくすると、エントリが非常に多くなり、適切なエントリを見つけるのが難しくなります。 この問題は、フィルターを使用して解決できます。
フィルタリングはRESTAPIの一般的な概念であり、通常、フィールドと値を持つクエリパラメーターがURLに追加されます。 例として、 jojo という名前のすべてのユーザーをフィルタリングするには、次のように使用できます。 GET /api/users?name=jojo
.
GraphQLでは、クエリ引数をフィルターとして使用します。 それらは、すてきでクリーンなインターフェースを作成します。
クライアントがURLを名前でフィルタリングできるようにすることで、「URLが見つからない」問題を解決できます。 full_url
分野。 これを実装するには、 shortener/schema.py
お気に入りのエディタでファイルします。
- vim shortener/schema.py
まず、インポートします Q
強調表示された行のメソッド:
import graphene
from graphene_django import DjangoObjectType
from django.db.models import Q
from .models import URL
...
これは、データベースクエリをフィルタリングするために使用されます。
次に、全体を書き直します Query
次の内容のクラス:
...
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
...
行っている変更は次のとおりです。
- 追加する
url
内部のフィルターパラメーターurls
変数とresolve_url
方法。 - 内部
resolve_urls
、という名前のパラメータの場合url
が指定されている場合、データベースの結果をフィルタリングして、指定された値を含むURLのみを返します。Q(full_url__icontains=url)
方法。
フル shortener/schema.py
ファイルはここに表示されます:
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()
ファイルを保存して閉じます。 ローカルサーバーを実行していない場合は、 python manage.py runserver
.
で最新の変更をテストします http://localhost:8000/graphql
. GraphiQLインターフェースで、次のステートメントを記述します。 communityという単語ですべてのURLをフィルタリングします。
query {
urls(url:"community") {
id
fullUrl
urlHash
clicks
createdAt
}
}
1つのURLを追加しただけなので、出力は1つのエントリのみです。 community
その中の文字列。 以前にURLを追加した場合、出力は異なる場合があります。
Output
{
"data": {
"urls": [
{
"id": "1",
"fullUrl": "https://www.digitalocean.com/community",
"urlHash": "077880af78",
"clicks": 1,
"createdAt": "2020-01-30T19:27:36.243900+00:00"
}
]
}
}
これで、URLを検索できるようになりました。 ただし、リンクが多すぎると、クライアントは、URLリストがアプリで処理できるよりも多くのデータを返していると不満を言う可能性があります。 これを解決するには、ページネーションを実装します。
ステップ8—ページネーションの実装
バックエンドを使用しているクライアントは、応答時間が長すぎる、またはURLエントリが多すぎると、応答時間が大きすぎると不満を言う可能性があります。 あなたのデータベースでさえ、膨大な情報のセットをまとめるのに苦労するかもしれません。 この問題を解決するために、 pagination と呼ばれる手法を使用して、クライアントが各リクエスト内で必要なアイテムの数を指定できるようにすることができます。
この機能を実装するデフォルトの方法はありません。 REST APIでも、名前と動作が異なるHTTPヘッダーまたはクエリパラメーターに表示される場合があります。
このアプリケーションでは、URLクエリに対してさらに2つの引数を有効にすることで、ページ付けを実装します。 first
と skip
. first
要素の最初の可変数を選択し、 skip
最初からスキップする要素の数を指定します。 たとえば、 first == 10
と skip == 5
最初の10個のURLを取得しますが、そのうち5個をスキップして、残りの5個だけを返します。
このソリューションの実装は、フィルターの追加に似ています。
を開きます shortener/schema.py
ファイル:
- vim shortener/schema.py
ファイルで、 Query
2つの新しいパラメータをに追加してクラスを作成します urls
変数と resolve_urls
次のコードで強調表示されているメソッド:
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
...
このコードは新しく作成されたものを使用します first
と skip
内部のパラメータ resolve_urls
データベースクエリをフィルタリングするメソッド。
ファイルを保存して閉じます。 ローカルサーバーを実行していない場合は、 python manage.py runserver
.
ページネーションをテストするには、次のGraphiQLインターフェイスで次のクエリを発行します。 http://localhost:8000/graphql
:
query {
urls(first: 2, skip: 1) {
id
fullUrl
urlHash
clicks
createdAt
}
}
URL短縮サービスは、データベースに作成された2番目のURLを返します。
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"
}
]
}
}
これは、ページネーション機能が機能していることを示しています。 URLを追加し、さまざまなセットをテストして、自由に試してみてください。 first
と skip
.
結論
GraphQLエコシステム全体が日々成長しており、その背後には活発なコミュニティがあります。 GitHubやFacebookなどの企業によって本番環境に対応していることが証明されており、このテクノロジーを独自のプロジェクトに適用できるようになりました。
このチュートリアルでは、クエリやミューテーションなどの概念を使用して、GraphQL、Python、Djangoを使用してURL短縮サービスを作成しました。 しかしそれ以上に、DjangoWebフレームワークを使用してWebアプリケーションを構築するためにこれらのテクノロジーに依存する方法を理解できました。
ここで使用されているGraphQLとツールの詳細については、GraphQLWebサイトおよびGrapheneドキュメントWebサイトを参照してください。 また、DigitalOceanには、PythonおよびDjangoの追加のチュートリアルがあり、どちらかについて詳しく知りたい場合に使用できます。