Dér*_*tti 1 ruby ruby-on-rails
这两个点在 Rails 中意味着什么
有一个这样的函数:
def period
start_date..end_date
end
Run Code Online (Sandbox Code Playgroud)
它是什么?这会生成一个数组吗?我看到这个课程中使用了这个
class Booking < ApplicationRecord
# ... some code is skipped here for simplicity's sake
validate :validate_other_booking_overlap
def period
start_date..end_date
end
private
def validate_other_booking_overlap
other_bookings = Booking.all
is_overlapping = other_bookings.any? from |other_booking|
period.overlaps?(other_booking.period)
end
errors.add(:overlaps_with_other) if is_overlapping
end
end
Run Code Online (Sandbox Code Playgroud)
eng*_*nky 11
这是 ruby 中包含有限的字面语法Range。
\n\nRange 表示一个区间\xe2\x80\x94 一组具有开始和结束的值...当用作迭代器时,Range 返回序列中的每个值。
\n
范围可以表示为包含两个点 ( ..) 或排除三个点 ( ...)。独占范围不包括最后一个元素。
在 2.6 之前,所有范围都是有限的(语法上需要开始和结束;尽管结束可以是Float::INFINITY[与 -Float::INFINITY开始相反],所以从技术上讲,并非所有范围都是“有限的”)。
在 2.6 中,“无限范围”是使用语法实现的x..。
从 2.7 开始,范围可以使用语法“无开头”..x
根据文档,无限范围和无限范围都是“半无限”,可以表示为包含或排除。(说实话,我不确定排他的无限范围是什么;尽管语法具有创造性的用途[见下文])
\n作为旁注:( TL;DR )
\nBooking从性能的角度来看,循环遍历每个都是一个坏主意。
更好的想法是使用以下方法将其卸载到数据库:
\ndef validate_other_booking_overlap\n overlaps_bookings = Booking\n .where(start_date: period)\n .or(Booking.where(end_date: period))\n .or(\n Booking.where(\n Booking.arel_table[:start_date].gt(start_date)\n .and(Booking.arel_table[:end_date].lt(end_date))))\n .where.not(id: id)\n .exists?\n errors.add(:overlaps_with_other) if overlaps_bookings\nend\nRun Code Online (Sandbox Code Playgroud)\nRails (Arel) 将 WHERE 条件中的有限范围转换为 SQL BETWEEN 子句,因此此查询条件将是:
\nWHERE\n((bookings.start_date BETWEEN \'####-##-##\' AND \'####-##-##\' \n OR bookings.end_date BETWEEN \'####-##-##\' AND \'####-##-##\')\n OR (\n bookings.start_date > \'####-##-##\' \n AND bookings.end_date < \'####-##-##\'\n ))\n AND bookings.id != # -- IS NOT NULL on create\nRun Code Online (Sandbox Code Playgroud)\n在较新版本的 Rails(>=6.0.3 和 ruby>=2.7)中:
\nBooking.arel_table[:start_date].gt(start_date)\n .and(Booking.arel_table[:end_date].lt(end_date))\nRun Code Online (Sandbox Code Playgroud)\n可以用这个代替
\nBooking.where(start_date: start_date..., end_date:...end_date))\nRun Code Online (Sandbox Code Playgroud)\n因为现在Rails(Arel)对待范围如下:
\nx..y属性介于 x 和 y 之间(例如属性 >= x AND 属性 <= y )x...y属性 >= x AND 属性 < yx..属性 >= xx...attribute >= x (这是 Rails 团队根据无限独占范围的 Ruby 实现/解释做出的决定)where.not(attribute: ..x)属性 > x..x属性 <= x...x属性 < x...现在我相信我们已经回到原点了。
\n更新
\n由于@max\'s 的评论,您可以使用该OVERLAPS函数(在数据库支持的情况下),如下所示
def validate_other_booking_overlap\n left = Arel::Nodes::Grouping.new(\n [Booking.arel_table[:start_date],\n Booking.arel_table[:end_date]])\n right = Arel::Nodes::Grouping.new(\n [Arel::Nodes::UnaryOperation.new(\n \'DATE\', \n [Arel::Nodes.build_quoted(start_date - 1)]),\n Arel::Nodes::UnaryOperation.new(\n \'DATE\', \n [Arel::Nodes.build_quoted(end_date + 1)])])\n condition = Arel::Nodes::InfixOperation.new(\'OVERLAPS\', left, right)\n errors.add(:overlaps_with_other) if Booking.where.not(id: id).where(condition).exists?\nend\nRun Code Online (Sandbox Code Playgroud)\n