验证多态父级的存在

dch*_*cke 14 polymorphism ruby-on-rails associations

我正在使用以下模型开发Rails 3.2应用程序:

class User < ActiveRecord::Base
  # Associations
  belongs_to :authenticatable, polymorphic: true

  # Validations
  validates :authenticatable, presence: true # this is the critical line
end

class Physician < ActiveRecord::Base
  attr_accessible :user_attributes

  # Associations
  has_one :user, as: :authenticatable
  accepts_nested_attributes_for :user
end
Run Code Online (Sandbox Code Playgroud)

我要做的是验证用户是否始终拥有可验证的父级.这本身很好用,但在我的形式中,用户模型抱怨不存在authenticatable.

我正在使用以下控制器为新医生显示一个表单,该表单接受用户的嵌套属性:

def new
  @physician = Physician.new
  @physician.build_user

  respond_to do |format|
    format.html # new.html.erb
    format.json { render json: @physician }
  end
end
Run Code Online (Sandbox Code Playgroud)

这是我的创建方法:

def create
  @physician = Physician.new(params[:physician])

  respond_to do |format|
    if @physician.save
      format.html { redirect_to @physician, notice: 'Physician was successfully created.' }
      format.json { render json: @physician, status: :created, location: @physician }
    else
      format.html { render action: "new" }
      format.json { render json: @physician.errors, status: :unprocessable_entity }
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

在提交表单时,它表示用户的authenticable不能为空.但是,一旦@physician保存,就应该分配authenticatable_id和authenticatable_type .如果我使用相同的表格来编辑医生及其用户,它可以正常工作,从那时起就分配了id和类型.

我在这做错了什么?

kon*_*ung 8

我相信这是预期的:

https://github.com/rails/rails/issues/1629#issuecomment-11033182(最后两条评论).

还可以从rails api中查看:

一对一协会

将对象分配给has_one关联会自动保存该对象和要替换的对象(如果有),以便更新其外键 - 除非父对象未保存(new_record?== true).

如果其中任何一个保存失败(由于其中一个对象无效),则会引发ActiveRecord :: RecordNotSaved异常并取消分配.

如果您希望将对象分配给has_one关联而不保存它,请使用build_association方法(如下所示).仍将保存要替换的对象以更新其外键.

将对象分配给belongs_to关联不会保存对象,因为外键字段属于父对象.它也没有保存父母.

还有这个

build_association(attributes = {})返回已使用属性实例化并通过外键链接到此对象但尚未保存的关联类型的新对象.

你必须先创建一个Parent.然后将它的id分配给多态对象.

从我所看到的,你创建了一个对象Physician.new,它构建了User,但此时它尚未保存,所以它没有id,因此没有任何内容可以分配给多态对象.因此,验证将始终失败,因为它在保存之前被调用.

换句话说:在您调用build_user的情况下,它返回User.new NOT User.create.因此,authenticatable没有分配authenticatable_id.

你有几个选择:

  • 首先保存关联用户.

    要么

  • 将验证移至after_save回调(可能但非常烦人和坏)

    要么

  • 更改您的应用程序结构 - 也许避免多态关联并切换到has_many通过?由于我不了解内部和业务要求,因此我很难判断.但在我看来,这不是多态关联的良好候选者.你会有更多的模型,而不仅仅是可验证的用户吗?

恕我直言,多态关联的最佳候选者是电话,地址等等.地址可以属于用户,客户,公司,组织,Area51等,是家庭,航运或计费类别,即它可以MORPH以适应多种用途,所以它是一个提取的好对象.但Authenticatable在我看来有点做作,并且在不需要的地方增加了复杂性.我没有看到任何其他对象需要可以进行身份​​验证.

如果您可以展示您的Authenticatable模型以及您的推理和迁移(?),我可以为您提供更多建议.现在我只是凭空捏捏这个:-)但它似乎是重构的一个很好的候选者.


Cyr*_*rus -1

我不确定这是否可以解决您的问题,但在验证多态父项是否存在时我使用类似的方法。

这是我在video具有parent多态关联的模型中使用的一些代码。这进去了video.rb

  validates_presence_of :parent_id, :unless => Proc.new { |p|
      # if it's a new record and parent is nil and addressable_type is set
      # then try to find the parent object in the ObjectSpace
      # if the parent object exists, then we're valid;
      # if not, let validates_presence_of do it's thing
      # Based on http://www.rebeccamiller-webster.com/2011/09/validate-polymorphic/
      if (new_record? && !parent && parent_type)
        parent = nil
        ObjectSpace.each_object(parent_type.constantize) do |o|
          parent = o if o.videos.include?(p) unless parent
        end
      end
      parent
    }
Run Code Online (Sandbox Code Playgroud)