Как создать связанные объекты (вы приняли параметры) для после сохранения в Rails? - PullRequest
0 голосов
/ 05 апреля 2011

Проблема, с которой я сталкиваюсь, заключается в том, что Продукт пытается создать варианты еще до того, как продукт будет создан, и существуют определенные обратные вызовы для вариантов, которые требуют существования продукта. Так как я могу переписать это так, чтобы v.save не выполнялся до тех пор, пока объект не будет создан или что-то в этом роде.

Product.class_eval do
  validates_presence_of [:style_no, :market_price, :designer, :short_description, :description]
  validates_numericality_of [:size_47_quantity,
                :size_46_quantity,
                :size_45_quantity,
                :size_44_quantity,
                :size_43_quantity,
                :size_42_quantity,
                :size_41_quantity,
                :size_40_quantity,
                :size_39_quantity]
  for i in 39..47
    define_method:"size_#{i}_quantity" do
      if v = self.variants.find_by_size(i)
        v.count_on_hand
      else
        0
      end
    end

    define_method:"size_#{i}_quantity=" do |amount|
      # if only there is some method that can postpone all the following if this product hasn't been created yet!
      self.id = Product.last.id + 1 unless self.id
      v = self.variants.find_by_size(i) || self.variants.new(:size => i)
      v.count_on_hand = amount
      v.save
    end
  end
end

Ответы [ 2 ]

1 голос
/ 05 апреля 2011

Вы можете попробовать это решение:

Класс продукта

class Product < ActiveRecord::Base
  validates_presence_of [:style_no, :market_price, :designer, :short_description, :description]
  has_many :variants

  # This method would check if variant was created or loaded.
  #
  # So many sequantial calls to it will return same object
  def variant_with_size(size)
     self.variants.select{|v| v.size == size}.first || self.variants.where('size = ?', size).first
  end

  module ClassExtensions
    def self.included(base)
      (39..47).each do |i|
        method = "size_#{i}_quantity".to_sym
        included_module = Module.new
        included_module.module_eval <<EOF
def #{method}
  if v = self.variant_with_size(#{i})
    v.count_on_hand
  else
    0
  end
end

def #{method}=(amount)
  v = self.variant_with_size(#{i}) || self.variants.build(:size => #{i})
  v.count_on_hand = amount
  v
end
EOF
        base.send :include, included_module
      end
    end
  end

  include ClassExtensions
end

Класс варианта

class Variant < ActiveRecord::Base
  belongs_to :product

  validates :count_on_hand, :numericality => true
end

Использование

Пример использования с правильным количеством варианта:

ruby-1.9.2-p180 :001 > p = Product.new
 => #<Product id: nil, style_no: nil, market_price: nil, designer: nil, short_description: nil, description: nil, created_at: nil, updated_at: nil> 
ruby-1.9.2-p180 :002 > p.size_39_quantity
 => 0 
ruby-1.9.2-p180 :003 > p.size_39_quantity = 2
 => 2 
ruby-1.9.2-p180 :004 > p.variants
 => [#<Variant id: nil, product_id: nil, size: 39, count_on_hand: 2, created_at: nil, updated_at: nil>] 
ruby-1.9.2-p180 :005 > p.save
 => true 
ruby-1.9.2-p180 :006 > p.variants
 => [#<Variant id: 3, product_id: 3, size: 39, count_on_hand: 2, created_at: "2011-04-06 06:34:46", updated_at: "2011-04-06 06:34:46">] 

Использование с неправильным количеством вариантов:

ruby-1.9.2-p180 :007 > p1 = Product.new
 => #<Product id: nil, style_no: nil, market_price: nil, designer: nil, short_description: nil, description: nil, created_at: nil, updated_at: nil> 
ruby-1.9.2-p180 :008 > p1.size_39_quantity = 'A'
 => "A" 
ruby-1.9.2-p180 :009 > p1.save
 => false 
ruby-1.9.2-p180 :010 > p1.errors
 => {:variants=>["is invalid"]} 
ruby-1.9.2-p180 :011 > p1.variants[0].errors
 => {:count_on_hand=>["is not a number"]} 
1 голос
/ 05 апреля 2011

На первый взгляд, я бы подумал об использовании обратного вызова after_save для Product для создания вариантов продукта.

Что-то вроде:

class Product < ActiveRecord::Base
  has_many :variants

  after_save :create_variants! if :not_a_variant?

  OPTIONS = [:size_1_qty, :size_2_qty] # TODO: move to a OptionType model associated with Product

  def not_a_variant?
    size.nil? # or however you might distinguish a Product from a Variant
  end

  private
    def create_variants!
      # OPTIONS could instead be related option_types. perhaps a 'size' option type with values of 40, 41, 42, etc.
      OPTIONS.each do |size|
        variants.build(...)
      end
      save!
    end
end

Я только что рассматривал проект корзины покупок Spree от Rails Dog, и они аналогичным образом обрабатывают варианты продуктов. Вы можете проверить это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...