简体   繁体   中英

How to replace data in one record with data from another record in a controller in Ruby on Rails?

So first I am a absolute beginner, so sorry if my code looks horrible. I am working on a booking platform where users can book seats in a bus and exchange them with each other if they don't need them. Exchanging them is the only way to get rid of a seat booking, they can't simply cancel/delete it.

If a user doesn't need their Booking then he/she can create a Angebot (offer) on a marketplace/list for that given Booking . Other users can request it with a Anfrage (request). This Anfrage (request) contains alternative data from the requesting user which contains the same columns as Booking . Now I am trying to replace the data in one Booking with data from a Anfrage (request). This is achieved when the user that created the Angebot (offer) and own the Booking accepts one of the Anfrage (request).

def update_booking_durch_anfrage
    @angebot = Angebot.where(id: anfrage_params[:angebot_id]).first 
    @booking = Booking.find_by_id(@angebot)
    @gleiche_anfragen = Anfrage.find_by_id(@angebot)
    respond_to do |format|
      if @booking.update( 
        user_id: @angebot.user_id,
        vorname: @angebot.vorname,
        nachname: @angebot.nachname,
        email: @angebot.email,
        handynummer: @angebot.handynummer
      ) 
        @gleiche_anfragen.destroy
        @angebot.destroy

        format.html { flash[:notice] = 'Die Buchung wurde erfolgreich auf dich übertragen!' and redirect_to action: "index" }
        format.json { render :show, status: :ok, location: @booking }
      else
        format.html { render :edit }
        format.json { render json: @booking.errors, status: :unprocessable_entity }
      end
    end
  end

Here are the schema.rb and the relations between the models.

create_table "anfrages", force: :cascade do |t|
    t.integer "angebot_id"
    t.integer "user_id"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.string "vorname"
    t.string "nachname"
    t.string "handynummer"
    t.string "email"
    t.index ["angebot_id"], name: "index_anfrages_on_angebot_id"
    t.index ["user_id"], name: "index_anfrages_on_user_id"
  end

  create_table "angebots", force: :cascade do |t|
    t.integer "booking_id"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["booking_id"], name: "index_angebots_on_booking_id"
  end

  create_table "bookings", force: :cascade do |t|
    t.integer "bus_id"
    t.integer "user_id"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.string "vorname"
    t.string "nachname"
    t.string "handynummer"
    t.string "email"
    t.index ["bus_id"], name: "index_bookings_on_bus_id"
    t.index ["user_id"], name: "index_bookings_on_user_id"
  end
class Angebot < ApplicationRecord
    attr_accessor :user_id, :vorname, :nachname, :email, :handynummer
    belongs_to :booking
    has_many :anfrages
end

class Anfrage < ApplicationRecord
    belongs_to :angebot
    belongs_to :user
end

class Booking < ApplicationRecord
    belongs_to :bus
    belongs_to :user
    has_one :angebot
end

But right now this method doesn't do what it is supposed to. It simply redirects me to the edit.html.erb view from Anfrages without doing anything to the data. I think it has to do with the way rails handles updates and the corresponding forms but I have no clue how to change that.

The other thing is that I am no sure if the controller is the right place for this kind of logic and maybe it belongs into the model.

Thanks in advance for your help and time.

Looking at your code, your controller's logic goes like

if @booking.update(...)
else
 ...
 format.html { render :edit }
end

So what is happening is updating the booking isn't working, returning false and redirecting you to the edit view. You can get more insight into exactly why updating that Booking is failing by instead calling @booking.update!(...) which will raise an error instead of just returning false if the booking does not update.

Additionally, you may be able to save yourself some headache by defining methods on your models to list those attributes. Something like

class Angebot
  def booking_attributes
    %i[user_id vorname nachname email handynummer].map do |a|
      [a, send(a)]
    end.to_h
  end
end

# Controller

@booking.update(@angebot.booking_attributes)

So I figured it out what was wrong. First the link_to method in my view was sending the wrong params so I set it up correctly. Than update_booking_durch_anfrage method wasn't able to fetch this data correctly because of all the relations. I now created this 4 step data fetch approach that seems to work. And the last thing is as you can see that the data which I tried to replace wasn't stored in Angebot but in Anfrage ...

So here's the solution that fetches the right Booking and updates it with data from the correct Anfrage . It's important that the link in the view uses PUT , because you want to update the data. Big thanks also to Justin for his booking_attributes method which made it a lot easier.

<%= link_to 'Tauschanfrage annehmen', {:controller => "angebots", :action => "update_booking_durch_anfrage", :id => anfrage.id, :angebot_id => anfrage.angebot_id }, :method => :put, data: { confirm: 'Bist du dir sicher, dass du die Tauschanfrage annehmen möchtest?'}, class: 'btn btn-outline-primary' %>

As you can see it sends the right parameters which are needed {:controller => "angebots", :action => "update_booking_durch_anfrage", :id => anfrage.id, :angebot_id => anfrage.angebot_id }

Next the method in the controller fetches the right data with a lot of .where methods.

    def update_booking_durch_anfrage  
      @anfrage = Anfrage.where(id: anfrage_params[:id]).first
      @angebot = Angebot.where(id: anfrage_params[:angebot_id]).first
      @booking = Booking.where(id: @angebot.booking_id).first
      @gleiche_anfragen = Anfrage.where(angebot_id: anfrage_params[:angebot_id]).first
        respond_to do |format|
          if @booking.update!(@anfrage.booking_attributes) 
            @gleiche_anfragen.destroy
            @angebot.destroy
            format.html { flash[:notice] = 'Die Buchung wurde erfolgreich übertragen!' and redirect_to action: "index" }
          else
            format.html { flash[:notice] = 'Irgendwas ist schief gegangen...' and redirect_to action: "index" }  
          end
        end
     end



class Anfrage < ApplicationRecord
    belongs_to :angebot
    belongs_to :user

    validates :vorname, presence: true
    validates :nachname, presence: true
    validates :email, presence: true, :format => /\A[^@\s]+@[^@\s]+\z/

    def booking_attributes
        %i[user_id vorname nachname email handynummer].map do |a|
          [a, send(a)]
        end.to_h
    end
end

class Booking < ApplicationRecord
        belongs_to :bus
        belongs_to :user
        has_one :angebot, dependent: :destroy
        validates :vorname, presence: true
        validates :nachname, presence: true
        validates :email, presence: true, :format => /\A[^@\s]+@[^@\s]+\z/
end

It saves the data after validation in the Model and everything works smoothly. I am sure that's a lot of spaghetti code but it gets the job done...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM