如何从 Vapor 3 中的 JSON 响应中保存父子关系
Posted
技术标签:
【中文标题】如何从 Vapor 3 中的 JSON 响应中保存父子关系【英文标题】:How to save Parent-Child relation from a JSON response in Vapor 3 【发布时间】:2019-10-09 19:31:41 【问题描述】:我正在使用 Vapor 3 开发一个 REST API。此 API 使用另一个 API 创建稍后将由应用程序使用的内容。 所以我创建了一个从这个 API(联赛和赛季)获取内容并将它们存储在我的 mysql 数据库中的函数。 API 的响应还包含我也想存储的嵌套对象,如果可能的话都在同一个请求中。以下是 API 响应:
"data": [
"id": 271,
"name": "Superliga",
"current_season_id": 16020,
"season":
"data":
"id": 16020,
"name": "2019/2020",
"league_id": 271,
]
这是模型:
final class League: MySQLModel
var id: League.ID?
var name: String
var current_season_id: Season.ID
var currentSeason: Parent<League, Season>
return parent(\League.current_season_id)
final class Season: MySQLModel
var id: Season.ID?
var name: String
var league_id: League.ID
var league: Parent<Season, League>
return parent(\.league_id)
这是执行请求并存储在数据库中的函数。
func getLeagues(using context: CommandContext) throws -> EventLoopFuture<Void>
guard let url = URL(string: "SOME_API_URL") else return .done(on: context.container)
let client = try context.container.client()
return client.get(url).flatMap( (response) -> EventLoopFuture<Void> in // do the request
let leagues = response.content.get([League].self, at: "data") // get the array of EventLoopFuture<[League]>
return context.container.requestPooledConnection(to: .mysql).flatMap( (connection) -> EventLoopFuture<Void> in // connecto to DB
let savedLeagues = leagues.flatMap(to: [League].self, (flattenLeagues) -> EventLoopFuture<[League]> in
return flattenLeagues.map (league) -> EventLoopFuture<League> in
return league.create(orUpdate: true, on: connection) // save on the DB
.flatten(on: context.container)
)
return savedLeagues.flatMap (_) -> EventLoopFuture<Void> in
return .done(on: context.container)
)
)
问题是:可以保存父子关系吗?我必须使用解码/编码功能手动完成吗?
我确实实现了编码/解码并创建了联赛,但不知道如何创建赛季以及在执行时如何保存所有内容league.create(orUpdate: true, on: connection)
如有任何帮助,将不胜感激。
【问题讨论】:
【参考方案1】:在我看来,您可以先解码 APIModel
,然后像这样将两个对象保存在 flatten 循环中
struct APILeague: Content
let id: League.ID
let name: String
let current_season_id: Season.ID
struct _Season: Codable
let data: Season
let season: _Season
final class League: MySQLModel
var id: League.ID?
var name: String
var current_season_id: Season.ID
var currentSeason: Parent<League, Season>
return parent(\League.current_season_id)
init (_ data: APILeague)
self.id = data.id
self.name = data.name
self.current_season_id = data.current_season_id
func getLeagues(using context: CommandContext) throws -> Future<Void>
guard let url = URL(string: "SOME_API_URL") else return .done(on: context.container)
let client = try context.container.client()
return client.get(url).flatMap response in // do the request
return response.content.get([APILeague].self, at: "data").flatMap leagues in
return context.container.requestPooledConnection(to: .mysql).flatMap connection in // connecto to DB
let operations = leagues.map league in
return League(league).create(orUpdate: true, on: connection).flatMap _ in
return league.season.data.create(orUpdate: true, on: connection).transform(to: ()) // transforming to Void
return operations.flatten(on: context.container)flatMap
return .done(on: context.container)
【讨论】:
成功了,感谢您的回答。不过,为了处理映射而复制所有字段似乎是一种过度杀戮。我可能还会有我的模型的公共对象以及返回应用程序......所以可能有 3 个重复的对象。 一个问题,最后的返回不能只是return operations.flatten(on: context.container)
而不是return operations.flatten(on: context.container).flatMap return .done(on: context.container)
?
@Lucho 是的,它可能只是return operations.flatten(on: context.container)
@Lucho 和这条线 guard let url = URL(string: "SOME_API_URL") else return .done(on: context.container)
我宁愿改成 guard let url = URL(string: "SOME_API_URL") else throw Abort(.internalServerError, reason: "API not available")
@Lucho btw 我可以在 Discord 上以 imike#3049 的身份随时聊天以上是关于如何从 Vapor 3 中的 JSON 响应中保存父子关系的主要内容,如果未能解决你的问题,请参考以下文章
将大型 JSON 直接从 Java 中的 REST API 响应流保存为文本
从 Vapor 3 到 Vapor 4 的 LingoVapor 包