Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use the joins method with first_or_initialize instead of find_or_initialize_by (Rails)?

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:

  • Test cases where the record only needs to be updated pass
  • Test cases where the attendance record doesn't exist yet (i.e. cases when I have to initialize) fail

The 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.

like image 922
reesaspieces Avatar asked Nov 16 '22 08:11

reesaspieces


1 Answers

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.

first_or_initialize has been removed according to: https://api.rubyonrails.org/classes/ActiveRecord/Relation.html. Thanks to @engineersmnky for the correction. The method is undocumented, but that looks likes a mistake.

like image 200
Ben Hull Avatar answered Nov 19 '22 10:11

Ben Hull