2012-06-21 41 views
0

我是Rails的新手,我知道这是一个简单的问题,但它让我发疯。 我的应用程序是用Ruby 1.9.2和Rails 3.1构建的。 我正在写一个应用程序来管理酒店式的房间预订。 所以这个类代表住宿:Rails和如何使子选择ids

class Accommodation < ActiveRecord::Base 
    has_many :reservations, :dependent => :destroy 
end 

而这一次的保留:

class Reservation < ActiveRecord::Base 
    belongs_to :accommodation 
end 

预订确实有一个名为accommodation_id场,其中住宿的ID存储

现在我必须在给定的日期范围内检查可用住宿,所以我写了这种方法:

def self.check_availability(params) 
    # This return the unavailables accommodations 
    ids_to_exclude = Reservation.select(:accommodation_id).where('start_date < ? AND end_date > ?', params[:reservation_end_date], params[:reservation_start_date]) 

    # HERE IS THE PROBLEM 
    accommodations = Accommodation.where("id NOT IN (?)", ids_to_exclude) 

    accommodations 
    end 

第一个查询作品,但第二个回报:

SELECT `accommodations`.* FROM `accommodation` WHERE (id NOT IN (NULL)) 

,我试图输出ids_to_exclude,而不是NULL

我究竟做错了什么?

更新:

我用Arel解决了这个问题。我写了下面

回答

0

ids_to_exclude需要的ID被提取

尝试在评论该解决方案

reservations_to_exclude = Reservation.select(:accommodation_id).where('start_date < ? AND end_date > ?', params[:reservation_end_date], params[:reservation_start_date]) 

reservations_to_exclude.each do |r| 
    ids_to_exclude << r.accommodation_id 
end 
+0

感谢您的回复,但是我发现Arel避免了'每个do'块的解决方案 –

0

你并不需要做两个数据库的访问对于你或许应该让一个范围,而你在那里。另外,我会打电话给路过params到模型的方法不好的形式,你应该更明确一些参数使事情更容易理解和维护:

class Accommodation < ActiveRecord::Base 
    def self.available_between(start_date, end_date) 
     joins(:reservations).where('reservations.start_date >= ? and reservations.end_date <= ?', end_date, start_date) 
    end 
end 

那么你可以说这事:

all_available = Accommodation.available_between(params[:reservation_start_date], params[:reservation_end_date]).all 

或本:

some = Accommodation.available_between(params[:reservation_start_date], params[:reservation_end_date]) 
        .where(some_other_conditions) 
        .limit(10) 
        .order(:price) 
+0

我首先想到了加入,但是由于在连接上保留了NULL,我放弃了这个想法。我刚刚尝试过你的方法,因为它构建了一个内部连接,所以没有任何结果,所以没有保留的Accoomodation不会出现 –

+0

@DrDuke:你可以'连接('...'外部连接保留'手动强制LEFT JOIN,并在'where'条件下添加'reservations.id为null'。 –

+0

是的,你是对的:我可以手动强制加入,但我正在寻找一些东西,而不用手动强制,也不用'to_sql'。无论如何,谢谢:) –

0

最后我发现阿雷尔解决方案:

reservations = Reservation.arel_table 

available_rooms = Accommodation.where(:id => reservations.project(:accommodation_id) 
     .where(reservations[:start_date].lt(params[:reservation_end_date])) 
     .where(reservations[:end_date].gt(params[:reservation_start_date]))) 
+0

这不会工作,因为我需要其ID不在子查询中的住宿。试图调整查询我有一个错误说:'不能访问Arel :: SelectManager' –