概述
让我们完成我们的mutation!
在本课中,我们将
- 了解 mutation 参数的常见约定和
input
类型 - 了解如何使用 mutation 响应来处理成功和失败的操作
Mutation 参数和输入
我们之前在 GraphQL 的 argument 中使用过 Query.playlist
field — 我们传递了一个名为 id
的 argument 。
type Query {playlist(id: ID!): Playlist}
对于 addItemsToPlaylist
mutation ,我们需要不止一个 argument 。
从文档中 ,我们需要以下参数:
playlist_id
- 播放列表的 ID,作为string
position
- 一个integer
,从零开始,表示我们想要插入曲目(s)的位置uris
- 一个以逗号分隔的string
,包含我们想要添加的曲目对应的uri 值
我们 可以 将所有三个都用作 argument ,但将 GraphQL input 类型 用作 argument 是最佳实践。
在 input
类型中, GraphQL schema 是一个特殊的 object type ,它将一组 argument 组合在一起,然后可以用作另一个 field 的参数。
作为命名约定,我们在类型名称后添加 Input
后缀,并为它赋予与它关联的 mutation 相同的名称。
在我们的例子中,我们可以将 argument 命名为 AddItemsToPlaylistInput
作为 field 。让我们开始创建它吧!
The AddItemsToPlaylistInput
类型
让我们创建一个名为 add_items_playlist_input.py
的新文件,放在 api/types
文件夹下,并定义一个名为 AddItemsToPlaylistInput
的类。
import strawberryclass AddItemsToPlaylistInput:...
我们还需要使用 GraphQL 的 input
类型来定义这个类,使用 @strawberry.input
装饰器。与我们迄今为止使用过的其他 Strawberry 函数类似,我们可以为这个函数提供 name
和 description
,在 GraphQL schema 中使用。
让我们将 @strawberry.input
装饰器应用到这个类上。
@strawberry.inputclass AddItemsToPlaylistInput:...
接下来,我们将添加属性。请记住,我们至少需要播放列表的 ID 和 URI 列表。我们 可以 也指定将这些项目添加到播放列表中的位置,但这对于 REST API 来说不是必需的。默认情况下,曲目将被追加到播放列表的末尾,因此我们可以从我们的 GraphQL schema 中省略它。请记住,你的 GraphQL API 不需要完全匹配你的 REST API!
playlist_id: strawberry.ID = strawberry.field(description="The ID of the playlist.")uris: list[str] = strawberry.field(description="A list of Spotify URIs to add.")
更新解析器
现在,让我们确保我们的 resolver 了解这个输入。回到 api/mutation.py
中,我们可以将其添加到函数的参数中。我们将这个 resolver 的 argument 命名为 input
。
注意, input
参数可以命名为 任何 东西,例如 playlist_tracks
或 tracks_input
!我们建议与你的团队协商确定命名约定。使用 input
作为 GraphQL 的 argument 名称是一个常见的约定。
def add_items_to_playlist(self,input: AddItemsToPlaylistInput) -> AddItemsToPlaylistPayload:...
既然我们已经在这里,让我们也使这个函数成为异步的,并添加 info
参数。我们稍后将需要它来访问 spotify_client
。
async def add_items_to_playlist(self,input: AddItemsToPlaylistInput,info: strawberry.Info) -> AddItemsToPlaylistPayload:...
现在,让我们使用我们的服务和输入类型!在 resolver 函数的主体中,我们将使用 add_tracks_to_playlist.asyncio
方法来调用我们的 mock_spotify_rest_api_client
包。
client = info.context["spotify_client"]await add_tracks_to_playlist.asyncio(playlist_id=input.playlist_id, uris=",".join(input.uris), client=client)
以方法签名为指南,我们可以从 input
中添加对应值到 playlist_id
和 uris
。对于最后一个参数,我们需要稍微调整一下格式。由于 input.uris
是一个 list 类型的 str
,而方法期望的是一个以逗号分隔的值的 str
类型,所以我们将使用 ",".join
。
这只是我们正在使用的 REST 端点的性质。要检索 playlist
对象,我们需要使用之前用过的 get_playlist
方法进行后续调用。
但是我们应该在哪里进行调用呢?如果将它包含在这个 resolver
中,那么即使 playlist
字段没有包含在 GraphQL
的 mutation
中,也意味着需要进行额外的 REST 调用!
我们之前已经遇到过这种情况,当时我们使用了 resolver
链。然而,在这种情况下,我们将保持 REST 调用包含在这个 resolver
中。从客户端应用程序的需求角度考虑,如果它们要向播放列表添加曲目,它们很 可能 会将播放列表及其曲目列表包含在 GraphQL
操作中!他们想要看到他们 结果 的 mutation
毕竟。
让我们继续。在 add_items_to_playlist
解析器中,我们将调用 get_playlist
函数。
data = await get_playlist.asyncio(playlist_id=input.playlist_id, client=client)playlist = Playlist(id=data.id, name=data.name, description=data.description)
看起来眼熟吧?这是我们在 Query.playlist
解析器中使用的代码。
最后,让我们更新 AddItemsToPlaylistPayload
实例中的返回值,具体来说是将硬编码的播放列表替换为 playlist
变量。
return AddItemsToPlaylistPayload(code=200,success=True,message="Successfully added items to playlist.",playlist=playlist,)
成功路径已经处理好了!现在如果发生错误怎么办?让我们将到目前为止的代码包装在 try
块和 except
任何被抛出的 Exception
中。
try:await add_tracks_to_playlist.asyncio(playlist_id=input.playlist_id, uris=",".join(input.uris), client=client)data = await get_playlist.asyncio(playlist_id=input.playlist_id, client=client)playlist = Playlist(id=data.id, name=data.name, description=data.description)return AddItemsToPlaylistPayload(code=200,success=True,message="Successfully added items to playlist.",playlist=playlist,)except Exception as e:...
如果出现错误,我们的返回值应该略有不同。我们仍然会返回 AddItemsToPlaylistPayload
类型,但这次代码将是 500
,成功状态将是 false
,并且我们将返回异常的任何消息。 playlist
参数将设置为 None
。
return AddItemsToPlaylistPayload(code=500,success=False,message=str(e),playlist=None,)
最后,不要忘记文件开头需要的所有导入
from .types.add_items_to_playlist_input import AddItemsToPlaylistInputfrom mock_spotify_rest_api_client.api.playlists import (add_tracks_to_playlist,get_playlist,)
探索时间!
在服务器运行最新的更改后,让我们开始一个新的工作区标签,从头开始构建我们的 mutation
。随着 input
参数的添加,当我们添加 addItemsToPlaylist
字段时, GraphQL
操作看起来有点不同:
mutation AddItemsToPlaylist($input: AddItemsToPlaylistInput!) {addItemsToPlaylist(input: $input) {}}
我们也可以看到 Variables 部分中有一个 input
属性在 JSON 对象中:
{"input": null}
让我们开始填写这个 input
对象。在 Documentation 面板中,点击 input
字段,并添加来自 AddItemsToPlaylistInput
的三个字段。Explorer 会自动更新你的 variables
。
{"input": {"playlistId": null,"uris": null}}
我们只需要更新这些 null
!我们将使用一个新的播放列表 ID,并在其中添加一个包含一个 URI 的数组。这些都是模拟数据,所以你可以在里面放任何你想要的作为占位符。它不需要实际存在于 Spotify 数据库中。
{"input": {"playlistId": "6LB6g7S5nc1uVVfj00Kh6Z","uris": ["ASongAboutGraphQL"]}}
最后,让我们填写 mutation
的其余部分,包括我们需要的字段。添加 code
、success
和 message
字段。然后对于播放列表,添加其详细信息 以及 其曲目!
mutation AddItemsToPlaylist($input: AddItemsToPlaylistInput!) {addItemsToPlaylist(input: $input) {codemessagesuccessplaylist {idnametracks {idname}}}}
这是一个巨大的 mutation
,按下播放按钮运行!我们应该看到我们的数据成功返回。在 tracks
列表的底部,我们将看到 id
设置为我们传递给 uri
变量,以及名称。
注意:你的响应看起来有点不同吗?因为 REST API 在所有 Odyssey
学习者之间共享,你可能会在你的响应中看到更多或更少的曲目。这很可能是因为 其他 学习者向播放列表添加了自己的曲目。我们也会定期重置数据以保持数据的干净。
太棒了!现在看看如果你将播放列表 ID 更新为一个不存在的值会发生什么。
{"input": {"playlistId": "DoesNotExist","uris": ["ASongAboutGraphQL"]}}
当我们再次运行 mutation
时,我们仍然会收到数据,但这次我们得到了一个错误代码、一条失败消息和一个 null
播放列表。
我们的 mutation 正在工作! 🎉
练习
input
类型?关键要点
- 在
GraphQL
中,Mutations
通常需要多个参数来执行操作。为了将参数分组在一起,我们使用GraphQL
input 类型来提高清晰度和可维护性。 - Strawberry 使用
@strawberry.input
将 Python 类标记为GraphQL
input 类型。 - 我们可以像访问任何其他
GraphQL
参数一样,在解析器函数中访问input
参数。
结论
太棒了,你做到了,你构建了一个 GraphQL API!你现在拥有一个可用的 GraphQL 服务器,它塞满了使用 Spotify REST API 作为 数据源 的播放列表和曲目。你已经编写了查询和 变异,并且在此过程中学习了一些常见的 GraphQL 约定。你已经探索了如何在你的架构设计中使用 GraphQL 参数、变量 和输入类型。花点时间庆祝一下;这可是学到了很多东西啊!
如果你有任何关于你想看到下一步内容的请求,请在下方告诉我们!
分享你对本节课的疑问和评论
本课程目前处于
你需要一个 GitHub 帐户才能在下方发布。没有帐户? 在我们的 Odyssey 论坛中发布。