0

我对 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 或更多产品。

在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. 首先我的代码抛出一个错误:PromotionsController#new 中的 ArgumentError 没有为名称“产品”找到关联。已经定义了吗?如果我将 PromoProduct 模型中的行更改为:accepts_nested_attributes_for :products 为 accept_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
    

    我该怎么办?

谢谢你。

4

1 回答 1

1
  1. You shouldn't put the "accept_nested_attribute_for" in the association table PromoProducts. It should exist in the model that you want to use for creating association to another model. "accept_nested_attribute_for" IIRC simply inserts an "[association]_attributes=" method for your model. For instance, if you add this method to your Product class for Promotion, you will get "promotion_attributes=" method inserted in the Product class. Then a nested form can use this function to create new objects with a hash that represents the model and association.

  2. Base on the above, the create action shouldn't be in PromoProduct controller, instead it should be in Promotion controller.

    <%= 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 %>
    

I don't know without trying if the above collection_select line is correct. But you can debug this by checking the parameter returned by the form to the controller in the server console log. Basically you should see a nested hash of

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

Let me know if you need more help on this. In my solution I used a combination of select_tag and options_from_collection_for_select. (But I don't recall the behavior of all these offhand without looking at the API doc.)

Lastly, do you need the :through model? I think since you created the through model you need to handle saving that in your create action. But since you don't have other attributes on the PromoProducts table I wonder if you want to simply leave it as a HABTM association and let rails deal with the rest?

于 2012-07-03T06:10:44.133 回答