概述
我们还缺少一些东西:播放列表的曲目 - 我们实际上想听的歌曲!
在本课中,我们将
- 创建
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 论坛上发布。