使用 vapor-fluent 更新模型
Using vapor-fluent to upsert models
我目前正在努力使用 vapor/fluent 进行更新插入。我有一个像这样的模型:
struct DeviceToken: PostgreSQLModel {
var id: Int?
var token: String
var updatedAt: Date = Date()
init(id: Int? = nil, token: String, updatedAt: Date = Date()) {
self.id = id
self.token = token
self.updatedAt = updatedAt
}
}
struct Account: PostgreSQLModel {
var id: Int?
let username: String
let service: String
...
let deviceTokenId: DeviceToken.ID
init(id: Int? = nil, service: String, username: String, ..., deviceTokenId: DeviceToken.ID) {
self.id = id
self.username = username
....
self.deviceTokenId = deviceTokenId
}
}
来自客户的类似
{
"deviceToken": {
"token": "ab123",
"updatedAt": "01-01-2019 10:10:10"
},
"account": {
"username": "user1",
"service": "some service"
}
}
发送中。
我想做的是插入新模型(如果它们不存在),否则更新它们。我看到了 create(orUpdate:)
方法,但是只有在 id 相同时才会更新(据我所知)。由于客户端不发送 id,我不太确定如何处理它。
我也无法解码模型,因为发送帐户时没有 deviceTokenId
,因此解码会失败。我想我可以通过覆盖 NodeCovertible
或使用两种不同的模型(一个用于解码没有 id 的 json 和上面的实际模型)来解决后一个问题。然而,第一个问题仍然存在。
我真正想做的是:
如果带有令牌的条目已存在则更新 DeviceToken 否则创建它
如果用户名和服务组合的帐户已经存在,请更新其用户名、服务和 deviceTokenId,否则创建它。 DeviceTokenId是1返回的id。
你有机会帮我吗?
对于所有感兴趣的人:
我通过在 PostgreSQLModel 上编写扩展来提供更新插入方法来解决它。我添加了一个要点供您查看:here.
由于此类链接有时会在您需要此处快速概览信息时断开:
实际更新插入实现:
extension QueryBuilder
where Result: PostgreSQLModel, Result.Database == Database {
/// Creates the model or updates it depending on whether a model
/// with the same ID already exists.
internal func upsert(_ model: Result,
columns: [PostgreSQLColumnIdentifier]) -> Future<Result> {
let row = SQLQueryEncoder(PostgreSQLExpression.self).encode(model)
/// remove id from row if not available
/// otherwise the not-null constraint will break
row = row.filter { (key, value) -> Bool in
if key == "id" && value.isNull { return false }
return true
}
let values = row
.map { row -> (PostgreSQLIdentifier, PostgreSQLExpression) in
return (.identifier(row.key), row.value)
}
self.query.upsert = .upsert(columns, values)
return create(model)
}
}
便捷方法
extension PostgreSQLModel {
/// Creates the model or updates it depending on whether a model
/// with the same ID already exists.
internal func upsert(on connection: DatabaseConnectable) -> Future<Self> {
return Self
.query(on: connection)
.upsert(self, columns: [.keyPath(Self.idKey)])
}
internal func upsert<U>(on connection: DatabaseConnectable,
onConflict keyPath: KeyPath<Self, U>) -> Future<Self> {
return Self
.query(on: connection)
.upsert(self, columns: [.keyPath(keyPath)])
}
....
}
我解决了另一个问题,即我的数据库模型无法解码,因为 id 不是从客户端发送的,方法是使用一个内部结构,该结构只包含客户端发送的属性。 id 和其他数据库生成的属性位于外部结构中。类似于:
struct DatabaseModel: PostgreSQLModel {
var id: Int?
var someProperty: String
init(id: Int? = nil, form: DatabaseModelForm) {
self.id = id
self.someProperty = form.someProperty
}
struct DatabaseModelForm: Content {
let someProperty: String
}
}
我目前正在努力使用 vapor/fluent 进行更新插入。我有一个像这样的模型:
struct DeviceToken: PostgreSQLModel {
var id: Int?
var token: String
var updatedAt: Date = Date()
init(id: Int? = nil, token: String, updatedAt: Date = Date()) {
self.id = id
self.token = token
self.updatedAt = updatedAt
}
}
struct Account: PostgreSQLModel {
var id: Int?
let username: String
let service: String
...
let deviceTokenId: DeviceToken.ID
init(id: Int? = nil, service: String, username: String, ..., deviceTokenId: DeviceToken.ID) {
self.id = id
self.username = username
....
self.deviceTokenId = deviceTokenId
}
}
来自客户的类似
{
"deviceToken": {
"token": "ab123",
"updatedAt": "01-01-2019 10:10:10"
},
"account": {
"username": "user1",
"service": "some service"
}
}
发送中。
我想做的是插入新模型(如果它们不存在),否则更新它们。我看到了 create(orUpdate:)
方法,但是只有在 id 相同时才会更新(据我所知)。由于客户端不发送 id,我不太确定如何处理它。
我也无法解码模型,因为发送帐户时没有 deviceTokenId
,因此解码会失败。我想我可以通过覆盖 NodeCovertible
或使用两种不同的模型(一个用于解码没有 id 的 json 和上面的实际模型)来解决后一个问题。然而,第一个问题仍然存在。
我真正想做的是:
如果带有令牌的条目已存在则更新 DeviceToken 否则创建它
如果用户名和服务组合的帐户已经存在,请更新其用户名、服务和 deviceTokenId,否则创建它。 DeviceTokenId是1返回的id。
你有机会帮我吗?
对于所有感兴趣的人: 我通过在 PostgreSQLModel 上编写扩展来提供更新插入方法来解决它。我添加了一个要点供您查看:here.
由于此类链接有时会在您需要此处快速概览信息时断开:
实际更新插入实现:
extension QueryBuilder
where Result: PostgreSQLModel, Result.Database == Database {
/// Creates the model or updates it depending on whether a model
/// with the same ID already exists.
internal func upsert(_ model: Result,
columns: [PostgreSQLColumnIdentifier]) -> Future<Result> {
let row = SQLQueryEncoder(PostgreSQLExpression.self).encode(model)
/// remove id from row if not available
/// otherwise the not-null constraint will break
row = row.filter { (key, value) -> Bool in
if key == "id" && value.isNull { return false }
return true
}
let values = row
.map { row -> (PostgreSQLIdentifier, PostgreSQLExpression) in
return (.identifier(row.key), row.value)
}
self.query.upsert = .upsert(columns, values)
return create(model)
}
}
便捷方法
extension PostgreSQLModel {
/// Creates the model or updates it depending on whether a model
/// with the same ID already exists.
internal func upsert(on connection: DatabaseConnectable) -> Future<Self> {
return Self
.query(on: connection)
.upsert(self, columns: [.keyPath(Self.idKey)])
}
internal func upsert<U>(on connection: DatabaseConnectable,
onConflict keyPath: KeyPath<Self, U>) -> Future<Self> {
return Self
.query(on: connection)
.upsert(self, columns: [.keyPath(keyPath)])
}
....
}
我解决了另一个问题,即我的数据库模型无法解码,因为 id 不是从客户端发送的,方法是使用一个内部结构,该结构只包含客户端发送的属性。 id 和其他数据库生成的属性位于外部结构中。类似于:
struct DatabaseModel: PostgreSQLModel {
var id: Int?
var someProperty: String
init(id: Int? = nil, form: DatabaseModelForm) {
self.id = id
self.someProperty = form.someProperty
}
struct DatabaseModelForm: Content {
let someProperty: String
}
}