2017-10-13 83 views
1

我有兩個Rails環境。一個運行Postgres和Rails 5.0.6的開發環境和Heroku上幾乎相同的環境。Rails回調在不同環境下表現不同

我有一個Administrator類,它生成基於用戶的forenamesurname字段before_save回調的Administrator一個用戶名。

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: '[email protected]', 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", "[email protected]"], ["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", "[email protected]"], ["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: "[email protected]", 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: '[email protected]', forename: 'Edward', surname: 'Scissorhands') 
    (1.9ms) BEGIN 
    Administrator Exists (1.1ms) SELECT 1 AS one FROM "administrators" WHERE "administrators"."email" = $1 LIMIT $2 [["email", "[email protected]"], ["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)並運行相同的代碼庫。

+0

這是在你的代碼一個錯字或禮物? 'Administrator.where(「username ILIKE?」,「#{proposed}%」)。size'' ILIKE'應該是'LIKE'。 –

+2

ILIKE是LIKE,但不區分大小寫。 –

回答

1

before_save在驗證之後被調用,因此出現錯誤。

改爲嘗試before_validation。

僅供參考以下是創建對象時,回調調用的順序:

  • before_validation
  • after_validation
  • before_save
  • around_save
  • before_create
  • around_create
  • after_create
  • after_save的
  • after_commit/after_rollback
+0

是的,這是對的。事實證明,我們有骯髒的測試數據導致驗證失敗,因爲我們選擇了錯誤的回調來處理我們的情況。謝謝。 –

1

你的代碼中的邏輯是有缺陷的。這是一個合法的錯誤;你需要重新設計用戶名生成的單詞。

例如,假設系統中有一個用戶名爲:edwardscissorhands1。沒有edwardscissorhands,並沒有edwardscissorhands2/3/4

行:Administrator.where("username ILIKE ?", "edwardscissorhands%").size回報1,然後你的邏輯試圖創建一個已經存在的新用戶。

......我無法確定在生產服務器上發生了什麼事,卻沒有看到實際的數據,但我敢打賭這是這樣的。它可能稍微更復雜一些,例如用戶:tom,tom3tomlord存在;因此您的邏輯嘗試創建第二個用戶。

例如,如果您生成了一些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 

這也許可以提高性能,明智的,儘管這裏的多個數據庫查詢不太可能在一個重大問題真正的應用程序(假設你沒有得到批次管理員具有相同的名稱!)。

相關問題