如何在Rails验证中验证外键?

时间:2013-09-21 15:46:16

标签: ruby-on-rails ruby ruby-on-rails-3 activerecord

在我的Rails应用中,我users可以拥有多个projects,而invoices可以有多个class Invoice < ActiveRecord::Base attr_accessible :number, :date, :project_id validates :project_id, :presence => true, :inclusion => { :in => ????????? } end

如何确保用户只能为他的项目之一而不是为其他用户的项目创建发票?

class InvoicesController < ApplicationController

  def new  
    @invoice = current_user.invoices.build(:project_id => params[:project_id])
  end

  def create
    @invoice = current_user.invoices.build(params[:invoice])    
    if @invoice.save
      flash[:success] = "Invoice saved."
      redirect_to edit_invoice_path(@invoice)
    else
      render :new
    end
  end

end

感谢您的帮助。


{{1}}

3 个答案:

答案 0 :(得分:1)

我认为不应该进行验证。您应该确保用户选择的项目是他的项目之一。

您可以在控制器上执行以下操作:

project = current_user.projects.find params[:project_id]
@invoice = Invoice.new(project: project)
# ...

您的创建操作可能看起来像这样。

  def create
    @invoice = current_user.invoices.build(params[:invoice])
    @invoice.project = current_user.projects.find params[:invoice][:project_id]
    if @invoice.save
      flash[:success] = "Invoice saved."
      redirect_to edit_invoice_path(@invoice)
    else
      render :new
    end
  end

答案 1 :(得分:1)

project_id是敏感的&#34;属性 - 所以从attr_accessible中删除它。你是对的,你不应该相信表格中的参数,你必须检查它。

def create
  @invoice = current_user.invoices.build(params[:invoice])
  # @invoice.project_id is nil now because this attr not in attr_accessible list
  @invoice.project_id = params[:invoice][:project_id] if current_user.project_ids.include?(params[:invoice][:project_id])
  if @invoice.save
    flash[:success] = "Invoice saved."
    redirect_to edit_invoice_path(@invoice)
  else
    render :new
  end
end

如果用户试图破解您的应用并将project_id更改为非拥有值,则方法create会使部分new无效@invoice不要忘记将project_id验证留在

如果您遇到异常Can't mass-assign protected attributes...,有几种方法可以做。最简单的方法是: 1.从环境配置中删除线(开发,测试,生产)

# Raise exception on mass assignment protection for Active Record models
config.active_record.mass_assignment_sanitizer = :strict

2。在分配之前拒绝来自参数的敏感参数。

# changes in method create
def create
  project_id = params[:invoice].delete(:project_id)
  @invoice = current_user.invoices.build(params[:invoice])
  @invoice.project_id = project_id if current_user.project_ids.include?(project_id)
  ...
end

答案 2 :(得分:0)

好的,幸运的是,这次我设法找到了我自己的解决方案。

我没有对我的控制器进行任何更改(“让我们保持'瘦”),但是我的模型添加了验证方法:

class Invoice < ActiveRecord::Base

  attr_accessible :number, :date, :project_id

  validates :project_id,  :presence     => true,
                          :numericality => { :only_integer => true },
                          :inclusion    => { :in => proc { |record| record.available_project_ids } }

  def available_project_ids
    user.project_ids
  end

end

我不确定这是好的还是坏的编码习惯。也许有人可以对此有所了解。但目前对我来说似乎很安全,到目前为止我还没能以任何方式破解它。