Rails嵌套表单使用has_many:through,而不是将数据保存到连接表

时间:2012-07-03 05:45:16

标签: ruby-on-rails

我是Rails的新手,这是我在StackOverflow上的第一篇文章。

说我有3个型号:

class Product < ActiveRecord::Base
  default_scope :order => :title
  has_many :line_items
  has_many :promo_products
  has_many :promotions, :through => :promo_products, :foreign_key => :promotion_id
  before_destroy :ensure_not_referenced_by_any_line_item
  before_destroy :ensure_not_referenced_by_any_promo_product

  validates :title, :presence => true, :uniqueness => true
  validates :description, :presence => true
  validates :price, :numericality => {:greater_than_or_equal_to => 0.01}
  private
  def ensure_not_referenced_by_any_line_item
    if line_items.empty?
      return true
    else
      errors.add(:base, 'Line Items present')
      return false
    end
  end
  def ensure_not_referenced_by_any_promo_product
    if promo_products.empty?
      return true
    else
      errors.add(:base, 'Some promotions are still in effect')
      return false
    end
  end
end

class Promotion < ActiveRecord::Base

  CART_OR_PRODUCT = ['Cart', 'Product']
  PROMOTION_TYPE = ['Percentage based', 'Value based']

  has_many :promo_products
  accepts_nested_attributes_for :promo_products
  has_many :products, :through => :promo_products, :foreign_key => :product_id
  accepts_nested_attributes_for :products
  #attr_accessible :promo_products_attributes, :title, :description, :cart_or_product, :promotion_type, :discount, :minimum_price, :minimum_quantity

  validates :title, :description, :presence => true
  validates :cart_or_product, :inclusion => {:in => CART_OR_PRODUCT, :message =>
  "is invlaid. Please select a valid option"}
  validates :promotion_type, :inclusion => {:in => PROMOTION_TYPE, :message =>
  "is invalid. Please select a valid option"}
  validates :discount, :minimum_price, :numericality => {:greater_than_or_equal_to => 0.00}
  validates :minimum_quantity, :numericality => {:greater_than_or_equal_to => 0}

end

class PromoProduct < ActiveRecord::Base

  belongs_to :promotion
  belongs_to :product
  accepts_nested_attributes_for :products
end

在促销新页面中,我想展示可能属于促销活动的产品列表。用户可以选择0个,1个或更多产品,具体取决于促销类型。

在promotion_controller的新动作中,我构建如下:

@promotion.promo_products.build.build_product

在_form促销活动中,我需要显示供用户选择的产品列表。我做了一个嵌套的形式,如:

<%= form_for(@promotion) do |f| %>
  <!-- other promotion fields -->
  <%= f.fields_for :promo_products do |pp| %>
    <%= pp.fields_for :products do |p| %>
      <div class="field">
        <%= f.label "Products" %><br />
        <%= collection_select :promo_product, :product_id, Product.all, :id, :title {:selected => @promotion.product_ids}, {:multiple => true} %>
      </div>
    <% end %>
  <% end %>
<% end %>

我有2个问题。

  1. 首先我的代码抛出一个错误: PromotionsController中的ArgumentError #new 找不到名称“产品”的关联。它已经定义了吗? 如果我更改PromoProduct模型中的行: accepts_nested_attributes_for:products 至 accepts_nested_attributes_for:product 然后没有错误,一切正常。
  2. 数据未保存到promo_product表。我在promo_product控制器中创建了动作:

    def create
      @promotion = current_promotion
      products = Product.select(:id => params[:product_id])
      products.each do |p|
        promo_product = @promotion.promo_products.build(p)
        promo_product.save
      end
      #@promo_product = PromoProduct.new(params[:promo_product])
      redirect_to promotions_path
    end
    

    我该怎么办呢?

  3. 谢谢。

1 个答案:

答案 0 :(得分:1)

  1. 您不应将“accept_nested_attribute_for”放在关联表PromoProducts中。它应该存在于您要用于创建与另一个模型的关联的模型中。 “accept_nested_attribute_for”IIRC只是为您的模型插入“[association] _attributes =”方法。例如,如果将此方法添加到Promotion类的Product类中,则会在Product类中插入“promotion_attributes =”方法。然后,嵌套表单可以使用此函数创建具有表示模型和关联的哈希的新对象。

  2. 基于以上所述,创建操作不应该在PromoProduct控制器中,而应该在Promotion控制器中。

    <%= form_for(@promotion) do |f| %>
      <!-- other promotion fields -->
      <%= f.fields_for :products do |pp| %>
        <div class="field">
          <%= f.label "Products" %><br />
          <%= collection_select :promo_product, :product_id, Product.all, :id, :title {:selected => @promotion.product_ids}, {:multiple => true} %>
        </div>
      <% end %>
    <% end %>
    
  3. 如果上面的collection_select行正确无误,我不知道。但是,您可以通过将表单返回的参数检查到服务器控制台日志中的控制器来进行调试。基本上你应该看到

    的嵌套哈希
    {:promotion => {:products => ...}}
    

    如果您需要更多帮助,请与我们联系。在我的解决方案中,我使用了select_tag和options_from_collection_for_select的组合。 (但是,如果不查看API文档,我不记得所有这些行为的行为。)

    最后,你需要:通过型号吗?我想想,因为您创建了直通模型,您需要在创建操作中保存它。但是,由于您在PromoProducts表上没有其他属性,我想知道您是否只想将其作为HABTM关联并让rails处理其余的?