Rails 5 - find_or_create_by,带有嵌套属性

时间:2017-11-24 21:40:35

标签: ruby-on-rails ruby-on-rails-5 nested-forms nested-attributes

我试图以相同的形式创建一个新对象及其相关记录,但希望相关记录使用find_or_create_by而不是仅创建(因为关联的模型记录可能和大多数时间已经存在)。我花了最近两天的时间挖掘了我能找到的与此主题相关的每篇帖子和文章,试图让它工作但是我的表单只是尝试创建新对象,而不是搜索现有对象。

模型

#order.rb
has_many :order_owners, dependent: :destroy, inverse_of: :order
has_many :owners, through: :order_owners

accepts_nested_attributes_for :order_owners

#owner.rb
has_many :order_owners, dependent: :destroy
has_many :orders, through: :order_owners
validates :name, presence: true, uniqueness: { case_sensitive: false } 

#order_owner.rb
belongs_to :owner
belongs_to :order

accepts_nested_attributes_for :owner

表格

orders/new.html.erb

<%= bootstrap_form_for(@orders, layout: :horizontal, label_col: "col-sm-2", control_col: "col-sm-6") do |f| %>
  <%= render 'shared/error_messages', object: f.object %>

  ...

  <%= f.fields_for :order_owners do |orderowner| %>

    <%= render 'orders/new_order_owners', f: orderowner, render_partial: 'orders/new_order_owners' %>

  <% end %>

  ...

  <%= f.form_group do %>
    <%= f.submit "Create Order", class: "btn btn-primary" %>
    <%= link_to_add_association fa_icon("plus", text: "Add Owner"), f, :order_owners,
                                  class: "btn btn-outline pull-right #{orderBtnDisable(@properties)}", partial: "orders/new_order_owners", id: "newOrderOwnerAdd" %>
  <% end %>
<% end %>

orders/new_order_owners部分

<div class="m-t-md m-b-md border-bottom form-horizontal nested-fields">

  <%= link_to_remove_association(fa_icon("remove", text: ""), f, { class: "btn btn-danger btn-outline pull-right" }) %>

  <% f.object.build_owner unless f.object.owner %>

  <%= f.fields_for :owner do |owner| %>
    <%= owner.select :name, options_from_collection_for_select(@owners, "name", "name"),
               { label: "Name:", include_blank: true }, { id: "orderPropOwnerSelect", data: { placeholder: "Select an existing Owner or type a new one.."} } %>
  <% end %>

</div>

控制器

orders/new

def new
  @order = Order.new
  @order.build_property
  @order.order_owners.build.build_owner
  @properties = Property.find_by_id(params[:property_id])
  if @properties
    @owners = @properties.owners
  else
    @owners = []
  end
  respond_to do |format|
    format.html
    format.js
  end
end

orders/create

def create
  @properties = Property.find(params[:order][:property_id])
  @order = @properties.orders.create(order_params)
  respond_to do |format|
    format.html { if @order.save
                    if params[:order][:owners_attributes]
                      order_prop_owner_check(@order, @properties)
                    end
                    flash[:success] = "Order created successfully!"
                    redirect_to property_order_path(@properties, @order)
                  else
                    @properties
                    @owner = @properties.owner
                    render 'new'
                  end
    }
    format.js {
                  if @order.save
                    flash.now[:success] = "Order Updated Successfully!"
                  else
                    flash.now[:danger] = @order.errors.full_messages.join(", ")
                  end
    }
  end
end

因此,您可以在new操作中看到,我实例化新订单,构建其关联属性(订单所属的属性),构建新的order_owner关系,并构建该关系的所有者。然后在提交时,它通过@ properties.orders.create(order_params)创建订单。

我得到的错误是&#34;订单所有者所有者名称已经存在。&#34;很明显它没有按名称查找所有者。 我尝试过:

  • 使用belongs_to和has_many变体重新定义order.rb和order_owner.rb中的autosave_associated_records_for_owner,但似乎它们永远不会被调用,所以我必须做错事。 (我已经尝试了几乎所有我能找到的答案的变体)
  • 在order.rb中,{li> before_add:回调has_many :owners, through: :order_ownershas_many :order_owners
  • 在order.rb中扩展has_many :ownershas_many :owners, through: :order_owners以及在order_order.rb中扩展belongs_to :owner

我也在表单中尝试过不同的调用关联变体,所以我一定只是误解了一些东西。我也使用Cocoon来管理嵌套表单,但我已经就不相关的问题与作者进行了交谈,Cocoon本质上只是嵌套表单的一个视图助手,因此解决方案必须在模型/控制器中使用。

欢迎任何和所有想法。提前谢谢。

P.S。我在控制器操作中留下了代码,这些操作可能/可能不属于这个确切的帖子,但我想展示整个操作的完整性。如果重要,我在另一个字段中选择属性时手动设置所有者通过AJAX选择。基本上它只是查找属性并将现有所有者添加到所有者选择。

1 个答案:

答案 0 :(得分:1)

所有者嵌套在订单中。因此,当您调用order.save时,它会运行所有验证(包括所有者)。如果您想使用find_or_create_by,则需要在before_save内进行,因此您可以在验证点击之前对所有者进行修改。

#order.rb

before_save :find_or_create_owner

def find_or_create_owner
  self.order_owners.each do |order_owner|
    order_owner.owner = Owner.find_or_create_by(name: final_owner.name)
  end
end

根据您的形式和业务逻辑,可能需要进一步定制,但这是主要概念。