Ruby on rails Rails回调在不同的环境中表现不同
我有两个Rails环境。一个运行Postgres和Rails 5.0.6的开发环境和Heroku上几乎相同的环境 我有一个Ruby on rails Rails回调在不同的环境中表现不同,ruby-on-rails,ruby,postgresql,heroku,Ruby On Rails,Ruby,Postgresql,Heroku,我有两个Rails环境。一个运行Postgres和Rails 5.0.6的开发环境和Heroku上几乎相同的环境 我有一个Administrator类,它根据用户的forename和names字段在保存之前为管理员生成用户名 class Administrator < ApplicationRecord validates :username, uniqueness: true validates :forename, presence: true validates :su
Administrator
类,它根据用户的forename
和names
字段在保存之前为管理员
生成用户名
class Administrator < ApplicationRecord
validates :username, uniqueness: true
validates :forename, presence: true
validates :surname, presence: true
before_save :generate_username
def generate_username
return if username.present?
proposed = "#{forename}#{surname}".downcase
existing_count = Administrator.where("username ILIKE ?", "#{proposed}%").size
self.username = existing_count.zero? ? proposed : "#{proposed}#{existing_count}"
end
end
正如您所看到的,回调被执行,用户的用户名被生成,并按预期保存到数据库中
但是,当我在Heroku(和Heroku Postgres)上运行的测试环境上运行相同的代码时,会发生以下情况:
irb(main):005:0> Administrator.create!(email: 'edward@test.net', forename: 'Edward', surname: 'Scissorhands')
(1.9ms) BEGIN
Administrator Exists (1.1ms) SELECT 1 AS one FROM "administrators" WHERE "administrators"."email" = $1 LIMIT $2 [["email", "edward@test.net"], ["LIMIT", 1]]
Administrator Exists (0.9ms) SELECT 1 AS one FROM "administrators" WHERE "administrators"."username" IS NULL LIMIT $1 [["LIMIT", 1]]
(0.9ms) ROLLBACK
ActiveRecord::RecordInvalid: Validation failed: Username has already been taken
(我在这里使用create!
而不是create
来显示开发中没有出现的验证错误。)
我不明白为什么不同环境下的行为会有所不同。两者都运行相同版本的Rails(5.0.6)和相同的代码基。代码中的逻辑有缺陷。这是一个合法的错误;您需要重新设计如何生成用户名
例如,假设您的系统中有一个名为:edwardscissorhands1
的用户。没有edwardscissorhands
,也没有edwardscissorhands2/3/4
等
行:Administrator.where(“username ILIKE?”,“edwardscissorhands%”)。size
返回1
,然后您的逻辑尝试创建一个已经存在的新用户
。。。在看不到实际数据的情况下,我无法确定您的生产服务器上发生了什么,但我敢打赌是这样的。它可能稍微复杂一些,例如用户:tom
、tom3
和tomlord
存在;因此,您的逻辑尝试创建第二个tom3
用户
例如,如果您生成了一些edwardscissorhards
用户,然后删除了其中一个或多个用户,则可能会发生这种情况
作为一个示例,这里有一种方法可以重新设计逻辑:
def generate_username
return if username.present?
proposed = "#{forename}#{surname}".downcase
return proposed unless Administrator.exists?("username ILIKE ?", proposed)
counter = 1
while(Administrator.exists?("username ILIKE ?", "#{proposed}#{counter}"))
counter += 1
end
"#{proposed}#{counter}"
end
虽然此处的多个数据库查询在实际应用程序中不太可能是一个主要问题(假设没有许多管理员同名!),但这可能会提高性能。在验证后调用\u save之前,因此会出现错误
请在验证之前尝试
以下是创建对象时调用回调的顺序,以供参考:
- 验证前
- 验证后
- 在你保存之前
- 关于保存
- 在创建之前
- 围绕你创造
- 创建后
- 保存后
- 提交后/回滚后
这是打字错误还是代码中存在<代码>管理员。其中(“用户名ILIKE?”,“#{建议的}%”)。大小
ILIKE
应该是LIKE
。ILIKE是LIKE,但不区分大小写。是的,这是正确的。事实证明,我们有脏的测试数据,这导致验证失败,因为我们为我们的情况选择了错误的回调。非常感谢。
def generate_username
return if username.present?
proposed = "#{forename}#{surname}".downcase
return proposed unless Administrator.exists?("username ILIKE ?", proposed)
counter = 1
while(Administrator.exists?("username ILIKE ?", "#{proposed}#{counter}"))
counter += 1
end
"#{proposed}#{counter}"
end