概述
我们还缺少一些东西:播放列表的曲目 - 我们实际上想听的歌曲!
在本课中,我们将
- 创建
Track类型 - 编写解析器 函数用于播放列表的曲目
The Track 对象
花几分钟时间研究上面的模型并开始思考我们需要哪些数据,以及它们可能是什么类型。为了简化起见,我们现在将忽略艺术家信息。
你可能还想查看 GET /playlists/{playlist_id}/tracks 端点 更好地了解 API 返回的内容以及它们对某些属性的命名方式。
慢慢来!
准备好后,将它与我们想出的内容进行比较
- 曲目的名称是
str。 - “E” 标签表示曲目是否为显式内容。我们可以将其设为
bool类型,并让客户端使用逻辑来显示或不显示“E” 标签。 - 曲目的时长。模型显示的格式为分钟和秒之间用冒号隔开,因此我们可能需要将其设为
str类型,或者客户端团队可能希望控制自己的格式,我们应该将其返回为int类型,其中时长以毫秒为单位。REST 端点返回后者,所以现在让我们坚持使用它。 - 虽然模型上没有显示,但拥有对象的标识符非常有用,因此我们将确保也返回曲目的 ID。
- 可以选择复制到曲目的链接以与他人共享,以便他们也可以在 Spotify 上打开它。因此,我们可能需要将该链接返回为
str。在 REST API 中,他们将其命名为uri。 - 我们将使所有这些 字段 不可为空,因为 REST API 也这样做。
你的设计可能与我们的设计略有不同,但考虑到这些因素,让我们继续创建我们的 Track 类!请记住,我们将所有 GraphQL 类型组织在一起,放在 api/types 文件夹下。
你应该拥有所有你需要知道的内容来尝试自己编写!如果你需要参考,请随意使用下面的内容
import strawberry@strawberry.type(description="A single audio file, usually a song.")class Track:id: strawberry.ID = strawberry.field(description="The ID for the track.")name: str = strawberry.field(description="The name of the track.")duration_ms: int = strawberry.field(description="The track length in milliseconds.")explicit: bool = strawberry.field(description="Whether or not the track has explicit lyrics (true = yes it does; false = no it does not OR unknown)")uri: str = strawberry.field(description="The URI for the track, usually a Spotify link.")
提示:在选择 字段 名称时,请尝试做到描述性且简洁。例如,我们可以选择 duration 作为 字段 名称之一。但是,duration_ms(它反过来在生成的 GraphQL 模式 中变为 durationMs)对以毫秒为单位返回的 字段 格式更具描述性。我们也可以选择 durationInMilliseconds 为了进一步清晰。如果我们还想支持返回格式化的字符串,我们可以添加一个新的 字段 称为 durationString。使用 GraphQL 描述也有助于提高清晰度。 了解有关 Apollo 文档中的模式命名约定的更多信息。
不要忘记使用 description 参数 在 strawberry.type 和 strawberry.field 上添加模式描述。
将曲目连接到播放列表
有了我们的 Track 类,我们现在可以添加期待已久的 tracks 字段 到 Playlist 中。
from .track import Trackclass Playlist:# ... other Playlist fieldstracks: list[Track] = strawberry.field(description="The playlist's tracks.")
让我们考虑一下这个特定的 解析器 函数。到目前为止,Playlist 类包含简单的属性 解析器(用于 id、name 和 description)。请记住,在幕后,Strawberry 为属性添加了默认 解析器。
我们可以对我们的 tracks 解析器 也这样做吗?好吧,让我们检查一下我们的数据来自哪里。对于下一部分,我们强烈建议使用代码编辑器的功能来 Cmd/Ctrl + 点击特定类型以导航到其类型导航!
特定播放列表的详细信息来自 Query.playlist 解析器 函数,该函数使用 get_playlist.asyncio 函数获取数据。
async def playlist(id: strawberry.ID, info: strawberry.Info) -> Playlist | None:client = info.context["spotify_client"]data = await get_playlist.asyncio(client=client, playlist_id=id)
如果我们检查 get_playlist.asyncio 函数的返回类型,我们会看到它返回 SpotifyObjectPlaylist。此类型具有 tracks 属性,类型为 SpotifyObjectPaginatedSpotifyObjectPlaylistTrack。 SpotifyObjectPaginatedSpotifyObjectPlaylistTrack 然后具有一系列与分页相关的其他属性,以及一个名为 items 的属性,它是一个 SpotifyObjectPlaylistTrack 类型列表。
接下来 那个 类型,我们获取了更多与播放列表曲目元数据相关的属性,比如谁添加了它以及何时添加,以及一个名为 Track 的属性,它是 SpotifyObjectPlaylistTrackItem 类型。 最后,这看起来像是我们正在寻找的 track 信息!除其他属性外,它还具有 id、name、explicit、duration_ms 属性,它们与我们 自己的 Track 类在我们的 GraphQL 架构 中完全匹配。
哇!所有这些都是为了说,我们必须深入挖掘几个级别才能返回我们需要的价值。
但是,我们实际上已经完成了第一级。我们已经将 SpotifyObjectPlaylist 类型转换为 我们 自己的 Playlist 类,在 Query.playlist 解析器 中。
async def playlist(id: strawberry.ID, info: strawberry.Info) -> Playlist | None:client = info.context["spotify_client"]data = await get_playlist.asyncio(client=client, playlist_id=id)if data is None:return Nonereturn Playlist(id=strawberry.ID(data.id),name=data.name,description=data.description,)
现在我们可以更新返回的对象。我们将添加一个新的属性到实例化中,tracks,它将返回一个列表。
return Playlist(id=strawberry.ID(data.id),name=data.name,description=data.description,tracks=[...],)
在列表中,我们将遍历 data.tracks.items 中的每个 item。
然后,使用 item 中的属性,创建 Track 类的实例,其中包含我们需要的全部 字段。
tracks=[Track(id=strawberry.ID(item.track.id),name=item.track.name,duration_ms=item.track.duration_ms,explicit=item.track.explicit,uri=item.track.uri,)for item in data.tracks.items],
我们还需要在文件顶部导入 Track 类型:
from .types.track import Track
探索时间!
这是一段漫长的旅程,但我们应该有足够的资源来 查询 播放列表的曲目!确保我们的服务器正在运行,并具有最新的更改。
query GetPlaylistDetails($playlistId: ID!) {playlist(id: $playlistId) {idnamedescriptiontracks {idnamedurationMsexplicituri}}}
使用 变量 部分设置为:
{"playlistId": "6Fl8d6KF0O4V5kFdbzalfW"}
哇,这个播放列表有这么多曲目!
另一种路径
现在,featuredPlaylists 路径怎么样?它是我们架构的另一个入口点,它返回一个 Playlist 类型的列表,然后可以访问它的 tracks 字段。让我们试一试。
query GetFeaturedPlaylists {featuredPlaylists {idnamedescriptiontracks {idnameexplicituri}}}
当我们运行这个 查询 时,我们得到了一个 errors 数组,其中包含消息 "Playlist.__init__() missing 1 required keyword-only argument: 'tracks'"。糟糕!
主要收获
- 解析器 函数可能涉及遍历多级数据,尤其是在嵌套对象场景中。
- 我们建议为架构选择描述性且简洁的 字段 名称。
下一步
让我们调查一下错误的根源并修复它。
分享您有关本课程的问题和评论
本课程目前处于
您需要一个 GitHub 帐户才能在下面发布。没有帐户? 请在我们的 Odyssey 论坛上发布。