Rails 5 - find_or_create_by,带有嵌套属性

时间:2021-09-19 01:16:30

I'm trying to create a new object with its associated records in the same form but would like the associated records to use find_or_create_by instead of just create (as the associated model records may and most of the time already will exist). I have spent the last two days digging through every post and article that I can find related to this subject trying to get this to work but still my form tries to create the new object only, not search for existing.

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

Models

楷模

#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

Form

形成

orders/new.html.erb

订单/ 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 partial

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>

Controller

调节器

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

So as you can see in the new action, I instantiate the new Order, build its associated property (its what the Order belongs_to), build the new order_owner relationship, and build the owner for that relationship. Then on submit it creates the order via @properties.orders.create(order_params).

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

The error that I get is "Order owners owner name already exists." so clearly its not looking up an owner by name. I have tried:

我得到的错误是“订单所有者所有者名称已存在”。很明显它没有按名称查找所有者。我努力了:

  • Redefining autosave_associated_records_for_owner in order.rb and order_owner.rb, using both belongs_to and has_many variations, but it seems like they never get called so I must be doing something wrong. (I have tried variations of almost every answer I could find on SO)
  • 使用belongs_to和has_many变体重新定义order.rb和order_owner.rb中的autosave_associated_records_for_owner,但似乎它们永远不会被调用,所以我必须做错事。 (我已尝试过几乎所有可以在SO上找到的答案的变体)
  • before_add: callback on both has_many :owners, through: :order_owners and has_many :order_owners in order.rb.
  • before_add:对has_many:owner的回调,通过:: order_owners和has_many:order_owners in order.rb。
  • Extending has_many :owners and has_many :owners, through: :order_owners in order.rb as well as belongs_to :owner in order_order.rb
  • 扩展has_many:owner和has_many:owner,通过:: order_owners in order.rb以及belongs_to:owner in order_order.rb

I've also tried different variations of calling associations and such within the form so I must be just misunderstanding something. I'm also using Cocoon to manage the nested forms but I've talked to the author on unrelated issues and Cocoon is essentially just a view helper for nested forms so the solution must something in the models/controller.

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

Any and all ideas welcome. Thanks in advance.

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

P.s. I left code in the controller actions that may/may not pertain to this exact post but I wanted to show the entire action for completeness. If it matters, I manually set the owners select via AJAX when a property is selected in another field. Basically it just looks up the property and adds existing owners to the owners select.

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

1 个解决方案

#1


1  

The owner is nested inside the order. So when you call order.save, it runs all validations (including owner's). If you want to use find_or_create_by you need to do it inside a before_save, so you can make modifications to the owner before the validation hits.

所有者嵌套在订单中。因此,当您调用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

Further customization may be needed depending on your form and business logic, but that's the main concept.

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

#1


1  

The owner is nested inside the order. So when you call order.save, it runs all validations (including owner's). If you want to use find_or_create_by you need to do it inside a before_save, so you can make modifications to the owner before the validation hits.

所有者嵌套在订单中。因此,当您调用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

Further customization may be needed depending on your form and business logic, but that's the main concept.

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