12. 播放列表的曲目
5m

概述

我们还缺少一些东西:播放列表的曲目 - 我们实际上想听的歌曲!

在本课中,我们将

  • 创建Track 类型
  • 编写 函数用于播放列表的曲目

The Track 对象

Mockup design of a playlist's tracks

花几分钟时间研究上面的模型并开始思考我们需要哪些数据,以及它们可能是什么类型。为了简化起见,我们现在将忽略艺术家信息。

你可能还想查看 GET /playlists/{playlist_id}/tracks 端点 更好地了解 API 返回的内容以及它们对某些属性的命名方式。

慢慢来!

准备好后,将它与我们想出的内容进行比较

Mockup design of a playlist's tracks

  • 曲目的名称是 str
  • “E” 标签表示曲目是否为显式内容。我们可以将其设为 bool 类型,并让客户端使用逻辑来显示或不显示“E” 标签。
  • 曲目的时长。模型显示的格式为分钟和秒之间用冒号隔开,因此我们可能需要将其设为 str 类型,或者客户端团队可能希望控制自己的格式,我们应该将其返回为 int 类型,其中时长以毫秒为单位。REST 端点返回后者,所以现在让我们坚持使用它。
  • 虽然模型上没有显示,但拥有对象的标识符非常有用,因此我们将确保也返回曲目的 ID。
  • 可以选择复制到曲目的链接以与他人共享,以便他们也可以在 Spotify 上打开它。因此,我们可能需要将该链接返回为 str。在 REST API 中,他们将其命名为 uri
  • 我们将使所有这些 不可为空,因为 REST API 也这样做。

你的设计可能与我们的设计略有不同,但考虑到这些因素,让我们继续创建我们的 Track 类!请记住,我们将所有 类型组织在一起,放在 api/types 文件夹下。

你应该拥有所有你需要知道的内容来尝试自己编写!如果你需要参考,请随意使用下面的内容

api/types/track.py
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(它反过来在生成的 中变为 durationMs)对以毫秒为单位返回的 格式更具描述性。我们也可以选择 durationInMilliseconds 为了进一步清晰。如果我们还想支持返回格式化的字符串,我们可以添加一个新的 称为 durationString。使用 描述也有助于提高清晰度。 了解有关 Apollo 文档中的模式命名约定的更多信息

不要忘记使用 description strawberry.typestrawberry.field 上添加模式描述。

将曲目连接到播放列表

有了我们的 Track 类,我们现在可以添加期待已久的 tracks Playlist 中。

api/types/playlist.py
from .track import Track
class Playlist:
# ... other Playlist fields
tracks: list[Track] = strawberry.field(description="The playlist's tracks.")

让我们考虑一下这个特定的 函数。到目前为止,Playlist 类包含简单的属性 (用于 idnamedescription)。请记住,在幕后,Strawberry 为属性添加了默认

我们可以对我们的 tracks 也这样做吗?好吧,让我们检查一下我们的数据来自哪里。对于下一部分,我们强烈建议使用代码编辑器的功能来 Cmd/Ctrl + 点击特定类型以导航到其类型导航!

特定播放列表的详细信息来自 Query.playlist 函数,该函数使用 get_playlist.asyncio 函数获取数据。

api/query.py
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 属性,类型为 SpotifyObjectPaginatedSpotifyObjectPlaylistTrackSpotifyObjectPaginatedSpotifyObjectPlaylistTrack 然后具有一系列与分页相关的其他属性,以及一个名为 items 的属性,它是一个 SpotifyObjectPlaylistTrack 类型列表。

接下来 那个 类型,我们获取了更多与播放列表曲目元数据相关的属性,比如谁添加了它以及何时添加,以及一个名为 Track 的属性,它是 SpotifyObjectPlaylistTrackItem 类型。 最后,这看起来像是我们正在寻找的 track 信息!除其他属性外,它还具有 idnameexplicitduration_ms 属性,它们与我们 自己的 Track 类在我们的 中完全匹配。

哇!所有这些都是为了说,我们必须深入挖掘几个级别才能返回我们需要的价值。

但是,我们实际上已经完成了第一级。我们已经将 SpotifyObjectPlaylist 类型转换为 我们 自己的 Playlist 类,在 Query.playlist 中。

api/query.py
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 None
return Playlist(
id=strawberry.ID(data.id),
name=data.name,
description=data.description,
)

现在我们可以更新返回的对象。我们将添加一个新的属性到实例化中,tracks,它将返回一个列表。

api/query.py
return Playlist(
id=strawberry.ID(data.id),
name=data.name,
description=data.description,
tracks=[
...
],
)

在列表中,我们将遍历 data.tracks.items 中的每个 item

然后,使用 item 中的属性,创建 Track 类的实例,其中包含我们需要的全部

api/query.py
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

探索时间!

这是一段漫长的旅程,但我们应该有足够的资源来 播放列表的曲目!确保我们的服务器正在运行,并具有最新的更改。

GraphQL 操作
query GetPlaylistDetails($playlistId: ID!) {
playlist(id: $playlistId) {
id
name
description
tracks {
id
name
durationMs
explicit
uri
}
}
}

使用 变量 部分设置为:

变量
{
"playlistId": "6Fl8d6KF0O4V5kFdbzalfW"
}
https://127.0.0.1:8000

Explorer - get playlist's tracks

哇,这个播放列表有这么多曲目!

任务!

另一种路径

现在,featuredPlaylists 路径怎么样?它是我们架构的另一个入口点,它返回一个 Playlist 类型的列表,然后可以访问它的 tracks 。让我们试一试。

GraphQL 操作
query GetFeaturedPlaylists {
featuredPlaylists {
id
name
description
tracks {
id
name
explicit
uri
}
}
}

当我们运行这个 时,我们得到了一个 errors 数组,其中包含消息 "Playlist.__init__() missing 1 required keyword-only argument: 'tracks'"。糟糕!

主要收获

  • 函数可能涉及遍历多级数据,尤其是在嵌套对象场景中。
  • 我们建议为架构选择描述性且简洁的 名称。

下一步

让我们调查一下错误的根源并修复它。

上一页

分享您有关本课程的问题和评论

本课程目前处于

测试版
.您的反馈将帮助我们改进!如果您遇到困难或感到困惑,请告诉我们,我们会帮助您解决问题。所有评论都将公开,必须遵守 Apollo 行为准则。请注意,已解决或已处理的评论可能会被删除。

您需要一个 GitHub 帐户才能在下面发布。没有帐户? 请在我们的 Odyssey 论坛上发布。