Rails создает объект таблицы соединений, который не должен создаваться, но обновляться в особом случае

В интернет-магазине у меня есть бронирование, которое должно знать, существует ли бронирование в заказе. У меня все работало, но потом детали...

...теперь «бронирование продукта» (или, на обычном английском языке: добавление продукта в корзину) добавляет совершенно новое бронирование в список заказов в каждом случае. Если этот продукт уже забронирован один раз, не следует изменять только количество.

Так легко, верно? Всего один простой оператор if, и все заработает.

bookings_controller.rb

  def create
    @order = current_order
    # If product has already been booked
    if @order.bookings.where(product_id: params[:product_id]).exists?
      # Then: Only alter the quantity in the booking.
      @booking = @order.bookings.where(product_id: params[:product_id])
      @booking.product_quantity = params[:product_quantity]
    else
      # Else: Make a new booking.
      @booking = @order.bookings.new(booking_params)
      @product = @booking.product
      @booking.product_name = @product.name
      @booking.product_price = @product.price
    end
    @order.sum_all_bookings 
    @order.save
  end

  # ...

  def booking_params
    params.require(:booking).permit(:product_quantity, :product_id)
  end

Кажется, это не работает.

Как сделать проверку в операторе if? Или мне следует пойти другим путем, чтобы обновить бронирование?

Изменить

Я пробовал различные комбинации в этой форме после ответа gmcnaughton. Я получаю либо несколько записей, либо вообще никаких записей. Этот не дает мне записей вообще.

bookings_controller.rb

  def create
    @order = current_order
    @booking = @order.bookings.find_or_create_by(product_id: params[:product_id])
    product = @booking.product
    if @booking.new_record?
      @booking.product_name = product.name
      @booking.product_price = product.price
    else
    @booking.product_quantity = params[:product_quantity]
    @booking.save
    @order.sum_all_bookings 
    @order.save
  end

Должен ли я также контролировать бронирование id, может быть? Но в этом нет смысла, ведь в случае нахождения существующей брони она уже должна быть там.

Может быть, я запускаю заказы через remote: true form?

Изменить 2

Также не работает:

bookings_controller.rb

  def create
    @order = current_order
    @booking = @order.bookings.where(product_id: params[:product_id]).first_or_initialize
    if @booking.new_record?
      @booking.product_id = params[:product_id]
      product = @booking.product
      @booking.product_name = product.name
      @booking.product_price = product.price
    else
    @booking.product_quantity = params[:product_quantity]
    @booking.save
    @order.sum_all_bookings 
    @order.save
  end

Изменить 3

Возможно, это как-то связано с этим:

categories_controller.rb / Магазин

def index
    @categories = Category.all.order(name: :asc)
    # Voor het inzien van wat al geselecteerd is.
    @order = current_order
    # Voor het aanslaan van een nieuwe booking.
    @booking = current_order.bookings.new
  end

Который в основном выкладывает весь начальный магазин. @booking предназначен для создания формы каждого продукта.

Сработало следующее:

  def create
    @booking = @order.bookings.find_by(product_id: params[:booking][:product_id])
    if @booking
      @booking.product_quantity = params[:booking][:product_quantity]
      @booking.save
    else
      @booking = @order.bookings.new(booking_params)
      @product = @booking.product
      @booking.product_name = @product.name
      @booking.product_price = @product.price
    end
    @order.save
  end

Видимо, мне нужно было получить параметры, добавив [:booking], как в params[:booking][:product_id]. Кто-нибудь знает, почему?


person Code-MonKy    schedule 08.10.2016    source источник


Ответы (1)


Идея правильная, но я думаю, что вы пропустили вызов .first:

@booking = @order.bookings.where(product_id: params[:product_id])
  => #<ActiveRecord::Relation>

должно быть:

@booking = @order.bookings.where(product_id: params[:product_id]).first
  => #<Booking>

... в противном случае вы обновляете отношение, а не модель Booking. Вероятно, вы также захотите вызвать @booking.save после его изменения.

Отдельно ActiveRecord также имеет first_or_initialize и first_or_create помощники, которые позволяют найти соответствующий экземпляр или построить/создать новый:

@booking = @order.bookings.where(product_id: params[:product_id]).first_or_initialize
if @booking.new_record?
  @product = @booking.product
  ...other stuff for new record...
else
  @booking.product_quantity = params[:product_quantity]
end
@booking.save
person gmcnaughton    schedule 08.10.2016
comment
Я работаю с вашим решением, но все, что я пробовал до сих пор, приводит к тому, что я получаю несколько записей. - person Code-MonKy; 08.10.2016
comment
Любая идея после моего последнего редактирования? Я воспользовался вашей идеей, она должна работать, и она намного элегантнее... но пока не работает. - person Code-MonKy; 09.10.2016
comment
find_or_create_by и new_record? не будут хорошо сочетаться друг с другом — если поиск создаст новую модель, у нее уже будет идентификатор, поэтому new_record? будет ложным. @order.bookings.where(...).first_or_initialize не работает? - person gmcnaughton; 09.10.2016
comment
Я внес правку 2. Это то, что вы имеете в виду? Это еще не работает. Может быть, сложность в том, что букинг — это стыковочная таблица между заказом и товаром, но я не понимаю, как это сделать. - person Code-MonKy; 09.10.2016
comment
Я также пробовал if @booking.product_id.nil?. Логика выглядит нормально, но чего-то не хватает - person Code-MonKy; 10.10.2016
comment
Я заметил, что он продолжает делать новые заказы с уникальным идентификатором. У меня все еще та же проблема после борьбы с ней в течение трех дней. Есть идеи, что происходит? - person Code-MonKy; 12.10.2016