Дублиране на запис и това са деца - но децата се изтриват от стария запис

Моят проблем е подобен на: Моят метод за клониране е кражба на децата от оригиналния модел

Но изглежда не мога да намеря решението за това да работи с моя. Опитвам се да създам формуляр за размяна на поръчки, който включва попълване на формуляра с детайлите на стария запис. Така че, когато запазя формуляра, той създава нов запис на поръчка, но децата изглежда се изтриват от стария запис на поръчка и се прехвърлят в новия.

Ето кода:

def new
 @old_order = Order.includes(:line_items).find(params[:id])
 @order = Order.new @old_order.attributes 
 @order.line_items = []
 @old_order.line_items.each do |old|
   new = old.dup    # the line_item id is set before creation. 
   new.order_id = @order.id
   new.save!

   @order.line_items << new
   @old_order.line_items << old   # this was to see if the old line_items would reappend to the old order. Didn't help...
 end
end

def create
 @order = Order.new(exchange_order_params)
 if @order.save
   @order.update_attributes!(stage: 2, ordered_at: Date.today)
   redirect_to admin_returns_url, notice: "Order moved to 'accepted' for processing"
 else
   flash.now[:alert] = "Please try again"
   render :action => "new"
 end
end

private
  def exchange_order_params
  params.require(:order).permit(:id, :user_id,
                 line_items_attributes: [:id, :order_id, :cart_id, :quantity, :_destroy, 
                 product_attributes: [:id, :sku, :euro_price, :sterling_price, :product_group_id, :product_size_id, :product_waistband_id]])
end

Schema.rb

create_table "orders", force: :cascade do |t|
    t.datetime "created_at",                         null: false
    t.datetime "updated_at",                         null: false
    t.boolean  "returned",           default: false
    t.date     "date_sent"
    t.date     "ordered_at"
    t.integer  "user_id"
    t.boolean  "return_requested",   default: false
    t.integer  "stage",              default: 0
    t.decimal  "order_total",        default: 0.0
    t.string   "transaction_secret"
    t.string   "token"
    t.string   "uuid"
    t.string   "currency"
    t.float    "discounted_by",      default: 0.0
  end

  add_index "line_items", ["cart_id"], name: "index_line_items_on_cart_id", using: :btree
  add_index "line_items", ["order_id"], name: "index_line_items_on_order_id", using: :btree
  add_index "line_items", ["product_id"], name: "index_line_items_on_product_id", using: :btree

  create_table "line_items", force: :cascade do |t|
    t.integer  "quantity"
    t.integer  "order_id"
    t.integer  "cart_id"
    t.integer  "product_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.float    "unit_price"
    t.string   "currency"
  end



  create_table "product_groups", force: :cascade do |t|
    t.string   "name"
    t.text     "description"
    t.datetime "created_at",  null: false
    t.datetime "updated_at",  null: false
  end

  create_table "product_sizes", force: :cascade do |t|
    t.string   "specification"
    t.datetime "created_at",    null: false
    t.datetime "updated_at",    null: false
  end

  create_table "product_waistbands", force: :cascade do |t|
    t.string   "specification"
    t.datetime "created_at",    null: false
    t.datetime "updated_at",    null: false
  end

  create_table "products", force: :cascade do |t|
    t.integer  "sku"
    t.integer  "product_group_id"
    t.integer  "product_size_id"
    t.integer  "product_waistband_id"
    t.decimal  "euro_price"
    t.decimal  "sterling_price"
    t.datetime "created_at",                       null: false
    t.datetime "updated_at",                       null: false
    t.integer  "stock_level",          default: 0
  end

  add_index "products", ["product_group_id"], name: "index_products_on_product_group_id", using: :btree
  add_index "products", ["product_size_id"], name: "index_products_on_product_size_id", using: :btree
  add_index "products", ["product_waistband_id"], name: "index_products_on_product_waistband_id", using: :btree

Също така в модела на поръчката рандомизирам идентификатора before_create, така че когато потребителят изпрати формуляра, той създава дублирано копие с различен идентификатор на поръчка. Това е същото за LineItems.

Order.rb (същото в LineItem.rb)

before_create :randomize_id

private
  def randomize_id
    begin
      self.id = SecureRandom.random_number(1_000_000)
    end while Order.where(id: self.id).exists?
  end

person jellybean_232    schedule 01.10.2015    source източник
comment
Можете ли да добавите съответните части от вашата схема към вашия въпрос, моля.   -  person Max Williams    schedule 01.10.2015
comment
Копирах това, което смятам за уместно. Съжалявам, ако е малко дълго   -  person jellybean_232    schedule 01.10.2015


Отговори (1)


Моят подход би бил да отменя метода ActiveRecord::Base#dup в модела Order, така че да е рекурсивен, което означава, че дублира и колекцията LineItem:

class Order < ActiveRecord::Base
  def dup
    duped_order = super
    duped_order.line_items = line_items.map(&:dup)
    duped_order
  end
end

правенето му по този начин го прави лесно тестваем. Сега контролерът става:

class OrderController < ApplicationController
  def new
    @order = Order.find(params[:id]).dup
  end

  def create
    # not sure how your form populates the params hash
    # here you need to new-up and then save the order and the line items
    # with the attributes from the form
  end
end

Напишете тестове, за да потвърдите, че това прави това, което възнамерявате. Това е перфектният пример за това къде трябва да се приложи старата парадигма на "дебелия модел кльощав контролер".

person Les Nightingill    schedule 02.10.2015
comment
Опитах това, но проблемът възниква, когато съм в create. От кода подавам exchange_order_params (който съм добавил към моя фрагмент), но идентификаторът на продукта търси идентификатора на LineItem (Грешка: Не може да се намери продукт с ID=2 за LineItem с ID=). Забелязах, че идентификаторът на LineItem и ID на поръчката вече са нула по някаква странна причина (вероятно от дублирането). Имам нужда от стария LineItem и ID на поръчката, за да предам стария запис в новия (разбира се с различни идентификатори). Съжалявам, ако това е малко трудно за разбиране, но се опитвам да обясня проблема си. - person jellybean_232; 05.10.2015
comment
Също така само да спомена, че трябва да запазя документите за самоличност. Не мога да ги извадя по причини. Разбирам, че ако премахна идентификационните номера, това ще работи, но тогава ще възникне друг проблем, който беше решен чрез запазване на идентификационните номера. И така, има ли начин да прехвърлите стария LineItem Id, докато го дублирате? - person jellybean_232; 05.10.2015
comment
Ако трябва да запазите id, тогава не „клонирате“ обектите, а вместо това използвате действителните оригинални обекти и затова не разбирам какво се опитвате да направите. Да, „dup“ премахва идентификатора и това обикновено е желаното поведение. Другият проблем, който споменавате, вероятно може да бъде решен без запазване на идентификаторите. Доколкото разбирам въпроса ви, не трябва да копирате идентификаторите, но може би не разбирам напълно въпроса ви. Ако искате да разработите по-подробно другия проблем, може би можем да разрешим този. - person Les Nightingill; 05.10.2015
comment
Да, съжалявам, доста трудно е за обяснение, но по принцип имам таблица с продукти, в която са изброени всички продукти и нивата на запасите. Така че там, където идват идентификаторите, е мястото, където създавам този нов запис за поръчка, но той трябва да актуализира нивата на запасите в таблицата с продукти. Знам, че клонирането на запис, но запазването на идентификатора звучи наистина странно, но по същество правя формуляр за поръчка за обмен, където взема атрибутите на старата поръчка и попълва нов формуляр за поръчка (защото искам да създам нов запис от стария нечий). - person jellybean_232; 05.10.2015
comment
Така че запазването на идентификаторите в силните параметри означава, че актуализирам таблицата с продукти и не създавам нови записи. Намерих това решение тук: stackoverflow .com/questions/18946479/ - person jellybean_232; 05.10.2015
comment
Вие не копирате обектите на продукта. Когато дублирате договорените позиции, те запазват същия product_id, но задават собствения си идентификатор на нула. Актуализирането на нивата на запасите от продукти вероятно трябва да се извърши с обратно извикване after_create на модела LineItem. Мисля, че лошо препоръчано решение за управление на запасите е причинило проблема, който публикувахте тук. Когато кажете нещо като xxx, звучи наистина странно, това е намек, че може да не го правите по най-добрия начин. Предлагам ви да разрешите този проблем, както предложих, и да преразгледате проблема с управлението на запасите. Вашият тестов пакет ще потвърди, че и двата проблема са разрешени. - person Les Nightingill; 05.10.2015
comment
Да, помислих добре за това и това, което казахте, е правилно. Определено ще трябва да намеря нов начин. Очевидно е, че не много хора са задавали моите въпроси с причина =) Благодаря много за това, което предложихте, че работи много добре. Ще отбележа като прието =) - person jellybean_232; 05.10.2015