我认为您想要做的是将其下推至约会模型上的验证。见http://api.rubyonrails.org/classes/ActiveRecord/Validations.html#M001391。
在 appt_range 下面的 sn-p 中构建了一个从开始到结束时间的范围,并且应该在创建/更新时调用 validate 方法。
也许是这样的。
class Appointment < ActiveRecord::Base
def appt_range
start_time..end_time
end
...rest of code...
protected
def validate
@appointments = Appointment.all(:conditions => { :date_of_appointment => date_of_appointment, :doctor_id => doctor_id})
errors.add_to_base("Appointment Conflict") if @appointments.any? {|appt| appt.appt_range.overlaps? appt_range}
end
end
然后你的控制器就会有
def create
@appointment = person.appointments.new(params[:appointment]))
if @appointment.save
...
end
end
def update
@appointment = person.appointments.find(params[:id])
if @appointment.update_attributes(params[:appointment])
...
end
end
但是话虽如此(这也是您原始代码中的问题),但存在竞争条件/问题。假设患者有一个从 10:00 -> 10:30 开始的 appt,并且他们想将其移至 10:15->10:45。由于当时已经为患者预约了医生,因此更新将失败。也许添加 patient_id 而不是当前患者可以解决这种极端情况,但您的测试应该涵盖这种可能性。
另外,我只是把它从脑海中浮现出来,还没有测试过,所以你的里程可能会有所不同(你没有指定 rails 的版本,但是从代码来看。看起来是 2.3.x?)。但希望这能为您指明更好的方向..
编辑...
我构建了一个准系统/简单的 rails 2.3.8 应用程序来测试它,它似乎可以在 create 上运行。看看http://github.com/doon/appt_test 我也包含了开发数据库。
rake db:migrate
== CreateAppointments: migrating =============================================
-- create_table(:appointments)
-> 0.0019s
== CreateAppointments: migrated (0.0020s) ====================================
Loading development environment (Rails 2.3.8)
ruby-1.8.7-p299 > a=Appointment.new(:patient_id=>1, :doctor_id=>1, :date_of_appointment=>'08/10/2010', :start_time=>" 2010-08-10 8:00", :end_time=>"2010-08-10 10:00")
=> #<Appointment id: nil, patient_id: 1, doctor_id: 1, date_of_appointment: "2010-08-10", start_time: "2000-01-01 08:00:00", end_time: "2000-01-01 10:00:00", created_at: nil, updated_at: nil>
ruby-1.8.7-p299 > a.save
Appointment Load (0.2ms) SELECT * FROM "appointments" WHERE ("appointments"."doctor_id" = 1 AND "appointments"."date_of_appointment" = '2010-08-10')
Appointment Create (0.5ms) INSERT INTO "appointments" ("end_time", "created_at", "updated_at", "patient_id", "doctor_id", "date_of_appointment", "start_time") VALUES('2000-01-01 10:00:00', '2010-08-07 22:20:33', '2010-08-07 22:20:33', 1, 1, '2010-08-10', '2000-01-01 08:00:00')
=> true
ruby-1.8.7-p299 > b=Appointment.new(:patient_id=>1, :doctor_id=>1, :date_of_appointment=>'08/10/2010', :start_time=>" 2010-08-10 9:00", :end_time=>"2010-08-10 11:00")
=> #<Appointment id: nil, patient_id: 1, doctor_id: 1, date_of_appointment: "2010-08-10", start_time: "2000-01-01 09:00:00", end_time: "2000-01-01 11:00:00", created_at: nil, updated_at: nil>
ruby-1.8.7-p299 > b.save
Appointment Load (0.4ms) SELECT * FROM "appointments" WHERE ("appointments"."doctor_id" = 1 AND "appointments"."date_of_appointment" = '2010-08-10')
=> false
ruby-1.8.7-p299 > b.errors['base']
=> "Appointment Conflict"
ruby-1.8.7-p299 > c=Appointment.new(:patient_id=>1, :doctor_id=>1, :date_of_appointment=>'08/10/2010', :start_time=>" 2010-08-10 11:00", :end_time=>"2010-08-10 12:00")
=> #<Appointment id: nil, patient_id: 1, doctor_id: 1, date_of_appointment: "2010-08-10", start_time: "2000-01-01 11:00:00", end_time: "2000-01-01 12:00:00", created_at: nil, updated_at: nil>
ruby-1.8.7-p299 > c.save
Appointment Load (0.3ms) SELECT * FROM "appointments" WHERE ("appointments"."doctor_id" = 1 AND "appointments"."date_of_appointment" = '2010-08-10')
Appointment Create (0.4ms) INSERT INTO "appointments" ("end_time", "created_at", "updated_at", "patient_id", "doctor_id", "date_of_appointment", "start_time") VALUES('2000-01-01 12:00:00', '2010-08-07 22:21:39', '2010-08-07 22:21:39', 1, 1, '2010-08-10', '2000-01-01 11:00:00')
=> true
这是我的 Appointment 类(我使用了 validate :symbol 方法)
class Appointment < ActiveRecord::Base
validate :conflicting_appts
def appt_range
start_time..end_time
end
private
def conflicting_appts
@appointments = Appointment.all(:conditions => { :date_of_appointment => date_of_appointment, :doctor_id => doctor_id})
errors.add_to_base("Appointment Conflict") if @appointments.any? {|appt| appt.appt_range.overlaps? appt_range}
end
end
同样在玩这个,虽然你应该确保测试另一种情况。患者 A 在 10 至 11 日与 A 医生有约会。患者 B 从 11 到 12 日与 Dr a 进行了约会。这些将在当前实现中重叠,因为它们共享 11 个共同点,并将被标记为冲突。
所以我不确定为什么它在创建时不起作用,如果您想显示您的代码,我们可以查看它。
好的,我知道它为什么不起作用了,这与开始时间和结束时间有关。看看这个。
来自测试...(在验证中添加一个记录器向我展示了这一点)。
appt.range == Sat Jan 01 09:06:00 UTC 2000..Sat Jan 01 21:10:00 UTC 2000 , my range = Tue Aug 10 09:15:00 UTC 2010..Tue Aug 10 14:19:00 UTC 2010
appt.range == Sat Jan 01 22:06:00 UTC 2000..Sat Jan 01 23:06:00 UTC 2000 , my range = Tue Aug 10 09:15:00 UTC 2010..Tue Aug 10 14:19:00 UTC 2010
appt.range == Sat Jan 01 09:30:00 UTC 2000..Sat Jan 01 12:14:00 UTC 2000 , my range = Tue Aug 10 09:15:00 UTC 2010..Tue Aug 10 14:19:00 UTC 2010
appt.range == Sat Jan 01 09:31:00 UTC 2000..Sat Jan 01 12:20:00 UTC 2000 , my range = Tue Aug 10 09:15:00 UTC 2010..Tue Aug 10 14:19:00 UTC 2010
当您从数据库中提取日期部分时,日期部分被截断并设置为 2000 年 1 月 1 日。因此,当您查询数据库时,您正在查看的范围不会重叠对于 2010 年的日期。将开始/结束时间设为日期时间将解决问题,因为这样日期将再次重要。否则需要修改 appt_range 以将日期调整回 date_of_appointment。更新时不会发生这种情况,因为您正在处理数据库中的所有数据。所以一切都有相同的日期
见http://github.com/doon/EMR/commit/b453bb3e70b5b6064bb8693cf3986cf2219fbad5
def appt_range
s=Time.local(date_of_appointment.year, date_of_appointment.month, date_of_appointment.day, start_time.hour, start_time.min, start_time.sec)
e=Time.local(date_of_appointment.year, date_of_appointment.month, date_of_appointment.day, end_time.hour, end_time.min, end_time.sec)
s..e
end
通过强制开始和结束时间使用 date_of_appointment 来修复它...