_ 作者选择了 Girls Who 代码以作为 Write for Donations计划的一部分接收捐款。
介绍
[GraphQL] (https://graphql.org/)是Facebook作为REST的替代品而创建和开源的API标准. APIs (英语). 相对于REST API,GraphQL使用一个打入的系统来定义其数据结构,所有发送和接收的信息都必须符合预先定义的策略. 也暴露出所有通信的单一端点,而不是针对不同资源的多个URL,并且通过只返回客户端要求的数据来解决了"过度"问题,从而产生更小更简洁的响应.
在本教程中,您将创建一个 URL shortener 的后端 - 一个服务,可以使用任何 URL 并生成更短,更易于阅读的版本 - 同时潜入 GraphQL 概念,如 查询和突变,以及工具,如 GraphiQL 接口.您可能已经使用过此类服务,如 bit.ly
。
自图表以来 QL是一种语言不可知论技术,它被执行在各种语言和框架之上. 在此,您将使用通用 [Python 编程语言] (https://www.digitalocean.com/community/tutorial_series/how-to-code-in-python-3), [Django web 框架] (https://andsky.com/tags/django?type=tutorials),以及 [Graphene-Django] (https://docs.graphene-python.org/projects/django/en/latest/) 库作为 GraphQL Python 执行,并为 Django 提供特定的集成.
前提条件
- 联合国 要继续这个教程,你需要Python版本的3.5或更高安装在你的开发机器上. 要安装 Python, 请遵从我们的教程 : [如何安装和设置 Python 3 的本地编程环境] (https://www.digitalocean.com/community/tutorial_series/how-to-install-and-set-up-a-local-programming-environment-for-python-3) 。 确保同时创建和启动 [虚拟环境] (https://andsky.com/tech/tutorials/how-to-install-python-3-and-set-up-a-local-programming-environment-on-ubuntu-18-04# step-2-%E2%80%94-setting-up-a-virtual-environment); 遵循此教程的引导, 您可以将您的项目目录命名为 `短' 。
- 联合国 需要获得关于Django的初级知识,但不是强制性的。 如果你好奇的话,你可以跟随由DigitalOcean社区创建的这个Django Development series. .
第1步:创建Django项目
在此步骤中,您将安装应用程序所需的所有工具,并设置您的Django项目。
一旦您创建了项目目录并启动了虚拟环境,如前提所述,请使用Python包管理器pip
安装所需的包,本教程将安装Django版本 2.1.7和Graphene-Django版本 2.2.0或更高版本:
1pip install "django==2.1.7" "graphene-django>==2.2.0"
接下来,您将使用django-admin
命令创建一个 Django 项目. 一个项目是默认的 Django 锅炉板 – 一组文件夹和文件,包含开始开发 Web 应用程序所需的一切。
1django-admin startproject shorty .
在创建您的项目后, 您将运行 [Django 迁移 ([ LINK0] ) 。 这些文件包含由Django生成的Python代码,并负责按照Django模型来改变应用程序的结构. 例如,修改可能包括创建表格。 默认情况下,Django会带着自己负责像Django Authority这样的子系统的一套迁移程序来进行,所以需要用以下命令执行它们:
1python manage.py migrate
这个命令使用Python解释器调用名为manage.py
的Django脚本,负责管理项目的不同方面,例如创建应用程序或运行迁移。
这将产生类似于以下的产量:
1[secondary_label Output]
2Operations to perform:
3 Apply all migrations: admin, auth, contenttypes, sessions
4Running migrations:
5 Applying contenttypes.0001_initial... OK
6 Applying auth.0001_initial... OK
7 Applying admin.0001_initial... OK
8 Applying admin.0002_logentry_remove_auto_add... OK
9 Applying admin.0003_logentry_add_action_flag_choices... OK
10 Applying contenttypes.0002_remove_content_type_name... OK
11 Applying auth.0002_alter_permission_name_max_length... OK
12 Applying auth.0003_alter_user_email_max_length... OK
13 Applying auth.0004_alter_user_username_opts... OK
14 Applying auth.0005_alter_user_last_login_null... OK
15 Applying auth.0006_require_contenttypes_0002... OK
16 Applying auth.0007_alter_validators_add_error_messages... OK
17 Applying auth.0008_alter_user_username_max_length... OK
18 Applying auth.0009_alter_user_last_name_max_length... OK
19 Applying sessions.0001_initial... OK
一旦Django的数据库准备好运行,请启动其本地开发服务器:
1python manage.py runserver
这将给出:
1[secondary_label Output]
2Performing system checks...
3
4System check identified no issues (0 silenced).
5March 18, 2020 - 15:46:15
6Django version 2.1.7, using settings 'shorty.settings'
7Starting development server at http://127.0.0.1:8000/
8Quit the server with CONTROL-C.
此命令将删除您的终端中的提示,并启动服务器。
请访问本地浏览器中的http://127.0.0.1:8000
页面,您将看到此页面:
要停止服务器并返回终端,请按CTRL+C
。每当您需要访问浏览器时,请确保之前的命令正在运行。
接下来,您将通过在项目中启用 Django-Graphene 库来完成此步骤。 Django 具有):
1vim shorty/settings.py
「settings.py」文件管理您项目中的所有设置,在其内部搜索「INSTALLED_APPS」条目,并添加「graphene_django」条目:
1[label shorty/shorty/settings.py]
2...
3INSTALLED_APPS = [
4 'django.contrib.admin',
5 'django.contrib.auth',
6 'django.contrib.contenttypes',
7 'django.contrib.sessions',
8 'django.contrib.messages',
9 'django.contrib.staticfiles',
10 'graphene_django',
11]
12...
此插件告知 Django 您将使用名为graphene_django
的应用程序,您在步骤 1 中安装了该应用程序。
在文件的底部,添加以下变量:
1[label shorty/shorty/settings.py]
2...
3GRAPHENE = {
4 'SCHEMA': 'shorty.schema.schema',
5}
在 GraphQL 中,一个 Schema 包含所有对象类型,如资源、查询和突变。
更改后,保存并关闭文件。
现在你已经配置了Django项目,下一步,你将创建一个Django应用程序及其模型。
步骤 2 — 设置 Django 应用程序和模型
Django 平台通常由一个项目和多个应用程序或 apps 组成,一个应用程序描述了项目内部的一组功能,如果设计得很好,可以在 Django 项目中重复使用。
在此步骤中,您将创建一个名为shortener
的应用程序,负责实际的URL缩短功能。
1python manage.py startapp shortener
在这里,您使用了startapp app_name
的参数,指示manage.py
创建一个名为shortener
的应用程序。
要完成应用创建,打开shorty/settings.py
文件
1vim shorty/settings.py
将应用的名称添加到您之前修改的相同的INSTALLED_APPS
条目:
1[label shorty/shorty/settings.py]
2...
3INSTALLED_APPS = [
4 'django.contrib.admin',
5 'django.contrib.auth',
6 'django.contrib.contenttypes',
7 'django.contrib.sessions',
8 'django.contrib.messages',
9 'django.contrib.staticfiles',
10 'graphene_django'
11 'shortener',
12]
13...
保存并关闭文件。
随着你的)是Django的关键功能之一。它们被用来以Pythonic
的方式表示数据库,允许你使用Python代码管理,查询和存储数据。
在打开models.py
文件以获取更改之前,本教程将概述您将进行的更改。
您的模型文件 - shortener/models.py
- 在您更换现有代码后将包含以下内容:
1[label shorty/shortener/models.py]
2from hashlib import md5
3
4from django.db import models
在这里,您将导入所需的代码所需的包,您将添加顶部的行 from hashlib import md5
以导入 Python 标准库,用于创建 URL 的 hash。
<$>[警告] 警告: 本教程指的是 hash 作为一个函数的结果,它需要输入并总是返回相同的输出。
请注意,MD5有碰撞问题(https://en.wikipedia.org/wiki/MD5# Collision_vulnerabilities)并应避免在生产中。
接下来,您将添加一个名为URL
的模型,其中包含以下字段:
full_url
:要缩短的 URL. *url_hash
:代表完整 URL 的短哈希. *click
:短 URL 被访问了多少次. *created_at
: URL 创建的日期和时间。
1[label shorty/shortener/models.py]
2...
3
4class URL(models.Model):
5 full_url = models.URLField(unique=True)
6 url_hash = models.URLField(unique=True)
7 clicks = models.IntegerField(default=0)
8 created_at = models.DateTimeField(auto_now_add=True)
您将通过将 MD5 哈希算法应用到)`方法中返回的第 10 个字符,每次 Django 将条目保存到数据库时都会执行。
所提到的操作将被添加到您的URL
模型中,使用此代码:
1[label shorty/shortener/models.py]
2...
3
4 def clicked(self):
5 self.clicks += 1
6 self.save()
7
8 def save(self, *args, **kwargs):
9 if not self.id:
10 self.url_hash = md5(self.full_url.encode()).hexdigest()[:10]
11
12 return super().save(*args, **kwargs)
现在你已经检查了代码,打开‘shortener/models.py’文件:
1vim shortener/models.py
代替代码为以下内容:
1[label shorty/shortener/models.py]
2from hashlib import md5
3
4from django.db import models
5
6class URL(models.Model):
7 full_url = models.URLField(unique=True)
8 url_hash = models.URLField(unique=True)
9 clicks = models.IntegerField(default=0)
10 created_at = models.DateTimeField(auto_now_add=True)
11
12 def clicked(self):
13 self.clicks += 1
14 self.save()
15
16 def save(self, *args, **kwargs):
17 if not self.id:
18 self.url_hash = md5(self.full_url.encode()).hexdigest()[:10]
19
20 return super().save(*args, **kwargs)
请确保保存并关闭文件。
要在数据库中应用这些更改,您需要通过运行以下命令创建迁移:
1python manage.py makemigrations
这将为您带来以下结果:
1[secondary_label Output]
2Migrations for 'shortener':
3 shortener/migrations/0001_initial.py
4 - Create model URL
然后执行迁移:
1python manage.py migrate
您将在您的终端中看到以下输出:
1[secondary_label Output]
2Operations to perform:
3 Apply all migrations: admin, auth, contenttypes, sessions, shortener
4Running migrations:
5 Applying shortener.0001_initial... OK
现在您已经设置了模型,下一步将创建 GraphQL 终端和查询。
第3步:创建查询
REST 架构在不同的终端中呈现不同的资源,每个终端都包含一个明确的数据结构,例如,您可以从 /api/users
获取用户列表,始终期待相同的字段。
首先,创建一个查询以获取所有URL,你需要几个东西:
- URL 类型,链接到您先前定义的模型。 * 命名为
urls
的查询声明. * 解决您的查询的方法,即从数据库中获取所有 URL 并将其返回客户端。
创建一个名为shortener/schema.py
的新文件:
1vim shortener/schema.py
首先,添加Python的导入
语句:
1[label shorty/shortener/schema.py]
2import graphene
3from graphene_django import DjangoObjectType
4
5from .models import URL
第一行导入主要的图形
库,其中包含基本的 GraphQL 类型,如列表
。DjangoObjectType
是从任何 Django 模型创建一个 Schema 定义的助手,第三行导入您之前创建的URL
模型。
然后,通过添加以下行来为URL
模型创建一个新的 GraphQL 类型:
1[label shorty/shortener/schema.py]
2...
3class URLType(DjangoObjectType):
4 class Meta:
5 model = URL
最后,添加这些行来为URL
模型创建查询类型:
1[label shorty/shortener/schema.py]
2...
3class Query(graphene.ObjectType):
4 urls = graphene.List(URLType)
5
6 def resolve_urls(self, info, **kwargs):
7 return URL.objects.all()
此代码创建一个Query
类,其中有一个名为urls
的字段,该字段是以前定义的URLType
的列表。
完整的shortener/schema.py
文件在这里显示:
1[label shorty/shortener/schema.py]
2import graphene
3from graphene_django import DjangoObjectType
4
5from .models import URL
6
7class URLType(DjangoObjectType):
8 class Meta:
9 model = URL
10
11class Query(graphene.ObjectType):
12 urls = graphene.List(URLType)
13
14 def resolve_urls(self, info, **kwargs):
15 return URL.objects.all()
保存并关闭文件。
现在必须将所有查询添加到主方案中,将其视为所有资源的持有人。
在shorty/schema.py
路径中创建一个新的文件,并与编辑器一起打开它:
1vim shorty/schema
通过添加以下行来导入下列Python包. 如前所述,第一行包含基本的GraphQL类型. 第二行导入先前创建的Schema文件。
1[label shorty/shorty/schema.py]
2import graphene
3
4import shortener.schema
接下来,添加主Query
类,通过继承,它将持有所有创建的查询和未来的操作:
1[label shorty/shorty/schema.py]
2...
3class Query(shortener.schema.Query, graphene.ObjectType):
4 pass
最后,创建方案
变量:
1[label shorty/shorty/schema.py]
2...
3schema = graphene.Schema(query=Query)
您在步骤 2 中定义的SCHEMA
设置指向您刚刚创建的schema
变量。
完整的 shorty/schema.py
文件在这里显示:
1[label shorty/shorty/schema.py]
2import graphene
3
4import shortener.schema
5
6class Query(shortener.schema.Query, graphene.ObjectType):
7 pass
8
9schema = graphene.Schema(query=Query)
保存并关闭文件。
接下来,启用 GraphQL 端点和 GraphiQL接口,这是一个用于与 GraphQL 系统交互的图形 Web 接口。
打开shorty/urls.py
文件:
1vim shorty/urls.py
为了学习目的,删除文件内容并将其保存,以便您可以从头开始。
您将添加的第一行是Python导入陈述:
1[label shorty/shorty/urls.py]
2from django.urls import path
3from django.views.decorators.csrf import csrf_exempt
4
5from graphene_django.views import GraphQLView
Django 使用 path
函数为 GraphiQL 接口创建一个可访问的 URL。随后,您将导入 csrf_exempt
,允许客户端将数据发送到服务器。 在 [Graphene Documentation] 中可以找到完整的解释(https://docs.graphene-python.org/projects/django/en/latest/installation/# csrf-exempt)。 在最后一行中,您通过 `GraphQLView’导入了接口的实际代码。
然后创建一个名为urlpatterns
的变量。
1[label shorty/shorty/urls.py]
2...
3urlpatterns = [
4 path('graphql/', csrf_exempt(GraphQLView.as_view(graphiql=True))),
5]
这将汇集所有必要的代码,使 GraphiQL 接口可用在 graphql/
路径中:
完整的shortener/urls.py
文件在这里显示:
1[label shorty/shorty/urls.py]
2from django.urls import path
3from django.views.decorators.csrf import csrf_exempt
4
5from graphene_django.views import GraphQLView
6
7urlpatterns = [
8 path('graphql/', csrf_exempt(GraphQLView.as_view(graphiql=True))),
9]
保存文件并关闭它。
回到终端,运行):
1python manage.py runserver
打开您的网页浏览器在 http://localhost:8000/graphql
地址. 您将被介绍这个屏幕:
GraphiQL 是一个界面,您可以运行 GraphQL 陈述并查看结果,其中一个功能是右上方的Docs
部分,因为 GraphQL 中的所有内容都被键入,因此您可以获得有关所有类型、查询、突变等的免费文档。
浏览页面后,将您的第一个查询插入主文本区域:
1query {
2 urls {
3 id
4 fullUrl
5 urlHash
6 clicks
7 createdAt
8 }
9}
此内容显示了 GraphQL 查询的结构:首先,您使用关键字查询
告诉服务器,您只需要一些数据返回。接下来,您使用在Query
类内的shortener/schema.py
文件中定义的urls
字段。
现在,点击左上角的 播放箭头按钮 。
您将收到以下回复,表示您仍然没有URL:
1[secondary_label Output]
2{
3 "data": {
4 "urls": []
5 }
6}
这表明 GraphQL 正在工作. 在您的终端中,按CTRL+C
来停止您的服务器。
在此步骤中,您已经完成了很多工作,创建了 GraphQL 终端,创建了一个查询以获取所有 URL,并启用了 GraphiQL 接口。
第4步:创建突变
大多数应用程序可以通过添加、更新或删除数据来更改数据库状态. 在 GraphQL 中,这些操作称为 Mutations。
要创建你的第一个突变,打开‘shortener/schema.py’:
1vim shortener/schema.py
在文件的末尾,开始添加一个名为CreateURL
的新类:
1[label shorty/shortener/schema.py]
2...
3class CreateURL(graphene.Mutation):
4 url = graphene.Field(URLType)
该类继承了graphene.Mutation
辅助器,以具有 GraphQL 突变的能力。它还具有属性名称url
,定义了转换完成后服务器返回的内容。
接下来,将名为论点
的子类添加到已经定义的类:
1[label shorty/shortener/schema.py]
2...
3 class Arguments:
4 full_url = graphene.String()
这里,你期待一个名为full_url
的参数,具有String
的内容:
现在添加以下行来创建变异
方法:
1[label shorty/shortener/schema.py]
2...
3
4 def mutate(self, info, full_url):
5 url = URL(full_url=full_url)
6 url.save()
这种变异
方法通过从客户端接收数据并将其保存到数据库中来完成很多工作,最终返回包含新创建项目的类本身。
最后,通过添加这些行创建一个突变
类,以保持您的应用程序的所有突变:
1[label shorty/shortener/schema.py]
2...
3
4class Mutation(graphene.ObjectType):
5 create_url = CreateURL.Field()
到目前为止,你只有一种突变叫做create_url
。
完整的shortener/schema.py
文件在这里显示:
1[label shorty/shortener/schema.py]
2import graphene
3from graphene_django import DjangoObjectType
4
5from .models import URL
6
7class URLType(DjangoObjectType):
8 class Meta:
9 model = URL
10
11class Query(graphene.ObjectType):
12 urls = graphene.List(URLType)
13
14 def resolve_urls(self, info, **kwargs):
15 return URL.objects.all()
16
17class CreateURL(graphene.Mutation):
18 url = graphene.Field(URLType)
19
20 class Arguments:
21 full_url = graphene.String()
22
23 def mutate(self, info, full_url):
24 url = URL(full_url=full_url)
25 url.save()
26
27 return CreateURL(url=url)
28
29class Mutation(graphene.ObjectType):
30 create_url = CreateURL.Field()
关闭并保存文件。
要完成添加突变,请更改 shorty/schema.py
文件:
1vim shorty/schema.py
更改文件以包含以下突出代码:
1[label shorty/shorty/schema.py]
2
3import graphene
4
5import shortener.schema
6
7class Query(shortener.schema.Query, graphene.ObjectType):
8 pass
9
10class Mutation(shortener.schema.Mutation, graphene.ObjectType):
11 pass
12
13schema = graphene.Schema(query=Query, mutation=Mutation)
保存并关闭文件. 如果您没有运行本地服务器,请启动它:
1python manage.py runserver
在您的 Web 浏览器中,导航到 http://localhost:8000/graphql
. 在 GraphiQL Web 界面中执行您的第一个突变,运行以下语句:
1mutation {
2 createUrl(fullUrl:"https://www.digitalocean.com/community") {
3 url {
4 id
5 fullUrl
6 urlHash
7 clicks
8 createdAt
9 }
10 }
11}
您用createURL
名称、fullUrl
参数和您在url
字段内定义的响应中想要的数据组成了突变。
输出将包含您刚刚在 GraphQL数据
字段中创建的 URL 信息,如下所示:
1[secondary_label Output]
2{
3 "data": {
4 "createUrl": {
5 "url": {
6 "id": "1",
7 "fullUrl": "https://www.digitalocean.com/community",
8 "urlHash": "077880af78",
9 "clicks": 0,
10 "createdAt": "2020-01-30T19:15:10.820062+00:00"
11 }
12 }
13 }
14}
随后,一个 URL 被添加到数据库中,其 hashed 版本,正如您可以在urlHash
字段中看到的那样。
1query {
2 urls {
3 id
4 fullUrl
5 urlHash
6 clicks
7 createdAt
8 }
9}
输出将显示存储的 URL:
1[secondary_label Output]
2{
3 "data": {
4 "urls": [
5 {
6 "id": "1",
7 "fullUrl": "https://www.digitalocean.com/community",
8 "urlHash": "077880af78",
9 "clicks": 0,
10 "createdAt": "2020-03-18T21:03:24.664934+00:00"
11 }
12 ]
13 }
14}
您也可以尝试执行相同的查询,但只要求您想要的字段。
接下来,再试一次用不同的URL:
1mutation {
2 createUrl(fullUrl:"https://www.digitalocean.com/write-for-donations/") {
3 url {
4 id
5 fullUrl
6 urlHash
7 clicks
8 createdAt
9 }
10 }
11}
产量将是:
1[secondary_label Output]
2{
3 "data": {
4 "createUrl": {
5 "url": {
6 "id": "2",
7 "fullUrl": "https://www.digitalocean.com/write-for-donations/",
8 "urlHash": "703562669b",
9 "clicks": 0,
10 "createdAt": "2020-01-30T19:31:10.820062+00:00"
11 }
12 }
13 }
14}
系统现在可以创建短的URL并列出它们,在下一步,您将允许用户通过其短版本访问URL,将其重定向到正确的页面。
步骤5 - 创建访问终端
在此步骤中,您将使用 Django 观点 - 采取请求并返回响应的方法 - 将访问 http://localhost:8000/url_hash
终端的任何人重定向到其完整的 URL。
打开shortener/views.py
文件与您的编辑器:
1vim shortener/views.py
首先,通过用以下行替换内容来导入两个包:
1[label shorty/shortener/views.py]
2from django.shortcuts import get_object_or_404, redirect
3
4from .models import URL
这些将在稍后更详细地解释。
接下来,您将创建一个名为root
的Django View。添加这个代码片段,负责您的文件末尾的View:
1[label shorty/shortener/views.py]
2...
3
4def root(request, url_hash):
5 url = get_object_or_404(URL, url_hash=url_hash)
6 url.clicked()
7
8 return redirect(url.full_url)
在函数内部,第一行试图使用url_hash
参数从数据库中获取URL。如果没有找到,它会将HTTP 404错误返回客户端,这意味着资源丢失了。之后,它会增加URL输入的点击
属性,确保跟踪该URL的访问次数。
完整的shortener/views.py
文件在这里显示:
1[label shorty/shortener/views.py]
2from django.shortcuts import get_object_or_404, redirect
3
4from .models import URL
5
6def root(request, url_hash):
7 url = get_object_or_404(URL, url_hash=url_hash)
8 url.clicked()
9
10 return redirect(url.full_url)
保存并关闭文件。
接下来,打开shorty/urls.py
:
1vim shorty/urls.py
添加以下突出代码以启用根
视图。
1[label shorty/shorty/urls.py]
2
3from django.urls import path
4from django.views.decorators.csrf import csrf_exempt
5
6from graphene_django.views import GraphQLView
7
8from shortener.views import root
9
10urlpatterns = [
11 path('graphql/', csrf_exempt(GraphQLView.as_view(graphiql=True))),
12 path('<str:url_hash>/', root, name='root'),
13]
root
视图将可在您的服务器的/
路径中访问,接受一个url_hash
作为字符串参数。
如果您没有运行本地服务器,请通过执行python manage.py runserver
命令启动该命令。
要测试你的新添加,打开你的网页浏览器,并访问http://localhost:8000/077880af78
URL. 请注意,URL的最后一部分是由突变从步骤5创建的哈希,你将被重定向到哈希的URL页面,在这种情况下,DigitalOcean社区网站。
现在你有 URL 重定向工作,你会通过实施错误处理执行突变来使应用程序更安全。
第6步:实施错误处理
处理错误是所有应用程序的最佳做法,因为开发人员通常不控制会发送到服务器的内容。在这种情况下,您可以尝试预测错误并最大限度地减少其影响。
作为一个编写的系统,GraphQL可以在一个叫做 Schema Validation 的操作中验证客户端要求和接收的一切。
在您的浏览器中再次导航到http://localhost:8000/graphql
,并在 GraphiQL 界面内执行下一个查询,使用iDontExist
字段:
1query {
2 urls {
3 id
4 fullUrl
5 urlHash
6 clicks
7 createdAt
8 iDontExist
9 }
10}
由于查询中没有定义的iDontExist
字段,因此 GraphQL 会返回一个错误消息:
1[secondary_label Output]
2
3{
4 "errors": [
5 {
6 "message": "Cannot query field \"iDontExist\" on type \"URLType\".",
7 "locations": [
8 {
9 "line": 8,
10 "column": 5
11 }
12 ]
13 }
14 ]
15}
这很重要,因为在GraphQL打字系统中,目标是发送和接收已经在方案中定义的信息。
目前的应用程序接受任何full_url
字段中的任意字符串。问题是,如果有人发送了一个构建不良的URL,你会在尝试存储的信息时将用户重定向到无处。
首先,打开shortener/models.py
文件:
1vim shortener/models.py
在导入部分中添加突出的行:
1[label shorty/shortener/models.py]
2from hashlib import md5
3
4from django.db import models
5from django.core.validators import URLValidator
6from django.core.exceptions import ValidationError
7
8from graphql import GraphQLError
9...
URLValidator
是一个 Django 辅助程序来验证 URL 字符串,而GraphQLError
则被 Graphene 用来用自定义消息提到例外。
接下来,请确保在将其保存到数据库之前验证用户接收的 URL。 通过在shortener/models.py
文件中添加突出的代码来启用此操作:
1[label shorty/shortener/models.py]
2class URL(models.Model):
3 full_url = models.URLField(unique=True)
4 url_hash = models.URLField(unique=True)
5 clicks = models.IntegerField(default=0)
6 created_at = models.DateTimeField(auto_now_add=True)
7
8 def clicked(self):
9 self.clicks += 1
10 self.save()
11
12 def save(self, *args, **kwargs):
13 if not self.id:
14 self.url_hash = md5(self.full_url.encode()).hexdigest()[:10]
15
16 validate = URLValidator()
17 try:
18 validate(self.full_url)
19 except ValidationError as e:
20 raise GraphQLError('invalid url')
21
22 return super().save(*args, **kwargs)
首先,此代码在)收到的 URL,并在
错误 URL的自定义消息中提出
GraphQLError`。
完整的 shortener/models.py
文件在这里显示:
1[label shorty/shortener/models.py]
2from hashlib import md5
3
4from django.db import models
5from django.core.validators import URLValidator
6from django.core.exceptions import ValidationError
7
8from graphql import GraphQLError
9
10class URL(models.Model):
11 full_url = models.URLField(unique=True)
12 url_hash = models.URLField(unique=True)
13 clicks = models.IntegerField(default=0)
14 created_at = models.DateTimeField(auto_now_add=True)
15
16 def clicked(self):
17 self.clicks += 1
18 self.save()
19
20 def save(self, *args, **kwargs):
21 if not self.id:
22 self.url_hash = md5(self.full_url.encode()).hexdigest()[:10]
23
24 validate = URLValidator()
25 try:
26 validate(self.full_url)
27 except ValidationError as e:
28 raise GraphQLError('invalid url')
29
30 return super().save(*args, **kwargs)
如果您没有运行本地服务器,请使用python manage.py runserver
命令启动它。
接下来,测试你的新错误处理在http://localhost:8000/graphql. 尝试在 GraphiQL 界面中创建一个新的 URL 与无效的
full_url`:
1mutation {
2 createUrl(fullUrl:"not_valid_url"){
3 url {
4 id
5 fullUrl
6 urlHash
7 clicks
8 createdAt
9 }
10 }
11}
当发送无效的 URL 时,您的例外将被提到自定义消息:
1[secondary_label Output]
2
3{
4 "errors": [
5 {
6 "message": "invalid url",
7 "locations": [
8 {
9 "line": 2,
10 "column": 3
11 }
12 ],
13 "path": [
14 "createUrl"
15 ]
16 }
17 ],
18 "data": {
19 "createUrl": null
20 }
21}
如果您在您的终端中查看python manage.py runserver
命令正在运行,则会出现一个错误:
1[secondary_label Output]
2
3...
4graphql.error.located_error.GraphQLLocatedError: invalid url
5
6[30/Jan/2020 19:46:32] "POST /graphql/ HTTP/1.1" 200 121
GraphQL 终端总是会与 HTTP 200 状态代码失败,这通常意味着成功. 请记住,尽管 GraphQL 是基于 HTTP 的,但它并不像 REST 那样使用 HTTP 状态代码或 HTTP 方法的概念。
随着错误处理的实施,您现在可以设置一个机制来过滤您的查询,尽量减少服务器返回的信息。
第7步:使用过滤器
假设你已经开始使用URL缩写器添加自己的链接,过了一段时间,会有那么多的条目,以至于找到合适的条目将变得困难。
过滤是REST API中常见的概念,通常在URL中附有一个带有字段和值的 _Query Parameter。
在 GraphQL 中,您将使用 Query Arguments 作为过滤器,它们创建了一个漂亮而干净的界面。
您可以通过允许客户端使用full_url
字段来按名称过滤 URL 来解决很难找到 URL
问题,然后在您最喜欢的编辑器中打开shortener/schema.py
文件。
1vim shortener/schema.py
首先,在突出的行中导入Q
方法:
1[label shorty/shortener/schema.py]
2import graphene
3from graphene_django import DjangoObjectType
4from django.db.models import Q
5
6from .models import URL
7...
这将用于过滤您的数据库查询。
接下来,重写整个Query
类,包含以下内容:
1[label shorty/shortener/schema.py]
2...
3class Query(graphene.ObjectType):
4 urls = graphene.List(URLType, url=graphene.String())
5
6 def resolve_urls(self, info, url=None, **kwargs):
7 queryset = URL.objects.all()
8
9 if url:
10 _filter = Q(full_url__icontains=url)
11 queryset = queryset.filter(_filter)
12
13 return queryset
14...
你正在做的修改是:
- 将)`方法。
完整的shortener/schema.py
文件在这里显示:
1[label shorty/shortener/schema.py]
2import graphene
3from graphene_django import DjangoObjectType
4from django.db.models import Q
5
6from .models import URL
7
8class URLType(DjangoObjectType):
9 class Meta:
10 model = URL
11
12class Query(graphene.ObjectType):
13 urls = graphene.List(URLType, url=graphene.String())
14
15 def resolve_urls(self, info, url=None, **kwargs):
16 queryset = URL.objects.all()
17
18 if url:
19 _filter = Q(full_url__icontains=url)
20 queryset = queryset.filter(_filter)
21
22 return queryset
23
24class CreateURL(graphene.Mutation):
25 url = graphene.Field(URLType)
26
27 class Arguments:
28 full_url = graphene.String()
29
30 def mutate(self, info, full_url)
31 url = URL(full_url=full_url)
32 url.save()
33
34 return CreateURL(url=url)
35
36class Mutation(graphene.ObjectType):
37 create_url = CreateURL.Field()
如果您没有运行本地服务器,请使用python manage.py runserver
启动。
在 http://localhost:8000/graphql. 在 GraphiQL 接口中,写下列声明,它将用 community 来过滤所有 URL:
1query {
2 urls(url:"community") {
3 id
4 fullUrl
5 urlHash
6 clicks
7 createdAt
8 }
9}
输出只是一个输入,因为你刚刚添加了一个URL,其中包含社区
字符串. 如果你以前添加了更多的URL,你的输出可能会有所不同。
1[secondary_label Output]
2
3{
4 "data": {
5 "urls": [
6 {
7 "id": "1",
8 "fullUrl": "https://www.digitalocean.com/community",
9 "urlHash": "077880af78",
10 "clicks": 1,
11 "createdAt": "2020-01-30T19:27:36.243900+00:00"
12 }
13 ]
14 }
15}
但是,如果链接太多,您的客户可能会抱怨URL列表返回的数据比他们的应用程序能够处理的更多。
步骤8 - 实施页面化
使用您的后端的客户可能会抱怨响应时间过长,或者如果有太多 URL 条目,其大小太大,即使您的数据库也可能难以组合大量信息,以解决此问题,您可以允许客户端使用称为 pagination 的技术在每个请求中指定想要的项目数量。
即使在 REST API 中,您可能会在 HTTP 标题或查询参数中看到它,具有不同的名称和行为。
在此应用程序中,您将通过向查询 URL 启用两个额外的参数来实现页面化:‘first’和‘skip’。‘first’将选择第一个可变元素数目,‘skip’将指定从开始跳过的元素数目。
实施此解决方案类似于添加过滤器。
打开shortener/schema.py
文件:
1vim shortener/schema.py
在文件中,通过将两个新的参数添加到urls
变量和resolve_urls
方法中,更改Query
类别,如下代码所示:
1[label shorty/shortener/schema.py]
2import graphene
3from graphene_django import DjangoObjectType
4from django.db.models import Q
5
6from .models import URL
7
8class Query(graphene.ObjectType):
9 urls = graphene.List(URLType, url=graphene.String(), first=graphene.Int(), skip=graphene.Int())
10
11 def resolve_urls(self, info, url=None, first=None, skip=None, **kwargs):
12 queryset = URL.objects.all()
13
14 if url:
15 _filter = Q(full_url__icontains=url)
16 queryset = queryset.filter(_filter)
17
18 if first:
19 queryset = queryset[:first]
20
21 if skip:
22 queryset = queryset[skip:]
23
24 return queryset
25...
此代码使用resolve_urls
方法中新创建的first
和skip
参数来过滤数据库查询。
如果您没有运行本地服务器,请使用python manage.py runserver
启动。
要测试页面化,请在 http://localhost:8000/graphql
的 GraphiQL 界面中发出以下查询:
1query {
2 urls(first: 2, skip: 1) {
3 id
4 fullUrl
5 urlHash
6 clicks
7 createdAt
8 }
9}
您的 URL 缩短器将返回您数据库中创建的第二个 URL:
1[secondary_label Output]
2
3{
4 "data": {
5 "urls": [
6 {
7 "id": "2",
8 "fullUrl": "https://www.digitalocean.com/write-for-donations/",
9 "urlHash": "703562669b",
10 "clicks": 0,
11 "createdAt": "2020-01-30T19:31:10.820062+00:00"
12 }
13 ]
14 }
15}
这表明页面功能是有效的. 通过添加更多的 URL 和测试不同的第一
和跳跃
功能,您可以自由玩。
结论
整个GraphQL生态系统每天都在增长,背后有一个活跃的社区,它已经被GitHub和Facebook等公司证明了,并且现在您可以将该技术应用到自己的项目中。
在本教程中,您使用 GraphQL、Python 和 Django 创建了一个 URL 缩短服务,使用了查询和突变等概念。
您可以探索更多关于 GraphQL 和这里使用的工具在 GraphQL 网站 和 Graphene 文档 网站. 此外,DigitalOcean 有额外的教程为 Python 和 Django,如果你想了解更多有关。