2011-05-27 84 views
6

我正在調查validates_presence_of實際如何工作。假設我有兩個型號validates_presence_of與belongs_to關聯,正確的方式

class Project < ActiveRecord::Base 
    [...] 
    has_many :roles 
end 

class Role < ActiveRecord::Base 
    validates_presence_of :name, :project 

    belongs_to :project 
end 

我希望它讓角色始終屬於現有的項目,但我剛剛從this example發現,這可能會導致保存無效(孤立的)角色進入數據庫。所以正確的做法是在我的Role模型中插入validates_presence_of :project_id,它似乎工作,即使我認爲語義上有更多的意義來驗證項目的存在而不是項目ID。

除此之外,我想我可以把一個無效的id(對於一個不存在的項目),如果我只是驗證project_id的存在,因爲默認情況下AR不會爲遷移添加完整性檢查,即使我添加他們手動某些數據庫不支持他們(即MySQL與MyISAM或sqlite)。這個例子證明

# with validates_presence_of :name, :project, :project_id in the role class 
Role.create!(:name => 'foo', :project_id => 1334, :project => Project.new) 
    AREL (0.4ms) INSERT INTO "roles" ("name", "project_id") VALUES ('foo', NULL) 
+----+------+------------+ 
| id | name | project_id | 
+----+------+------------+ 
| 7 | foo |   | 
+----+------+------------+ 

當然我不會寫這樣的代碼,但我想阻止這種在DB錯誤的數據。

我想知道如何確保一個角色總是有一個(真實的和保存的)項目關聯。

我發現了validates_existence寶石,但我寧願不添加寶石到我的項目中,除非是絕對必要的。

對此有何想法?

更新

validates_presence_of :project和在遷移PROJECT_ID列添加:null => false似乎是一個清潔的解決方案。

+0

我強烈建議使用validates_existence gem來做這件事,因爲它正是你需要的。此外,它具有相當小的依賴性。 – Jits 2011-06-02 13:47:50

+1

只是一個快速不 - 確保你使用你的數據庫來驗證。讓生活更安全。 – CharlesJHardy 2011-06-02 17:34:20

+0

@Jits,我想我會那樣做的。 @Chuck我也會這樣做,但這樣我就不會有驗證錯誤,所以我仍然需要在ruby級別進行驗證。 – Fabio 2011-06-06 16:57:21

回答

2

我嘗試了很多驗證器的組合,但最乾淨的解決方案是使用validates_existence寶石。有了,我可以寫這樣

r = Role.new(:name => 'foo', :project => Project.new) # => #<Role id: nil, name: "foo", project_id: nil, created_at: nil, updated_at: nil> 
r.valid? # => false 
r.errors # => {:project=>["does not exist"], :project_id=>["does not exist"]} 

代碼,所以我最終的模型很簡單,只要

class Role < ActiveRecord::Base 
    belongs_to :project 
    validates_existence_of :project 
    # or with alternate syntax 
    validates :project, :existence => true 
    [...] 
end 

使用DB驗證加阿迪亞溶液(即:空=>在遷移和validates_presence_of假:項目在模型中)Role#valid?將返回true,Role#save將在project_id爲空時在數據庫級別引發異常。

6

如果找不到具有id的對象,Rails將嘗試在id上查找並添加驗證錯誤。

class Role < AR::Base 
    belongs_to :project 
    validates_presence_of :project, :name 
end 


Role.create!(:name => "admin", :project_id => 1334)# Project 1334 does not exist 
# => validation error raised 

我看到你的問題也想處理作者對象提供的情況,但是新的而不是db。在存在檢查不起作用的情況下。將解決。

Role.create!(:name => "admin", :project => Project.new) # Validation passes when it shouldn't. 

更新: 在一定程度上可以減輕這樣做對相關的驗證通過虛設新對象的效果:項目。

class Role < ActiveRecord::Base 
    belongs_to :project 
    validates_presence_of :project 
    validates_associated :project 
end 

如果Project.new.valid?是假的,然後Role.create!(:name => "admin", :project => Project.new)也將產生一個錯誤。但是,如果Project.new.valid?爲真,那麼上述內容將在保存時創建一個項目對象。

是否使用validates_associated :project對您有幫助?

+0

我不喜歡'validates_associated:project',因爲它似乎需要兩條線才能滿足相同的要求,即項目必須存在,但它確實有效。 – Fabio 2011-06-01 02:08:19