开发者

Duplicate records created in has_many :through associations

I have three models: Booking, ExtraNight and BookedExtraNight. Running Rails 2.3.11

Booking:

has_many :extra_nights, :through => :booked_extra_nights
has_many :booked_extra_nights, :dependent => :destroy

ExtraNight:

has_many :booked_extra_nights, :dependent => :destroy
has_many :bookings, :through => :booked_extra_nights

BookedExtraNight:

belongs_to  :extra_night
belongs_to  :booking

Now because of the way the application works the booking exists when the booked_extra_night is created. A booked_extra_night is created with the Booking Update action. In the view the booked_extra_night is built using:

<% unless @booking.booked_extra_nights.exists? %>
   <% @booking.booked_extra_nights.build %>
<% end %>

I use nested_form_for @booking and f.fields_for :booked_extra_nights to create the booked_extra_nights.

Now everything works fine until I press submit (update action) when two copies of the booked_extra_night is create.

See log:

开发者_StackOverflow中文版Processing BookingsController#update (for 127.0.0.1 at 2011-02-21 07:44:22) [PUT]
Parameters: {"action"=>"update", "_method"=>"put",   "authenticity_token"=>"b/M+VjMxA8RFqbubhBeF494B/zhxi/2Eb3EtoCoRLx0=",  "id"=>"5b2jwg7qw5na3vz4nt", "booking"=>{"booked_extra_nights_attributes"=>{"0"=> {"number_of_days"=>"2", "from_date(1i)"=>"2011", "from_date(2i)"=>"9", "from_date(3i)"=>"1",  "_destroy"=>"", "extra_night_id"=>"7"}}}, "controller"=>"bookings"}
Booking Load (1.3ms)   SELECT * FROM "bookings" WHERE ("bookings"."random_url_key" =  '5b2jwg7qw5na3vz4nt') LIMIT 1
Variant Load (0.6ms)   SELECT * FROM "variants" WHERE ("variants"."id" = 27) 
SQL (0.1ms)   BEGIN
SQL (0.7ms)   INSERT INTO "booked_extra_nights" ("number_of_days", "created_at",   "updated_at", "booking_id", "from_date", "extra_night_id") VALUES(2, '2011-02-21  06:44:22.525154', '2011-02-21 06:44:22.525154', 69, '2011-09-01', 7) RETURNING "id"
SQL (0.8ms)   COMMIT
SQL (0.6ms)   BEGIN
SQL (0.6ms)   INSERT INTO "booked_extra_nights" ("number_of_days", "created_at",  "updated_at", "booking_id", "from_date", "extra_night_id") VALUES(2, '2011-02-21 06:44:22.544452', '2011-02-21 06:44:22.544452', 69, '2011-09-01', 7) RETURNING "id"
SQL (25.8ms)   COMMIT
SQL (0.1ms)   BEGIN
Booking Update (0.6ms)   UPDATE "bookings" SET "updated_at" = '2011-02-21 06:44:22.575409', "aasm_state" = 'step3' WHERE "id" = 69
SQL (0.5ms)   COMMIT
Redirected to http://localhost:3000/bookings/5b2jwg7qw5na3vz4nt/step3

As you can see two identical records is created, now if I were to build 4 booked_extra_nights and press submit I'd end up with 8 records.

I've also found out that if I create a booked_extra_night record at the same time the booking is created then I can add as many as I want doing the above without duplicates. This happens on all 2.3.x versions of rails as far as I know, so it's obviously something I'm doing wrong. Any help would be greatly appreciated since it's doing my head in.

Thanks!


I've had this problem as well, and my solution was in my controller, to not use the build, and to use new and supply the parent parameter.

Extract in my code.. Instead of using

@new_brand = @company.brands.build/new

I use

@new_brand = Brand.new(:company => @company)

The first adds this object to the @company as an empty object, then when you submit its create another new one within your create action.

The second just creates a new object in memory for the purposes of the form, but when the values are submitted to the create action, there are no objects tied to the company


Thanks, Rabbot! You got me on the right track, sad to say, but the update action were a mess. I've re-factored it and now everything works, I think the double saves had to do with me first updating the booking, then redirecting, then updating the booking again through AASM.

Below is the old update action (I told you it was a mess):

def update
  @booking = Booking.find_by_random_url_key(params[:id])
  @variant = @booking.variant
  if params[:back_button]
    if @booking.aasm_state == "step2"
      redirect_to booking_step1_url(@booking)
    elsif @booking.aasm_state == "step3"
      redirect_to booking_step2_url(@booking)
    elsif @booking.aasm_state == "step4"
      redirect_to booking_step3_url(@booking)
    elsif @booking.aasm_state == "step5"
      redirect_to booking_step4_url(@booking)
    end
    @booking.previous!
  else
    if @booking.update_attributes(params[:booking]) && @booking.aasm_state == "step1"
      redirect_to booking_step2_url(@booking)
      @booking.next!
    elsif @booking.update_attributes(params[:booking]) && @booking.aasm_state == "step2"
      @booking.next!
      redirect_to booking_step3_url(@booking)
    elsif @booking.update_attributes(params[:booking]) && @booking.aasm_state == "step3"
      redirect_to booking_step4_url(@booking)
      @booking.next!
    elsif @booking.update_attributes(params[:booking]) && @booking.aasm_state == "step4"
      redirect_to booking_url(@booking)
      @booking.next!
    end
  end
end

And this is the new, re-factored update action.

  def update
    @booking = Booking.find_by_random_url_key(params[:id])
    @variant = @booking.variant
    if params[:back_button]
      @booking.previous!
      redirect_to :controller => "bookings", :action => "#{@booking.aasm_state}", :id => @booking
    else
      @booking.update_attributes(params[:booking])
      @booking.next!
      redirect_to :controller => "bookings", :action => "#{@booking.aasm_state}", :id => @booking
    end
  end

Same functionality, many many lines less. And it does not create duplicate extra nights.

Thanks, Rabbot. Your answer made me think, as I'd tried changing stuff in the view and model several times without success. I just assumed the controller worked since it worked for everything else.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜