在rails中识别冲突的约会是为了更新而不是创建?

时间:2010-08-05 22:41:39

标签: ruby-on-rails

我写了这个冲突方法,以检查保存或创建的约会是否与已为特定培训师保存的约会冲突。在某人尝试在def createdef update中创建或更新现有约会后,首先会调用此方法。

在更新约会时有效但在创建时识别冲突。

任何想法?

  def is_conflicting()
    @new_appointment = person.appointments.build(params[:appointment])
    @appointments = Appointment.all(:conditions => { :date_of_appointment => @new_appointment.date_of_appointment, :doctor_id => @new_appointment.doctor_id})
    @appointments.each do |appointment|
      logger.info( appointment)
        if(@new_appointment.start_time < appointment.end_time && appointment.start_time < @new_appointment.end_time)
          return true 
        end
    end
    return false
  end

def create
    @appointment = person.appointments.build(params[:appointment])
      respond_to do |format|
        if(is_conflicting == false)
        if @appointment.save
....more code...
        end
        end
      end 
end

  def update
    @appointment = person.appointments.find(params[:id])
      respond_to do |format|
        if(is_conflicting == false)
          if @appointment.update_attributes(params[:appointment])
        .....more code...........
           end
         end
     end
   end

医生设置的表格部分。

  <p>
    <%= f.label :doctor_id %>
    <%= f.select :doctor_id, Doctor.find(:all, :order => "name").collect { |s|
        [s.name, s.id]} %>
  </p>

谢谢

4 个答案:

答案 0 :(得分:3)

你正在创造一个不可能的条件。你的条件是@new_appointment必须在appointment的end_time之后有一个start_time,在appointment的start_time之前有一个end_time ......这在逻辑上是不可能的。

我建议使用这个: http://api.rubyonrails.org/classes/ActiveSupport/CoreExtensions/Range/Overlaps.html

您需要根据@new_appointment.start_time..@new_appointment.end_time

之类的开始和结束时间创建范围

答案 1 :(得分:2)

build仅对已经存在于数据库中的对象进行操作:请参阅此处:http://apidock.com/rails/ActiveRecord/Associations/ClassMethods 在这里Ruby on Rails. How do I use the Active Record .build method in a :belongs to relationship?

另外,您的代码仍然可以使用一些重构。你的if语句将返回true或false,所以你为什么要特意返回true。此外,您不需要空的parens,您应该定义返回布尔值的方法,以问号结尾。最后,为什么要在create方法中构建一个新约会,然后在验证方法中创建一个新对象?

def conflicting? appointment
  @appointments = Appointment.all(:conditions => {... all of them})
  # Enumerable#any? returns true or false for the collection, 
  # so you dont have to specify a return value 
  # since its the last evaluation in the method
  @appointments.any?{|apt| appointment.start_time < apt.end_time && apt.start_time < appointment.end_time} #=> takes each appointment in appointments assigns to apt and checks against the passed in appointment object
end

然后在您的创建或更新方法

# assuming start/end times are form parameters coming from a view
@appointment = Appointment.new params[:appointment]
# substituting the lookup and update_attributes in the update action, obviously
@appointment.save unless conflicting? @appointment

答案 2 :(得分:2)

我认为您想要做的是将其推迟到约会模型的验证。请参阅http://api.rubyonrails.org/classes/ActiveRecord/Validations.html#M001391

在下面的代码段中,appt_range构建从开始到结束时间的范围,并且应该在创建/更新时调用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开始有一个appt - &gt; 10:30,他们希望将其移至10:15-> 10:45。更新将失败,因为当时已经为患者预约了Dr.也许添加patient_id而非当前患者会解决这种边缘情况,但您的测试应该涵盖这种可能性。

此外,我刚从头脑中扯出来,并没有测试过,所以你的里程可能会有所不同(你没有指定rails的版本,但是从代码中看到。看起来是2.3.x?)。但希望这会让你指向更好的方向..

编辑...

我构建了一个准系统/简单的导轨2.3.8应用程序,以测试它,它似乎适用于创建。看看http://github.com/doon/appt_test我也包括了dev db。

 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的博士有关。这些将在当前实施中重叠,因为它们共有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。由于您正在处理来自db的所有数据,因此在更新时不会发生这种情况。所以一切都有相同的日期

请参阅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 ...

来修复它

答案 3 :(得分:0)

那里的if else语句的数量很难看。我不知道逻辑是什么。尝试使用可枚举类进行此类工作。您可能只是在嵌套中出错。我发现return true通常会产生不良结果,即使你认为逻辑是稳定的,总是会返回true。

jruby-1.5.0 > apt1 = (1.hour.from_now..2.hour.from_now) #=> Fri, 06 Aug 2010 01:58:43 UTC +00:00..Fri, 06 Aug 2010 02:58:43 UTC +00:00 

jruby-1.5.0 > apt2 = (3.hour.from_now..4.hour.from_now)#=> Fri, 06 Aug 2010 03:59:07 UTC +00:00..Fri, 06 Aug 2010 04:59:07 UTC +00:00 

jruby-1.5.0 > @appointments = [apt1, apt2]

jruby-1.5.0 > @appointments.any?{|apt| (5.hour.from_now..6.hour.from_now).overlaps? apt } #=> false 

jruby-1.5.0 > @appointments.any?{|apt| (1.hour.from_now..3.hour.from_now).overlaps? apt } #=> true