创建实例集 id,虽然它应该是 nil
Creation of instance set id, although it should be nil
我正在用 fluent 做一个小的 vapor 项目。现在我应该能够创建一个用户,为所述创建的用户创建一个令牌,并 return 具有会话令牌的用户。之后我应该能够登录并创建一个新令牌。
现在的问题是,每当我创建一个令牌时,该令牌的 id
值设置为 1
,即使我将其设置为 nil
。创建用户时不会发生这种情况,然后自动增量按预期工作。
我使用的数据库是MySQL.
我的迁移CreateTokens.swift
:
import Fluent
struct CreateTokens: Migration {
func prepare(on database: Database) -> EventLoopFuture<Void> {
database.schema(Token.schema)
.field("id", .int, .identifier(auto: true))
.unique(on: "id")
.field("user_id", .int, .references("users", "id"))
.field("value", .string, .required)
.unique(on: "value")
.field("created_at", .datetime, .required)
.field("expires_at", .datetime)
.create()
}
func revert(on database: Database) -> EventLoopFuture<Void> {
database.schema(Token.schema).delete()
}
}
我的令牌(Token.swift
):
import Vapor
import Fluent
final class Token: Model {
static let schema = "tokens"
@ID(custom: "id", generatedBy: .database)
var id: Int?
@Parent(key: "id")
var user: User
@Field(key: "value")
var value: String
@Field(key: "expires_at")
var expiresAt: Date?
@Timestamp(key: "created_at", on: .create)
var createdAt: Date?
init() {}
init(id: Int? = nil,
userId: User.IDValue,
token: String,
expiresAt: Date?
) {
self.id = id
self.$user.id = userId
self.value = token
self.expiresAt = expiresAt
}
}
extension Token: ModelTokenAuthenticatable {
static let valueKey = \Token.$value
static let userKey = \Token.$user
var isValid: Bool {
guard let expiryDate = expiresAt else {
return true
}
return expiryDate > Date()
}
}
在我的用户结构中,当我创建一个令牌时:
func createToken() throws -> Token {
let calendar = Calendar(identifier: .gregorian)
let expiryDate = calendar.date(byAdding: .year, value: 1, to: Date())
let token = try Token(userId: requireID(), token: [UInt8].random(count: 16).base64, expiresAt: expiryDate)
return token
}
如果我在 return token
上设置断点并打印出令牌,我会得到以下信息:
(lldb) po token
Token(input: [expires_at: Optional(2021-11-11 21:01:10 +0000), value: "wn8kvw/GYSfqL280RLCDbQ==", id: 1])
ID 值设置为 1
,但在代码中(对我来说)很清楚我已将其设置为 nil
。即使我在 return 之前添加 token.id = nil
,它仍然设置为 1
。
我在这里做错了什么?还是我偶然发现了流畅的错误?
我至少花了 1.5 小时才找到这个问题,这是一个愚蠢的错误。
问题出在:
@Parent(key: "id")
var user: User
这应该是 user_id
作为键,而不是 id
。现在它将 id 重新写入我发送的任何 userId。
正如您所提到的,键“id”似乎同时用于 id
和 user
属性,这是这种字符串类型 API.
我试图缓解这种情况的一种方法是扩展 FieldKey
并强迫自己 总是 使用这些静态属性而不是字符串,这样 IDE 的自动完成功能让我有机会思考要使用的正确字段键——这是您手动输入时所缺少的东西。
extension FieldKey {
static var userID: FieldKey { "user_id" }
}
// And in the model
@Parent(key: .userID)
var user: User
希望这对以后有所帮助!
我正在用 fluent 做一个小的 vapor 项目。现在我应该能够创建一个用户,为所述创建的用户创建一个令牌,并 return 具有会话令牌的用户。之后我应该能够登录并创建一个新令牌。
现在的问题是,每当我创建一个令牌时,该令牌的 id
值设置为 1
,即使我将其设置为 nil
。创建用户时不会发生这种情况,然后自动增量按预期工作。
我使用的数据库是MySQL.
我的迁移CreateTokens.swift
:
import Fluent
struct CreateTokens: Migration {
func prepare(on database: Database) -> EventLoopFuture<Void> {
database.schema(Token.schema)
.field("id", .int, .identifier(auto: true))
.unique(on: "id")
.field("user_id", .int, .references("users", "id"))
.field("value", .string, .required)
.unique(on: "value")
.field("created_at", .datetime, .required)
.field("expires_at", .datetime)
.create()
}
func revert(on database: Database) -> EventLoopFuture<Void> {
database.schema(Token.schema).delete()
}
}
我的令牌(Token.swift
):
import Vapor
import Fluent
final class Token: Model {
static let schema = "tokens"
@ID(custom: "id", generatedBy: .database)
var id: Int?
@Parent(key: "id")
var user: User
@Field(key: "value")
var value: String
@Field(key: "expires_at")
var expiresAt: Date?
@Timestamp(key: "created_at", on: .create)
var createdAt: Date?
init() {}
init(id: Int? = nil,
userId: User.IDValue,
token: String,
expiresAt: Date?
) {
self.id = id
self.$user.id = userId
self.value = token
self.expiresAt = expiresAt
}
}
extension Token: ModelTokenAuthenticatable {
static let valueKey = \Token.$value
static let userKey = \Token.$user
var isValid: Bool {
guard let expiryDate = expiresAt else {
return true
}
return expiryDate > Date()
}
}
在我的用户结构中,当我创建一个令牌时:
func createToken() throws -> Token {
let calendar = Calendar(identifier: .gregorian)
let expiryDate = calendar.date(byAdding: .year, value: 1, to: Date())
let token = try Token(userId: requireID(), token: [UInt8].random(count: 16).base64, expiresAt: expiryDate)
return token
}
如果我在 return token
上设置断点并打印出令牌,我会得到以下信息:
(lldb) po token
Token(input: [expires_at: Optional(2021-11-11 21:01:10 +0000), value: "wn8kvw/GYSfqL280RLCDbQ==", id: 1])
ID 值设置为 1
,但在代码中(对我来说)很清楚我已将其设置为 nil
。即使我在 return 之前添加 token.id = nil
,它仍然设置为 1
。
我在这里做错了什么?还是我偶然发现了流畅的错误?
我至少花了 1.5 小时才找到这个问题,这是一个愚蠢的错误。
问题出在:
@Parent(key: "id")
var user: User
这应该是 user_id
作为键,而不是 id
。现在它将 id 重新写入我发送的任何 userId。
正如您所提到的,键“id”似乎同时用于 id
和 user
属性,这是这种字符串类型 API.
我试图缓解这种情况的一种方法是扩展 FieldKey
并强迫自己 总是 使用这些静态属性而不是字符串,这样 IDE 的自动完成功能让我有机会思考要使用的正确字段键——这是您手动输入时所缺少的东西。
extension FieldKey {
static var userID: FieldKey { "user_id" }
}
// And in the model
@Parent(key: .userID)
var user: User
希望这对以后有所帮助!