Ruby on rails Rails:如何通过另一个模型为我的模型中的字段指定有效值

Ruby on rails Rails:如何通过另一个模型为我的模型中的字段指定有效值,ruby-on-rails,model,controller,Ruby On Rails,Model,Controller,我要做的是定义一个有效值列表,然后在相关表上添加新角色时根据该值列表进行验证 让我举一个具体的例子: 假设我有一个“就业”表,其中包含以下字段: user_id (tied to a user table) employer_id (tied to an employer table) position_id (tied to a position table) details efbegdt efenddt 当用户向该表添加新行时,我希望确保雇主id和职位id已经存在于其他表中,并且如果两种

我要做的是定义一个有效值列表,然后在相关表上添加新角色时根据该值列表进行验证

让我举一个具体的例子:

假设我有一个“就业”表,其中包含以下字段:

user_id (tied to a user table)
employer_id (tied to an employer table)
position_id (tied to a position table)
details
efbegdt
efenddt
当用户向该表添加新行时,我希望确保雇主id和职位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
这个问题是否有“最佳实践”解决方案

更新

只是添加了另一个示例,详细介绍了所有设置。在本例中,用户可以在电子邮件表中存储多个电子邮件地址,但每种类型(个人、工作、学校等)只能有一个地址。另一个表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
require 'spec_helper'

describe Email do

  let(:user) { FactoryGirl.create(:user) }
  before { @email = user.emails.build(email_dfn_id: 1,
                                      value: "personal_email@test.com",
                                      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\u dfn\u id=999,无效代码)失败。

validates :employer_id, presence: true, inclusion: { in: Employer.all.map(&:id), message: 'must be a valid employer.' }

我假设在您的关系设置中,用户属于雇主

在这种情况下,也许可以使用关联方法简化代码

如果使用默认的表单生成器

<% f.select("user", "employer_id", Employer.all) %> 
<> P>如果更多的涉及授权和安全,你可以考虑像Geave>代码> CCAN(<代码)>来缩小用户可以选择的选择范围。

用于授权和其他权限目的

例如,您不希望人们选择“禁用”/“预览”条目。如果你使用cancan,你可以有这样的方法

<% f.select("user", "employer_id", Employer.accessible_by(current_ability) %> 


如果他们的能力被正确定义,试图欺骗系统将以拒绝访问而告终

使用
validate\u关联

class Employment < ActiveRecord::Base
  belongs_to :employee

  validates_associated :employee
end
class Employment

阅读文档。

employees
表有1000行时,您的解决方案可能会很昂贵,因为您将把所有id加载到内存中,并为每次验证执行数组查找。除此之外,在生产模式下,此解决方案将不包括启动服务器后添加的员工。我认为它不会那么昂贵。我也不确定你的第二点是否正确。我尝试在生产模式下启动我的控制台,ModelName.all在创建新记录时进行了更新。这似乎要求ID有效,但我担心扩展。我用这种方法遇到的另一个问题是,检查新行是否有效返回为失败。(请参阅上面我的帖子的更新…{should'u valid}在这种情况下将失败)模型。所有调用都不会捕获我所指的场景。加载类时,验证列表将初始化。一旦初始化,列表就不会更改。您可以通过尝试根据添加到数据库的新行验证代码来测试这一点。如果要为每次验证重新计算列表,则必须提供
lambda
,而不是静态列表。谢谢。我不知道。当定义静态列表不起作用时,我确实使用lambdas进行了一些验证,但我不知道为什么会起作用。我也考虑过这样做。。。我用这种方法遇到的问题是,以下rspec测试将失败:'描述“当雇主id无效时”在{@employment.employer\u id=999}之前执行{should\u invalid}end'如果雇主id=999当前不是雇主表上的有效id,您应该在测试之前预先创建一个
雇主
。是的-我也这样做了,虽然我在一些初始工作中没有意识到,我已经将自动递增id设置为比我想象的更高的数字。重置测试数据库后,我能够根据正确的ID 1进行验证(请参阅主帖子中的新示例),但这种“validates_associated”设置似乎仍然不足以要求验证ID是否有效(即,ID=999的测试仍然返回“valid”,但它不应该返回),这是一个好主意,但正如您所说,如果可能的话,我真的希望通过关联方法来实现这一点。我在主要帖子中发布了另一个(非常具体的)例子——你有没有想过为什么当前的设置没有强制要求电子邮件的ID必须是“有效的”,正如email_dfn模型所定义的那样?
require 'spec_helper'

describe Email do

  let(:user) { FactoryGirl.create(:user) }
  before { @email = user.emails.build(email_dfn_id: 1,
                                      value: "personal_email@test.com",
                                      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
validates :employer_id, presence: true, inclusion: { in: Employer.all.map(&:id), message: 'must be a valid employer.' }
<% f.select("user", "employer_id", Employer.all) %> 
def create
  @user = User.new(params[:id])
  @user.employer = Employer.find(params[:user][:employer_id])

  ..... # Standard save code or your own. 

end
<% f.select("user", "employer_id", Employer.accessible_by(current_ability) %> 
class Employment < ActiveRecord::Base
  belongs_to :employee

  validates_associated :employee
end