什麼錯誤?
若在新建一張資料表時,希望其中欄位不要有空值,可以在 建立時 輸入null: false以資料庫層級進行資料驗證。但若是在 已經存在 的資料表下,希望新增一欄不為空值的欄位時,就會引發錯誤。
假設有張資料表為User,其中已有name、email等欄位,但還沒有任何資料存入(如下表)
| User table | |
|---|---|
| Name | |
這時想要在欄位中加入gender,並指定不能為空值:
1
2
3def change
add_column :users, :gender, :string, null: false
end
若是以上面方式進行migration,將會出現錯誤:
1
Cannot add a NOT NULL column with default value NULL
為何產生錯誤?
SQLite在新增一個不能為空值的欄位時,會需要你順手設定他的default值;因為SQLite將欄位的default設為null,若這時限制新的欄位不能是null,卻又不給他其他default,將會產生矛盾,因此發生錯誤。
如何解決?
1. 圖法煉鋼,砍掉資料表重練
若資料表內沒有資料,且沒有其他關聯,那砍掉可能比較快;但如果已經有其他資料,就沒辦法這樣子用了。
2. 在User Model中增加資料驗證
可以在 model/user.rb 內增加資料驗證:validates_presence_of :gender
這是一個避開資料庫設定的方法,雖然也能擋掉gender是空值的情形,但資料庫層級與應用程式層級的驗證仍是不同的;若不是在資料庫中設限制,一旦應用程式轉換了就有可能讓資料出現空值,因此這個方法並沒有真正解決問題。
3. 加入default值的設定
1 | def change |
若過去的資料能夠有個固定的default,這將會是個好辦法;不過以性別的例子,就不會是個好方式。
4. 先不設定null與default,之後在change_column
1 | def change |
採取先新增欄位,再瞬間增加條件限制。能夠這樣做是因為SQLite對新增欄位較為嚴謹,算是一個繞過障礙的做法吧!
但如果資料表已經有資料了…
那就沒辦法了,只能讓歷史資料在該欄位先有值,再做新的資料表遷移了。