has_one association with optional: true?

8k Views Asked by At

In a simple booking app:

  • When user selects a seat, a TempAppointment is created.
  • After the seat has been paid for, an Appointment is created based off the info in the TempAppointment record.

The Appointment record cannot be created first, since the passenger may not pay, in which case the TempAppointment stays as-in, and an associated Appointment record never gets created.

My natural thinking is that a TempAppointment has_one Appointment (which works), but when I add optional: true, I see errors:

class TempAppointment < ApplicationRecord
  has_one :appointment, optional: true 
end

try to create a new TempAppointment

ta = TempAppointment.new(cruise_id: 1, passenger_id: 1, start_time: start_time, end_time: start_time + 3600)
ArgumentError: Unknown key: :optional. Valid keys are: :class_name, :anonymous_class, :foreign_key,
 :validate, :autosave, :foreign_type, :dependent, :primary_key, :inverse_of, :required, :as, :touch

Why can't has_one work with optional: true?

3

There are 3 best solutions below

0
lucyjosef On BEST ANSWER

has_one is optional: true by default (even if it isn't really an option of this method, I mean has_one never means it is require)

So if you set the optional: true to the other model, be careful, this means that this other model doesn't need to be in a relationship with the first one. It's a one way dependence.

# your model_a doesn't need any model_b to exist
class ModelA < ApplicationRecord
  belongs_to :model_b, optional: true
  
  [...]
end

# same for model_b, it can exist without model_a
class ModelB < ApplicationRecord
  has_one :model_a
  
  [...]
end

But if you do

# your model_a has to belong to a model_b, otherwise it will fail
class ModelA < ApplicationRecord
  belongs_to :model_b
  
  [...]
end

# your model_b can still exist without model_a
class ModelB < ApplicationRecord
  has_one :model_a
  
  [...]
end
0
stevec On

I switched the model associations around and it worked.

AFAIK, it makes no difference anywhere in the app.

Some helpful hints here

Here's what worked:

class Appointment < ApplicationRecord
  has_one :temp_appointment 
end
class TempAppointment < ApplicationRecord
  belongs_to :appointment, optional: true 
end
0
Corey Sheesley On

On the Model where the has_one relationship is defined, I think you might be able to use required: false. So in the end, it would look something like this:

class Appointment < ApplicationRecord
  has_one :temp_appointment, required: false
end
class TempAppointment < ApplicationRecord
  belongs_to :appointment, optional: true 
end

https://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_one