I have a 3 models
class Task < ActiveRecord::Base
belongs_to :user, :dependent => :destroy
has_many :clock_ins
accepts_nested_attributes_for :clock_ins, :allow_destroy => true
end
class ClockIn < ActiveRecord::Base
belongs_to :task, :dependent => :destroy
has_one :clock_out
end
class ClockOut < ActiveRecord::Base
belongs_to :clock_in, :dependent => :destroy
end
Currently I can create a ClockIn
for each Task
.
When I start a new ClockIn
, I want to create a ClockOut
for whichever Task
is c开发者_开发问答urrently open.
How do I search my tasks for one with a ClockIn
that does not have a ClockOut
?
Solution
Combine Models
Fix destroys
Iterate all tasks then update
task.clocks.where(:clock_out => nil).first.update_attribute :clock_out, Time.now
As you have a one-to-one relation between clock_in and clock_out, switching has_one
and belongs_to
shouldn't make much difference. What is the data stored in clock_in and clock_out? if is is just a datetime you might want to consider merging the two models and using a single table. If you do not want to change any of the modeling LEFT OUTER JOIN
is the way to go. So you have three options:
Merge the models:
class Task < ActiveRecord::Base belongs_to :user has_many :working_hours accepts_nested_attributes_for :working_hours, :allow_destroy => true end class WorkingHour < ActiveRecord::Base belongs_to :task # has two columns clock_in_time and clock_out_time end task.working_hours.where(:clock_out_time => nil).first.update_attribute(:clock_out_time => Time.now)
Switch has_one and belongs_to
class ClockIn < ActiveRecord::Base belongs_to :task belongs_to :clock_out # now clock_ins will have column check_out_id end class ClockOut < ActiveRecord::Base has_one :clock_in # doen't have any check_in_id end task.clock_ins.where(:clock_out_id => nil).first.create_clock_out(:time => Time.now)
Go with the outer join
class ClockIn < ActiveRecord::Base belongs_to :task has_one :clock_out scope :has_no_check_out, { :joins => "LEFT OUTER JOIN clock_outs ON clock_ins.id = clock_outs.clock_in_id" :conditions => "clock_outs.clock_in_id IS NULL" } end task.clock_ins.has_no_check_out.first.create_clock_out(:time => Time.now)
Please note that your :dependent => :destroy
definitions don't look very good. Currently if you destroy a clock_out, corresponding clock_in will be destroyed, resulting in corresponding task being destroyed and leaving other clock_ins related with the task orphaned. Also when the task is destroyed, it will result in user being destroyed. This seems to be very odd chain of events. Destroying a clock_out, results in destroying a user, Ouch!
You should use :dependent => :destroy
like following:
# user.rb
has_many :tasks, :dependent => :destroy
# task.rb
has_many :clock_ins, :dependent => :destroy
# clock_in.rb
has_one :clock_out, :dependent => :destroy
精彩评论