Как изменить порядок, с которым связан элемент OrderItem в Rails?

#ruby-on-rails #caching

#ruby-on-rails #кэширование

Вопрос:

У Order has_many OrderItems . So OrderItem есть order_id поле.

Как изменить, к какому Order OrderItem элементу принадлежит? Такой, который вы можете вызвать @order.order_items .

Я попытался изменить order_id of OrderItem , но это не работает. Или, по крайней мере, он не отображается при вызове @order.order_items .

Редактировать: консоль

 ~/practice/dinner_dash >>  rails c --sandbox
Loading development environment in sandbox (Rails 4.0.3)
Any modifications you make will be rolled back on exit
2.0.0-p451 :001 > order1 = Order.first
  Order Load (0.2ms)  SELECT "orders".* FROM "orders" ORDER BY "orders"."id" ASC LIMIT 1
 => #<Order id: 2, status: "Unsubmitted.", user_id: 1, created_at: "2014-06-28 16:21:24", updated_at: "2014-06-28 16:21:24"> 
2.0.0-p451 :002 > order1.order_items.each { |order_item| puts order_item }
  OrderItem Load (2.5ms)  SELECT "order_items".* FROM "order_items" WHERE "order_items"."order_id" = ?  [["order_id", 2]]
#<OrderItem:0x007f9dd34d0710>
#<OrderItem:0x007f9dd35b9550>
#<OrderItem:0x007f9dd35b8fd8>
 => [#<OrderItem id: 8, quantity: 5, item_id: 2, order_id: 2, created_at: "2014-06-28 16:30:33", updated_at: "2014-06-29 02:06:17">, #<OrderItem id: 9, quantity: 2, item_id: 3, order_id: 2, created_at: "2014-06-28 16:30:45", updated_at: "2014-06-28 16:56:41">, #<OrderItem id: 15, quantity: 1, item_id: 4, order_id: 2, created_at: "2014-06-28 17:42:17", updated_at: "2014-06-28 17:42:17">] 
2.0.0-p451 :003 > order2 = Order.last
  Order Load (0.2ms)  SELECT "orders".* FROM "orders" ORDER BY "orders"."id" DESC LIMIT 1
 => #<Order id: 15, status: "Unsubmitted.", user_id: 2, created_at: "2014-06-28 17:38:08", updated_at: "2014-06-28 17:40:11"> 
2.0.0-p451 :004 > order2.order_items.first.order = order1
  OrderItem Load (0.3ms)  SELECT "order_items".* FROM "order_items" WHERE "order_items"."order_id" = ? ORDER BY "order_items"."id" ASC LIMIT 1  [["order_id", 15]]
 => #<Order id: 2, status: "Unsubmitted.", user_id: 1, created_at: "2014-06-28 16:21:24", updated_at: "2014-06-28 16:21:24"> 
2.0.0-p451 :005 > order1.order_items.each { |order_item| puts order_item }
#<OrderItem:0x007f9dd34d0710>
#<OrderItem:0x007f9dd35b9550>
#<OrderItem:0x007f9dd35b8fd8>
 => [#<OrderItem id: 8, quantity: 5, item_id: 2, order_id: 2, created_at: "2014-06-28 16:30:33", updated_at: "2014-06-29 02:06:17">, #<OrderItem id: 9, quantity: 2, item_id: 3, order_id: 2, created_at: "2014-06-28 16:30:45", updated_at: "2014-06-28 16:56:41">, #<OrderItem id: 15, quantity: 1, item_id: 4, order_id: 2, created_at: "2014-06-28 17:42:17", updated_at: "2014-06-28 17:42:17">] 
2.0.0-p451 :006 > 
  

Редактировать: реальная проблема

Я думаю, что реальная проблема заключается в том, что @order возвращаемый by load_order кэшируется. Итак, вызывается второй раз load_order , он возвращает старое @order , которое было кэшировано в памяти. Я не уверен, как это исправить.

application_controller.rb

 def load_order
    @order = Order.find_or_initialize_by_id(session[:order_id], status: 'Unsubmitted.', user_id: session[:user_id])
    if @order.new_record?
        @order.save!
        session[:order_id] = @order.id
    end
  end
  

sessions_controller.rb

 def create
        user = login(params[:email], params[:password], params[:remember_me])
        if user
            load_order
            @order.update(user_id: user.id)
            merge_orders
            session[:order_id] = Order.find_by_user_id(user.id).id if Order.find_by_user_id(user.id)
            load_order
            redirect_back_or_to root_path, notice: 'Logged in!'
        else
            flash.now.alert = 'Email or password was invalid.'
            render :new
        end
    end
  

Редактировать: код, который не работает

Те order_items , которые распечатываются каждый раз, одинаковы. Однако order_item.order_id он изменяется.

 def merge_orders
    orders = Order.all
    user_orders = {}
    orders.each do |order|
        if user_orders[order.user_id] == nil
            user_orders[order.user_id] = order
        else
            original_order = user_orders[order.user_id]

            original_order.order_items.each do |order_item|
                item = Item.find_by_id(order_item.item_id)
                puts "nnn#{item.name} x #{order_item.quantity}nnn"
            end

            current_order = order
            # assign order_id of current_order to original_order
            current_order.order_items.each do |order_item|
                puts "order_item.order_id: #{order_item.order_id}"
                order_item.update(order_id: original_order.id)
                puts "order_item.order_id: #{order_item.order_id}"
            end

            original_order.order_items.each do |order_item|
                item = Item.find_by_id(order_item.item_id)
                puts "nnn#{item.name} x #{order_item.quantity}nnn"
            end

            # delete current_order
            current_order.destroy
            puts "nnn"
        end
    end
  end
  

order.rb

 class Order < ActiveRecord::Base
  belongs_to :user
  has_many :order_items, dependent: :destroy

  validates_associated :order_items
end
  

order_item.rb

 class OrderItem < ActiveRecord::Base
  belongs_to :item
  belongs_to :order

  validates_presence_of :order_id, :item_id, :quantity
  validates :quantity, numericality: { only_integer: true, greater_than: 0 }
end
  

Комментарии:

1. Показать вам модели и код, которые не работают.

Ответ №1:

Как изменить, к какому порядку относится элемент OrderItem?

Самое простое объяснение — изменить order_id эту запись. Другого способа просто нет, если, конечно, у вас нет нескольких записей, связанных с order

Слияние

Я думаю, что ваша проблема связана с вашей merge функциональностью

Я не понимаю, что вы с этим делаете — похоже, вы выводите item x quantity или что-то в этом роде — если вы хотите показать содержимое определенного порядка, вам лучше всего использовать для этого код контроллера:

 #app/controllers/orders_controller.rb
Class OrdersController < ApplicationController
   def index
      # loop through orders amp; create instance variable to output
   end
end
  

Если вы пытаетесь установить current_order , вы, вероятно, захотите использовать модель на основе сеанса, которая позволит вам сохранять current_order объект по мере необходимости


  • Как правило, вы никогда не должны напрямую выводить данные с помощью Rails — ваше использование puts в основном будет выводить определенные данные, которые не входят в компетенцию MVC

  • Рассматривали ли вы возможность изменения вашей OrderItem модели на just Item ? Это будет намного менее подробным, когда вы ссылаетесь на него -> @order.items

Комментарии:

1. Обновление order_id — не единственный способ. Пожалуйста, обратитесь к ссылке rails guides для belongs_to: guides.rubyonrails.org /…

2. Извините, вы меня неправильно поняли. Я имел в виду с точки зрения ядра — обновление order_id в базе данных — единственный способ фундаментально изменить ассоциацию. Как вы это делаете программно, это другой вопрос

3. @RichPeck puts Инструкции предназначены только для отладки. Что я пытаюсь сделать в merge_orders : если есть два одинаковых заказа user_id , присвоите order_id s второго порядка исходному порядку.

4. @RichPeck Я думаю, что я понял проблему, но не могу понять, как ее исправить. См. Редактирование вопроса.

5. Как я уже сказал в своем ответе, order.reload

Ответ №2:

Изменение order_id элемента OrderItem и его сохранение должны работать. Вы также можете сделать это:

 order_item.order = a_different_order
  

Поскольку эти данные могут быть кэшированы в памяти, чтобы увидеть это влияние на исходный порядок, вам, возможно, придется сделать:

 original_order.reload.order_items
  

Вы также можете проверить другой другой способ, выполнив:

 order_item.reload.order
  

ps. Вы также должны проверить awesome_print gem, чтобы распечатать это на консоли.

pps. Вы должны делать это в тесте 🙂

Вот пример из моей консоли, где ответ принадлежит пользователю

 1.8.7-p352 :003 > a = Answer.last
1.8.7-p352 :005 > a.user_id
 => 4212 
1.8.7-p352 :006 > old_user = a.user
1.8.7-p352 :007 > a.user = User.last
1.8.7-p352 :008 > a.user_id
 => 7290 
1.8.7-p352 :009 > a.user = old_user
1.8.7-p352 :010 > a.user_id
 => 4212 
1.8.7-p352 :016 > a.changed?
 => true 
1.8.7-p352 :017 > a.save
  SQL (7.1ms)  BEGIN
  AREL (5.1ms)  UPDATE `answers` SET `updated_at` = '2014-06-29 17:39:30', `user_id` = 7290 WHERE `answers`.`id` = 21
  SQL (0.4ms)  COMMIT
 => true 
1.8.7-p352 :018 > a.changed?
 => false 
  

Комментарии:

1. Насколько я понимаю, вызов update должен сохранить его. Это правда? original_order.reload.order_items сейчас выводит правильные вещи, но в моем приложении они по-прежнему не работают (когда я вхожу в систему, не отображается, что порядок обновляется).

2. Я думаю, что я понял проблему, но не знаю, как ее исправить. См. Редактирование вопроса.

3. Итак, что я понял из этого, так это то, что я должен делать order_item.order = original_order вместо order_item.update(order_id: original_order.id) . Но это не работает. original_order.reload.order_items Последующий вызов также ничего не делает.

4. Два приведенных выше метода эквивалентны. Вам нужно упростить свою ситуацию, чтобы выяснить, что вы делаете неправильно. Я не понимаю, как вы обрабатываете изменение поля, это проблема. Сделайте это в консоли и опубликуйте свой код, как я это сделал.

5. Ps, очевидно, вам нужно сохранить свою модель. Добавил этот бит выше.