Is there a way to rewrite the process below, which currently uses find_or_initialize_by, using the joins method?
For context - I have users (employees) who record their attendances in the system (a user has many attendances, and an attendance record belongs to a user).
Attendance.find_or_initialize_by(
user: User.find_by(name: 'Bob'),
date: Time.zone.today
)
.update(...) # Update some columns after this
I'm trying to rewrite it using .joins like this:
Attendance.joins(:user)
.where(users: {name: 'Bob'}, date: Time.zone.today)
.first_or_initialize
.update(...) # Update some columns after this
My tests come back with mixed results:
updated passattendance record doesn't exist yet (i.e. cases when I have to initialize) failThe error message is
ActiveRecord::RecordInvalid: Validation failed: User must exist.
But I'm confused because the user actually exists - if I debug using byebug, the user is there.
Rather than starting from the Attendance model, I would tend to start from the User, like this:
User.find_by(name: 'Bob').attendances.find_or_initialize_by(date: Time.zone.today).update(...)
That keeps things easy to read. You could add an association extension method to make things more convenient:
class User < ActiveRecord::Base
has_many :attendances do
def for_date(date)
find_or_initialize_by(date: Time.zone.today)
end
end
end
# Then call with:
User.attendances.for_date(Time.zone.today)
Depending on what you're doing with that attendance record, you could also have your for_date method take extra arguments.
Thanks to @engineersmnky for the correction. The method is undocumented, but that looks likes a mistake.first_or_initialize has been removed according to: https://api.rubyonrails.org/classes/ActiveRecord/Relation.html.
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