【问题标题】:Rails: Filter nested attribute in scopeRails:过滤范围内的嵌套属性
【发布时间】:2016-12-13 10:45:43
【问题描述】:

我的模型有以下关系:

Building 有很多 Rooms 有很多 Beds 有很多 Accommodations,

我想渲染所有Buildings,但在日期范围内创建Accommodations。从其他答案中,我了解到我需要为 Building 模型创建范围,但我不明白如何在此范围内过滤此类嵌套属性。

编辑:

假设我有Building 1、2 和 3。每个Building 都有自己的Rooms,Beds 有Accommodations。假设只有Building 1 有一个Accommodation 在范围内。所以返回的数据一定是:

Building 1
    ...
    Room n
      ...
        Bed n
         Accommodation that is in range
        Bed n+1
      ...
    ...
Building 2 (accommodation arrays in beds are empty since there are no accommodations that are in range)
    ...
Building 3 (accommodation arrays is beds are empty since there are no accommodations that are in range)
    ...

【问题讨论】:

    标签: ruby-on-rails activerecord rails-activerecord


    【解决方案1】:

    为了避免在模型中编写过度嵌套的连接查询setup indirect relations

    class Building
      has_many :rooms
      has_many :beds, though: :rooms
      has_many :accommodations, through: :beds
    end
    
    class Room
      belongs_to :building
      has_many :beds
      has_many :accommodations, through: :beds
    end
    
    class Bed
      belongs_to :room
      has_many :accommodations
      has_one :building, through: :room
    end
    
    class Accommodation
      belongs_to :bed
      has_one :room, through: :bed
      has_one :building, through: :room
    end  
    

    这将让您直接查询building.accommodations,ActiveRecord 将为您加入中间表。

    然后在查询时使用Range

    Building.includes(:accommodations)
            .where(accommodations: { created_at: start_time..end_time })
    

    这将使用大多数数据库驱动程序构造一个WHERE 'accommodations.created_at' BETWEEN ...

    难题的另一个关键部分是您没有过滤嵌套属性。相反,上面发生的是您使用的是join and setting conditions on the joined table

    【讨论】:

    • 这仅包括住宿创建日期在范围内的建筑物,但我只想过滤住宿并返回所有建筑物
    • 然后改用左外连接。
    【解决方案2】:
    class Building
      scope :accomodations_for_dates, lambda { |start_date, end_date|
        joins(rooms: [beds: :accomodations]).where("accomodations.created_at >= #{start_date} AND accomodations.end_date <= #{end_date}")
      }
    end
    

    对于 PostgreSQL 和 MySQL 数据库,您可以使用BETWEEN

    "accomodations.created_at BETWEEN #{start_date} AND #{end_date}"
    

    要返回所有建筑物,但要使用过滤后的住宿,包括:

    includes(rooms: [beds: :accomodations]).where(accomodations: { created_at: start_date..end_date })
    

    【讨论】:

    • 这仅包括住宿创建日期在范围内的建筑物,但我只想过滤住宿并返回所有建筑物
    • @KudayarPirimbaev 不幸的是,您的评论对我来说毫无意义。你能改写一下吗?
    • 假设我有 1 号、2 号、3 号楼,1 号楼有 1 个在范围内的住宿,1 个不在范围内,其他建筑物根本没有。您的解决方案将返回 1 号楼和所有住宿,而我需要返回 1 号楼以及范围内的住宿和其他建筑物
    • @KudayarPirimbaev 那么结果会是什么?有配套住宿的大楼?
    • 我的意思是我需要在建筑物内过滤住宿,而不仅仅是满足条件的建筑物
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-07
    • 1970-01-01
    • 1970-01-01
    • 2023-03-31
    • 2020-01-20
    • 1970-01-01
    相关资源
    最近更新 更多