2012-07-03 56 views
0

我有点新的Rails,这是我的第一篇文章StackOverflow。Rails嵌套窗体与has_many:通过,不保存数据加入表

说我有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个或更多产品,具体取决于促销类型。

在promotions_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. 首先我的代码抛出一个错误: 引发ArgumentError在PromotionsController#新 没有相关发现名称'产品。它已被定义? 如果我更改了PromoProduct模型行: accepts_nested_attributes_for:产品 到 accepts_nested_attributes_for:产品 然后有没有错误,一切工作正常。
  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 
    

    我该如何解决这个问题?

谢谢。

+0

我设法解决了这个问题。遵循这里给出的想法[链接](https://gist.github.com/2820294)。似乎工作正常。从promo_product创建操作中删除创建,并通过促销创建操作使其生效。 –

回答

1
  1. 您不应该在关联表PromoProducts中放置“accept_nested_attribute_for”。它应该存在于您想要用于创建与其他模型的关联的模型中。 “accept_nested_attribute_for”IIRC只需为模型插入一个“[association] _attributes =”方法。例如,如果您将此方法添加到您的Product类以获取Promotion,则会在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 %> 
    

我不知道,但不尝试如果上述collection_select线是正确的。但是,您可以通过在服务器控制台日志中检查表单返回给控制器的参数来进行调试。基本上你应该看到一个嵌套的散列

{:promotion => {:products => ...}} 

让我知道你是否需要更多的帮助。在我的解决方案中,我使用了select_tag和options_from_collection_for_select的组合。 (但是,我不记得API文档时没有记住所有这些行为。)

最后,你需要:through模型吗?我认为,因为你创建了你需要处理的通过模型保存你的创建行动。但是由于PromoProducts表上没有其他属性,我不知道你是否想简单地将它作为HABTM关联关系,让rails来处理其余的关系?