Reputation: 489
Issue
I'm encountering a problem when editing a form with a belongs_to relationship (extra_guest belongs_to age_table).
I am able to create a new extra_guest and assign it to an age_table, but I cannot get the edit/update to work as my update function returns a falseClass.--> @extra_guest.update(extra_guest_params).errors.full_messages
returns undefined method `errors' for false:FalseClass
Code
models
class ExtraGuest < ApplicationRecord
belongs_to :age_table
validates :age_table, presence: true
end
class AgeTable < ApplicationRecord
belongs_to :park
has_many :extra_guests, dependent: :destroy
validates :name, :age_from, :age_to, presence: true
validates_associated :extra_guests
end
class Attraction < ApplicationRecord
belongs_to :park
has_many :extra_guests, dependent: :destroy
accepts_nested_attributes_for :extra_guests, allow_destroy: true
validates :name, presence: true
end
class Park < ApplicationRecord
has_many :attractions, dependent: :destroy
has_many :age_tables, dependent: :destroy
validates :name, :currency, presence: true
end
extra_guests_controller
def edit
@extra_guest = ExtraGuest.find(params[:id])
@age_table = @extra_guest.age_table
@age_table_list = AgeTable.where(park: @attraction.park)
end
def update
@extra_guest = @attraction.extra_guests.find(params[:id])
@age_table = AgeTable.find(params[:age_table])
authorize @extra_guest
if @extra_guest = @extra_guest.update(extra_guest_params)
redirect_to root_path
else
@attraction = Attraction.find(params[:attraction_id])
@extra_guest = ExtraGuest.find(params[:id])
@age_table_list = @attraction.park.age_tables
render 'edit'
end
end
private
def extra_guest_params
params.require(:extra_guest).permit(:name, :age_table_id,
extra_guest_prices_attributes: [:id, :name, :price_type, :start_date, :end_date, :price, :duration, :duration_min, :duration_max, :backend_only, :weekend_extra, :_destroy])
end
views/extra_guests/form
<%= simple_form_for [@attraction, @extra_guest] do |f|%>
<%= f.input :age_table, :as => :select, :selected => @age_table.id, :collection => @age_table_list.map {|u| [u.name, u.id]}, :include_blank => false %>
<% f.button :submit %>
Error message + params
Couldn't find AgeTable without an ID
{"utf8"=>"✓",
"_method"=>"patch",
"authenticity_token"=>"l8HMnVIRybZg==",
"extra_guest"=>
{"age_table"=>"104",
"extra_guest_prices_attributes"=>
{"0"=>{"price"=>"1.0", "weekend_extra"=>"", "start_date"=>"2019-10-15", "end_date"=>"20-09-2019", "duration"=>"", "duration_min"=>"", "duration_max"=>"", "_destroy"=>"false", "id"=>"42"},
"1"=>{"price"=>"1.0", "weekend_extra"=>"", "start_date"=>"2019-10-15", "end_date"=>"2019-10-16", "duration"=>"", "duration_min"=>"", "duration_max"=>"", "_destroy"=>"false", "id"=>"43"}}},
"commit"=>"Save new option",
"attraction_id"=>"185",
"id"=>"55"}
Upvotes: 0
Views: 144
Reputation: 15838
First of all, you say you have an error with this code @extra_guest.update(extra_guest_params).errors.full_messages
but the code you show does not have that line.
Now, the update
method returns false if it fails https://apidock.com/rails/ActiveRecord/Persistence/update
This line:
@extra_guest = @extra_guest.update(extra_guest_params)
will set @extra_guest to false if it fails, you don't need to set @extra_guest, just use if @extra_guest.update(extra_guest_params)
Using the line of code you name but it's not on the code you showed,@extra_guest.update(extra_guest_params).errors.full_messages
, if there are errors then @extra_guest.update(extra_guest_params)
will be false, so no .errors
method is found.
you have to split it in two lines:
@extra_guest.update(extra_guest_params) # after this, @extra_guest will have the errors hash set
@extra_guest.errors.full_messages # call it on the object and not on the result value from the update method
EDIT: you are permitting age_table_id
but the parameter is age_table
, fix the name of the parameter to be age_table_id
too
Upvotes: 1
Reputation: 13
It looks to me like you tried to use @attraction before defining it. You could fix this by moving your definition of @attraction further up in the method, but I would move it into its own method like so:
private
def attraction
@attraction ||= Attraction.find(params[:attraction_id])
end
Then you use the method name, which is now defined for the whole controller and invoked when you use it (as opposed to an instance variable which will just be 'nil' if you invoke it without defining it). The ||= allows the method to return the existing value of the instance variable if it is defined, rather than running the query every time the method is called. So first line of your update action would be
@extra_guest = attraction.extra_guests.find(params[:id])
I would do something similar for the other instance variables you have there (@extra_guest, @age_table, and @age_table_list should be defined in private methods separately). Incidentally, using a lot of instance variables for a single controller (you have 4 in this controller, which is a lot) is considered a bit of a code smell, but you should make something that works first and then refactor. Reference for later: https://thoughtbot.com/blog/sandi-metz-rules-for-developers
Upvotes: 0