[英]Rails callbacks behaving differently on different environments
我有兩個Rails環境。 一個運行Postgres和Rails 5.0.6的開發環境,以及一個在Heroku上幾乎相同的環境。
我有一個Administrator
類,該類根據用戶的surname
和surname
字段在before_save
回調中為Administrator
生成用戶forename
。
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
驗證用戶之后, FORENAMESURNAMEX
的形式生成用戶名,其中X是遞增數字(或不遞增)。
這是我在開發機器上的Rails控制台中運行的命令。
irb(main):012:0> Administrator.create(email: 'edward@test.net', forename: 'Edward', surname: 'Scissorhands')
D, [2017-10-13T10:00:18.985765 #280] DEBUG -- : (0.2ms) BEGIN
D, [2017-10-13T10:00:18.987554 #280] DEBUG -- : Administrator Exists (0.5ms) SELECT 1 AS one FROM "administrators" WHERE "administrators"."email" = $1 LIMIT $2 [["email", "edward@test.net"], ["LIMIT", 1]]
D, [2017-10-13T10:00:18.988923 #280] DEBUG -- : Administrator Exists (0.4ms) SELECT 1 AS one FROM "administrators" WHERE "administrators"."username" IS NULL LIMIT $1 [["LIMIT", 1]]
D, [2017-10-13T10:00:18.990155 #280] DEBUG -- : (0.4ms) SELECT COUNT(*) FROM "administrators" WHERE (username ILIKE 'edwardscissorhands%')
D, [2017-10-13T10:00:18.992000 #280] DEBUG -- : SQL (0.5ms) INSERT INTO "administrators" ("email", "created_at", "updated_at", "username", "forename", "surname") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id" [["email", "edward@test.net"], ["created_at", "2017-10-13 10:00:18.990421"], ["updated_at", "2017-10-13 10:00:18.990421"], ["username", "edwardscissorhands"], ["forename", "Edward"], ["surname", "Scissorhands"]]
D, [2017-10-13T10:00:18.995845 #280] DEBUG -- : (1.8ms) COMMIT
=> #<Administrator id: 10, email: "edward@test.net", created_at: "2017-10-13 10:00:18", updated_at: "2017-10-13 10:00:18", role: nil, otp_public_key: nil, username: "edwardscissorhands", forename: "Edward", surname: "Scissorhands">
如您所見,將執行回調,並生成用戶的用戶名,並將其持久保存到數據庫中。
但是,當我在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
盡管在實際應用程序中,這里的多個數據庫查詢不太可能成為主要問題(假設您沒有很多同名管理員!),這可能會提高性能。
驗證之后調用before_save,因此會出錯。
請嘗試使用before_validation。
供參考,以下是在創建對象時調用的訂單回調:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.