I am unable to get my association to save in localhost:3000/controller_name/new
. I believe it is due to belongs_to
failing validation, but I am not sure how to fix it (beyond just dropping the association via requires:false
/optional: true
, the consequences of which I am not sure of). I created my association following this tutorial but it was for a previous version of rails.
I have a polymorphic address table that can belong to events, businesses, users, etc. I am trying to add it to event.
Address migration - you can see it references addressable
:
class CreateAddresses < ActiveRecord::Migration[5.1]
def change
create_table :addresses do |t|
t.string :address
t.decimal :latitude, null: false, precision: 10, scale: 6, index: true
t.decimal :longitude, null: false, precision: 10, scale: 6, index: true
t.references :addressable, polymorphic: true, index: true
end
end
end
Address model:
class Address < ApplicationRecord
belongs_to :addressable, polymorphic: true
end
Event model:
class Event < ApplicationRecord
has_one :address, as: :addressable
accepts_nested_attributes_for :address
end
Event Controller:
class EventsController < ApplicationController
#...stuff...
# GET /events/new
def new
@event = Event.new
@event.address = @event.build_address
#@event.address = Address.new(addressable: @event)
#@event.address = @event.create_address
#@event.address = @addressable.User.new
end
#...stuff...
You can see I tried multiple methods to create the event's address, they mostly create the below item, the ones using addressable
cause a Nil
crash.
#<Address id: nil, address: nil, latitude: nil, longitude: nil, addressable_type: "Event", addressable_id: nil>
Event Form (Uses Simple_form gem):
<%= simple_form_for @event do |f| %>
<%= f.input :name %>
<%= f.input :description %>
<%= f.simple_fields_for :address do |address| %>
<%= render :partial => 'shared/address/form', :locals => {:f => address} %>
<% end %>
<%= f.button :submit %>
<% end %>
Address form partial:
<!-- Google Maps Must be loaded -->
<% content_for :head do %>
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCMh8-5D3mJSXspmJrhSTtt0ToGiA-JLBc&libraries=places"></script>
<% end %>
<div id="map"></div>
<%= f.input :address %>
<%= f.input :latitude %>
<%= f.input :longitude %>
Forms render fine. When I try to save I get
Started POST "/events" for 127.0.0.1 at 2017-07-01 16:06:23 -0400
Processing by EventsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"R0zlSs3UUNV3x8sQn5ocmE4jP12uOsFza7FezBAuhP4sw2MhF1OhixF8sAfDsLpfMEX7x5rhJ9HZfbKna8ncEA==", "event"=>{"name"=>"asd", "description"
=>"asd", "address_attributes"=>{"address"=>"asd", "latitude"=>"1", "longitude"=>"1"}}, "commit"=>"Create Event"}
(0.1ms) begin transaction
(0.1ms) rollback transaction
And I am kept on the new
page. If I insert a byebug
into create
and print out @event.errors
it shows:
#<ActiveModel::Errors:0x007fb29c34a9a8 @base=#<Event id: nil, name: "asd", description: "asd", min_users: nil, max_users: nil, start_time: nil, recurring: nil, created_at: nil, upd
ated_at: nil>, @messages={:"address.addressable"=>["must exist"]}, @details={:"address.addressable"=>[{:error=>:blank}]}>
How can I create the address.addressable? What are the consequences of turning off the requires validation as some SO answers suggest?
Discovered the issues and wrote a blog post discussing this in depth. Essentially I was encountering 2 separate problems
1.The error I was receiving - "address.addressable"=>["must exist"]
. address.addressable is the 'parent' table's tuple ID. In my case it is the Event ID. This error is trying to tell me that this foreign key is missing from the new address when we try to save it in the controller's create function. Like I said in the question, you can set optional:true
to ignore this problem, and it somehow magically gets filled in after saving the Event. Or you can manually assign it in the create function before it saves.
def create
@event = Event.new(event_params)
@event.address.addressable = @event #<<<<<<<<<<< manually assign address.addressable
respond_to do |format|
if @event.save #Saves the event. Addressable has something to reference now, but validation does not know this in time
format.html { redirect_to @event, notice: 'Event was successfully created.' }
format.json { render :show, status: :created, location: @event }
else
#dont forget to remove the 'byebug' that was here
format.html { render :new }
format.json { render json: @event.errors, status: :unprocessable_entity }
end
end
end
2.Event ID was a string. In my address migration I was using t.references :addressable, polymorphic: true, index: true
which is an alias for and addressable_id
of integer type. I needed to change my migration to use string IDs - so delete the references
line and add this in instead.
t.string :addressable_id, index: true
t.string :addressable_type, index: true
This is covered in slightly more detail in the blog post.