Reputation: 403
I'm new to rails and was trying to implement a polymorphic association. Basically, I have a device object which can belong to either a outlet or a port, but not both. From my understanding, a polymorphic association would be the way to do this, so I modeled it like this:
class Device < ApplicationRecord
belongs_to :io, polymorphic: true
end
class Outlet < ApplicationRecord
has_one :device, as: :io
end
class Port < ApplicationRecord
has_one :device, as: :io
end
create_table "devices", force: :cascade do |t|
...
t.integer "io_id"
t.index ["io_id"], name: "index_devices_on_io_id"
end
Now, I ran into some trouble trying to assign an outlet
or port
to the device's io
:
can't write unknown attribute
io_type
@outlet = Outlet.where(id: @outlet_id).first @device.io = @outlet if @device.save redirect_to @device
which is from
class DevicesController < ApplicationController
def new
@device = Device.new(io_id: params[:io_id])
end
def create
@outlet_id = params[:device][:io_id]
@device = Device.new(device_params)
@outlet = Outlet.where(id: @outlet_id).first
@device.io = @outlet
if @device.save
redirect_to @device
else
render 'new'
end
end
Now, I will gladly accept some help on why this error was thrown, but that's not my primary question. I had a thought while debugging this-- does it even matter if I don't use a polymorphic association here? Would it also be fine just to create two separate associations on my device
table, one pointing to the port
table and the other to the outlet
table and make them both optional? One would remain null and the other would be assigned, and then I could check which field != null in my business logic. Will I run into problems down the road going this route?
Upvotes: 0
Views: 43
Reputation: 20263
Device
needs both an io_id
and an io_type
. Otherwise, how would Device
know what kind of thing the io_id
belongs to?
Currently, as shown by your migration, you only have io_id
. And so, you're getting the unknown attribute error for io_type
.
You should add io_type
to your table and you should be good to go.
I suggest you modify your create
action to be more like (with some comments):
class DevicesController < ApplicationController
def new
@device = Device.new(io_id: params[:io_id])
end
def create
# how do you know the :io_id is for an Outlet and not a Port?
# if you add a hidden field that includes io_type, so that
# params[:device] = {io_id: x, io_type: 'Outlet'}, then you
# should be able to do something like:
@io = params[:device][:io_type].constantize.find_by(id: params[:device][:io_id])
# the has_one association provides the build_device method.
# if you use this method, then io_id and io_type will
# automatically be set on @device
@device = @io.build_device(device_params)
if @device.save
redirect_to @device
else
render 'new'
end
end
Upvotes: 1