If I understand has_one correctly, the foreign key is on the associated record. This means that I could have multiple related records on the "foreign" side. When I use the has_one records to get the associated record, how do I determine which one will be returned?
Here's an example:
class Job < ActiveRecord::Base
has_one :activity_state
end
class ActivityState < ActiveRecord::Base
belongs_to :job
end
Let's say there are 2 rows in the activity_states table that have their foreign key set to Job #1
ActivityState.where(job_id: 1).select(:id, :job_id)
#=> [#<ActivityState id: 1, job_id: 1>, #<ActivityState id: 2, job_id: 1]
When I try to get the activity_state from the JobRequest, how do I determine which one is returned?
JobRequest.find(1).activity_state
#=> #<ActivityState id: 1, job_id: 1>
Right now, it looks like it is returning the first one it finds.
What if I wanted to return the latest one that was created?
As discussed in the comments, the main issue is that your system is creating multiple ActivityState objects for each job. As you mentioned, your original code was doing this:
ActivityState.create(job_id: @job.id)
The problem is that the old ActivityState still contains the job_id for that job. Consequently, when you executed @job.activity_state, you were indeed seeing the first (and old) activity state because it is the first in the database. Specifically, the has_one relationship executes a LIMIT 1 sql clause, so technically the "first" record is dependent on however your database was ordering the records (usually by order of entry)
Normally, for a has_one relationship, if you were to do
@job.activity_state = ActivityState.new(...activity state params...)
Rails attempts to enforce the "one association per record" concept by resetting the "old" associated record's job_id column to null. Your original code was unintentionally circumventing that enforcement. If you change it to this line of code instead, you will allow Rails to work its magic and ensure consistent behaviour with your association.
You should have a has_many association in this case. To return a particular record based on a column, create a method.
class Job < ActiveRecord::Base
has_many :activity_states
def latest_activity_state
self.activity_states.order("updated_at ASC").last # or created_at, depends
end
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With