我想要做的是定義一個有效值列表,然後在相關表上添加新角色時,根據該值列表進行驗證。Rails:如何通過另一個模型爲我的模型中的字段指定有效值
讓我提供一個具體的例子:
說我有一個「就業」表,具有以下字段:
user_id (tied to a user table)
employer_id (tied to an employer table)
position_id (tied to a position table)
details
efbegdt
efenddt
當用戶添加一個新行到這個表,我想確保其他表上的employer_id和position_id已經存在,並且如果兩種情況都不是這種情況,則不允許保存。
的解決方案,我已經看到了迄今爲止採取這種形式:
class Employment < ActiveRecord::Base
EMPLOYERS = ['Google', 'Yahoo', 'Microsoft']
POSITIONS = ['Web Developer', 'Database Admin', 'QA']
validates_inclusion_of :employer_id, :in => EMPLOYERS
validates_inclusion_of :position_id, :in => POSITIONS
end
但這種方法不夠靈活,以適應潛在的僱主數千名和立場,也沒有提供一種簡單的方法,讓用戶添加新的有效條目,如果他們的僱主目前不存在。
我也看到了這種做法:
class Employment < ActiveRecord::Base
validate :employer_exists
protected
def employer_exists
ids = Employer.all.map(&:id)
if !employer_id.blank? && !ids.member?(employer_id)
errors.add(:employer_id, "invalid employer")
end
end
end
這更接近我想要的東西,但是當我測試使用RSpec,檢查是否對僱主的表裏是有效的失敗:
Failure/Error: it { should be_valid }
expected valid? to return true, got false
是否存在針對此問題的「最佳實踐」解決方案?
UPDATE
只是增加另一個例子,所有的設置詳細說明。在此示例中,用戶可以在電子郵件表中存儲多個電子郵件地址,但每種類型(個人,工作,學校等)的地址限制爲一個。另一張表,email_dfn,定義了所有有效的類型:
遷移文件
class CreateEmailDfns < ActiveRecord::Migration
def change
create_table :email_dfns do |t|
t.string :short_description
t.string :long_description
t.timestamps
end
end
end
和
class CreateEmails < ActiveRecord::Migration
def change
create_table :emails do |t|
t.integer :user_id
t.integer :email_dfn_id
t.string :value
t.text :notes
t.timestamps
end
add_index :emails, [:user_id, :email_dfn_id]
end
end
型號
class Email < ActiveRecord::Base
attr_accessible :value, :notes, :email_dfn_id
belongs_to :user
belongs_to :email_dfn
validates_associated :email_dfn
valid_email_regex = /\A[\w+\-.][email protected][a-z\d\-.]+\.[a-z]+\z/i
validates :value, presence: true,
length: { maximum: 256 },
format: { with: valid_email_regex },
uniqueness: { case_sensitive: false }
validates :user_id, presence: true
validates :email_dfn_id, presence: true
end
和
個class EmailDfn < ActiveRecord::Base
attr_accessible :short_description,
:long_description,
validates_uniqueness_of :short_description,
:long_description
has_many :emails
end
測試
require 'spec_helper'
describe Email do
let(:user) { FactoryGirl.create(:user) }
before { @email = user.emails.build(email_dfn_id: 1,
value: "[email protected]",
notes: "My personal email address") }
subject { @email }
it { should respond_to(:value) }
it { should respond_to(:notes) }
it { should respond_to(:email_dfn_id) }
it { should respond_to(:user_id) }
it { should respond_to(:user) }
its(:user) { should == user }
it { should be_valid }
describe "when user id is not present" do
before { @email.user_id = nil }
it { should_not be_valid }
end
describe "when email id is invalid" do
before { @email.email_dfn_id = 999 }
it { should_not be_valid }
end
end
在該當前的設置,最後一次試驗(設定email_dfn_id = 999,無效代碼)失敗。
您的解決方案可能是昂貴的,當'employees'表有行的1000,你會加載所有ID的內存和每個驗證執行數組查找。除此之外,在生產模式下,該解決方案不包括啓動服務器後添加的員工。 –
我不認爲這會很貴。我也不確定你的第二點是否正確。我試圖在生產模式下啓動我的控制檯,並且ModelName.all在創建新記錄時進行了更新。 – Max
這似乎工作要求ID是有效的,但我關心縮放。我用這種方法碰到的另一個問題是,檢查新行是否有效會失敗。 (見更新我的帖子上面...它應該{} be_valid是一個會在這種情況下失敗) – user1248862