我的Event model
form
时间和to
时间在我的日程安排应用中,我希望在保存之前验证重叠时间。
我的观看图像如下;
Departure date: Dec 31, 2016
Day1
07:00 - 07:20 event1
10:30 - 11:30 event2
15:40 - 16:10 event3
[add event button]
Day2
08:15 - 09:05 event4
12:08 - 13:04 event5
14:00 - 14:25 event6
[add event button]
[save schedule button]
from
时间和to
时间可以同时更改和添加。
如果我尝试为07:05 - 07:30
添加(或更改为)Day1
,例如13:50 - 14:30
Day2
,我想要做的就是显示错误等等。
虽然我尝试了一些代码overlap
,between
,cover
并提及this post或this post等等,但我还没有能够将它们应用到我的代码中。
schema.rb
create_table "events", force: :cascade do |t|
t.time "from"
t.time "to"
t.integer "room_id"
...
create_table "rooms", force: :cascade do |t|
t.integer "schedule_id"
...
create_table "schedules", force: :cascade do |t|
t.integer "user_id"
t.date "departure_date"
...
提供以下模型:
class Schedule < ActiveRecord::Base
belongs_to :user
has_many :rooms, inverse_of: :schedule
accepts_nested_attributes_for :rooms, allow_destroy: true
...
class Room < ActiveRecord::Base
belongs_to :schedule, inverse_of: :rooms
has_many :events, inverse_of: :room
accepts_nested_attributes_for :events, allow_destroy: true
...
class Event < ActiveRecord::Base
belongs_to :room, inverse_of: :events
has_one :schedule, autosave: false, through: :room
...
_schedule_form.html.erb
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :title %>
<%= f.text_field :title, class: 'form-control' %>
<br>
<%= f.label :departure_date %>
<div class="input-group date" id="datetimepicker">
<%= f.text_field :departure_date, :value => (f.object.departure_date.strftime('%b/%d/%Y') if f.object.departure_date), class: 'form-control' %>
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
<script type="text/javascript">
$(function () {
$('#datetimepicker').datetimepicker({format:'MMM-DD-YYYY'});
});
</script>
<br>
<div id="room">
<%= f.simple_fields_for :rooms do |a| %>
<div id="room_<%= a.object.object_id %>">
<p class="day-number-element-selector"><b>Day <%= a.index.to_i + 1 %></b></p>
<%= a.simple_fields_for :events do |e| %>
<span class="form-inline">
<p>
<%= e.input :from, label: false %> -
<%= e.input :to, label: false %>
</p>
</span>
<%= e.input :title, label: false %>
<% end %>
</div>
<%= a.link_to_add "Add event", :events, data: {target: "#room_#{a.object.object_id}"}, class: "btn btn-primary" %>
<%= a.input :room %>
<% end %>
</div>
如果您能告诉我如何检查并显示错误,将不胜感激。
修改<!/强>
仍然无效
event.rb
class Event < ActiveRecord::Base
before_save :assign_date
belongs_to :room, inverse_of: :events
has_one :schedule, autosave: false, through: :room
validate :cannot_overlap_another_event
scope :in_range, -> range {
where('(\'from\' BETWEEN ? AND ?)', range.first, range.last)
}
scope :exclude_self, -> id { where.not(id: id) }
def cannot_overlap_another_event
range = Range.new from, to
overlaps = Event.exclude_self(id).in_range(range)
overlap_error unless overlaps.empty?
end
def overlap_error
errors.add(:overlap_error, 'There is already an event scheduled in this hour!')
end
development.log
....
Started POST "/schedules" for 218.33.213.91 at 2016-04-07 11:44:59 +0000
Processing by SchedulesController#create as HTML
...
[1m[35m (0.5ms)[0m begin transaction
[1m[36m (0.5ms)[0m [1mSELECT COUNT(*) FROM "events" WHERE ("events"."id" IS NOT NULL) AND (('from' BETWEEN '2016-04-07 07:00:00.000000' AND '2016-04-07 07:20:00.000000'))[0m
[1m[35m (0.3ms)[0m SELECT COUNT(*) FROM "events" WHERE ("events"."id" IS NOT NULL) AND (('from' BETWEEN '2016-04-07 07:05:00.000000' AND '2016-04-07 07:30:00.000000'))
[1m[36mSQL (0.5ms)[0m [1mINSERT INTO "schedules" ("title", "departure_date", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?)[0m [["title", "test title"], ["departure_date", "2016-04-10"], ["user_id", 1], ["created_at", "2016-04-07 11:45:00.061460"], ["updated_at", "2016-04-07 11:45:00.061460"]]
...
答案 0 :(得分:3)
好吧,如果您需要服务器端验证,可以在模型类中实现一些custom
验证器:
validate :cannot_overlap_another_event
接下来,您需要自己编写此方法的代码:
def cannot_overlap_another_event
range = Range.new from, to
overlaps = Appointment.exclude_self(id).in_range(range)
overlap_error unless overlaps.empty?
end
解释此代码的作用,您可以使用Range
和from
日期创建to
对象。然后,它使用辅助作用域来排除Event
本身并检查是否存在此范围内的事件。
scope :in_range, -> range {
where('(from BETWEEN ? AND ?)', range.first, range.last)
}
scope :exclude_self, -> id { where.not(id: id) }
overlap_error
是一种填充模型错误哈希以在屏幕上显示的方法:
def overlap_error
errors.add(:overlap_error, 'There is already an event scheduled in this hour!')
end
答案 1 :(得分:0)
在EventsController#Create
&amp; EventsController#Update
方法检查旧时间之间的新时间,如:
events = Event.where(:departure_date => event_params[:departure_date])
events.each do |event|
if event_params[:to].between?(event.to, event.from) || event_params[:from].between?(event.to, event.from)
flash[:error] = "Current Slot Time is already taken!"
redirect_to :back
return
end
end
答案 2 :(得分:0)
看看这个https://makandracards.com/makandra/984-test-if-two-date-ranges-overlap-in-ruby-or-rails。关键是要为重叠的兄弟姐妹定义一个范围。像这样的事情:
# Return a scope for all interval overlapping the given interval, including the given interval itself
named_scope :overlapping, lambda { |interval| {
:conditions => ["id <> ? AND (DATEDIFF(start_date, ?) * DATEDIFF(?, end_date)) >= 0", interval.id, interval.end_date, interval.start_date]
}}
你也可以查看这个宝石:https://github.com/robinbortlik/validates_overlap我认为它可以提供帮助。
答案 3 :(得分:0)
我在一个项目中遇到了同样的问题,我的解决方案是在模型中包含一个验证方法,所以我有一个休假期间的模型,我必须添加期间(失误),注意这些失误不重叠;假设您收集了一系列失效 (active_permits) 并且您刚刚从表单中获得了一个新的失效 (lapse),那么只有两个基本规则可以确定这两个时期是否重叠: lapse[:end] 必须更小 (即比 active_permit[:start] 更早发生)或 lapse[:start] 必须大于(发生晚于)active_permit[:end],因此除非满足这些规则之一,否则两个时期重叠。代码必须是这样的:
def lapse_validation(lapse)
error = 0
@active_permits.each do |active_permit|
unless (lapse[:end] < active_permit[:start] || lapse[:start] > active_permit[:end])
error = 1
errors.add(:new_permit, ": The dates you entered overlapp an existing leave period")
end
end
return error
end
您可以在此处找到有关重叠时期的更详细讨论:https://www.soliantconsulting.com/blog/determining-two-date-ranges-overlap/