在创建之前验证关联对象存在

时间:2014-08-17 01:39:46

标签: ruby-on-rails validation

我一直在关注Getting Started rails教程,现在正在尝试一些自定义功能。

我有2个模型,PersonHangoutPerson可以包含多个Hangouts。创建Hangout时,必须选择Person并与新Hangout相关联。但是,当我调用create动作时,我遇到了问题。这会在validate_presence_of之前触发。

我是以错误的方式来做这件事的吗?似乎我不应该创建自定义before_create验证,以确保使用Hangout创建Person

#hangout_controller
def create
  @person = Person.find(params[:hangout][:person_id])

  @hangout = @person.hangouts.create(hangout_params)
  @hangout.save

  redirect_to hangouts_path(@hangout)
end

#hangout.rb
class Hangout < ActiveRecord::Base
  belongs_to :person

  validates_presence_of :person 
end

#person.rb
class Person < ActiveRecord::Base
  has_many :hangouts

  validates :first_name, presence: true
  validates :met_location, presence: true
  validates :last_contacted, presence: true

  def full_name
    "#{first_name} #{last_name}"
   end
end

3 个答案:

答案 0 :(得分:2)

  

在人员的validate_presence_of之前触发创建操作

我认为你对rails MVC感到困惑。 您的表单包含一个网址,当您提交表单时,您的表单参数将根据您在routes.rb中定义的路由发送到您的控制器操作您的控制器操作,在这种情况下创建操作,与模型交互,这是检查您的验证,如果所有验证都通过,您的对象将保存在数据库中,所以即使在您的应用程序中控件首先传递给您的控制器,但您的对象是如果所有验证都通过,则只保存一次。

Rails MVC


现在让我们回到你的代码。有几件事你做错了

一个。 您无需单独关联您的人员

在你的创作中你有这一行:

@person = Person.find(params[:hangout][:person_id])

您不需要这样做,因为您的person_id已经来自您的表单,它会自动将您的视频群聊与此人关联。

您正在调用create方法而不是build:

当你调用.association.create方法时,它会为你做两件事,它首先初始化你的对象,在你的情况下是你的环聊,如果所有的验证都被传递,它会保存它。如果未传递所有验证,则只需回滚查询。

如果您使用.association.build,它只会使用来自您表单的参数初始化您的对象

℃。 验证错误不会显示:

如上所述,由于您正在调用create方法而不是构建,因此您的验证错误将不会显示。


<强>修正

您的create方法应如下所示:

def create
  @hangout = Hangout.new(hangout_params) # since your person_id is coming from form it'll automatically associate your new hangout with person
  if @hangout.save
    redirect_to hangouts_path(@hangout)
  else
    render "new"  # this will show up validation errors in your form if your hangout is not saved in database
  end
end

private

  def hangout_params
    params.require(:hangout).permit(:person_id, :other_attributes)
  end

答案 1 :(得分:0)

您对控制器和模型职责感到困惑。

让我试着解释一下我认为令你困惑的事情:

首先在rails console

中尝试此操作
Hangout.create

它不应该让你,因为你没有将Person对象传递给create方法。因此,我们确认验证工作正常。该验证意味着在创建Hangout之前,请确保存在person属性。所有这些都是在模型级别,对控制器一无所知!

让我们去控制器部分。当控制器的创建动作被触发时,该控制器根本不知道你要做什么。它没有运行任何验证。这只是一个动作,如果你愿意,可以调用Hangout模型来创建其中一个。

我相信当你说“它发射”时您是说create的{​​{1}}操作首先调用HangoutController create模式上的Hangout方法。这完全没问题。验证在模型级别运行。

答案 2 :(得分:0)

嵌套属性

我认为您最好使用accepts_nested_attributes_for - 我们已经通过在嵌套模型上使用验证来实现您之前寻求的功能(尽管您已经能够使用reject_if :: all_blank):

#app/models/person.rb
Class Person < ActiveRecord::Base
   has_many :hangouts
   accepts_nested_attributes_for :hangouts, reject_if: :all_blank
end

#app/models/hangout.rb
Class Hangout < ActiveRecord::Base
   belongs_to :person
end

这将使您能够调用reject_if: :all_blank方法 -

  

传递:all_blank而不是Proc将创建一个proc   拒绝所有属性为空的记录,不包括任何值   为_destroy。

-

这意味着您可以创建以下内容:

#config/routes.rb
resources :people do
   resources :hangouts # -> domain.com/people/:people_id/hangouts/new
end

#app/controllers/hangouts_controller.rb
Class HangoutsController < ApplicationController
   def new
      @person = Person.find params[:people_id]
      @hangout = @person.hangouts.build
   end

   def create
      @person = Person.find params[:people_id]
      @person.update(hangout_attributes)
   end

   private

   def hangout_attributes
       params.require(:person).permit(hangouts_attributes: [:hangout, :attributes])
   end
end

虽然我没有测试过上述内容,但我相信这是你应该处理的方式。这基本上会保存特定Hangout的{​​{1}}关联对象 - 允许您拒绝Person关联对象为空

观点如下:

Hangout