rails 4通过关联在has_many上使用includes时保留连接对象顺序

时间:2014-12-10 23:02:57

标签: mysql ruby ruby-on-rails-4 eager-loading

使用Rails 4.1.6和Ruby 2.1.2并给出以下三个对象:

class FilmCollection
  has_many :film_collection_films, -> { order(:position) }
  has_many :films, through: :film_collection_films
end

class FilmCollectionFilm
  belongs_to :film_collection_film
  belongs_to :film
end

class Film
  has_many :film_collection_films
end

FilmCollections是电影的集合,其成员通过连接对象FilmCollectionFilm表示。 FilmCollectionFilm有一个位置列,因此集合的给定成员可以重新排序(像电影队列一样),因此我们需要连接对象而不是has_and_belongs_to_many。

当我尝试一次性加载多个集合的电影时,我的问题就出现了。

调用FilmCollection.first.films会给我一个看起来像这样的查询:

SELECT `film_collections`.* FROM `film_collections`
  ORDER BY `film_collections`.`id` ASC LIMIT 1
SELECT `films`.* FROM `films`
  INNER JOIN `film_collection_films` ON `films`.`id` = `film_collection_films`.`film_id`
  WHERE `film_collection_films`.`extensional_film_collection_id` = 1
  ORDER BY `film_collection_films`.`position` ASC

根据连接对象中的位置正确对影片进行排序。

但是调用FilmCollection.includes(:films).first.films会得到以下查询:

SELECT `film_collections`.* FROM `film_collections`
  ORDER BY `film_collections`.`id` ASC LIMIT 1
SELECT `film_collection_films`.* FROM `film_collection_films`
  WHERE `film_collection_films`.`film_collection_id` IN (1)
  ORDER BY `film_collection_films`.`position` ASC
SELECT `films`.* FROM `films`  WHERE `films`.`id` IN (1, 16, 53, 185)

收集正确的电影,但忽略查询第二部分的顺序。如果在使用.includes()进行加载加载但仍然没有n + 1查询时,如何保留各个连接对象的排序?

1 个答案:

答案 0 :(得分:2)

事实证明,你可以通过明确地包括连接对象以及通过它的关联来解决这个问题。下面的行将正确选择所有内容,并保留连接对象上存在的任何条件/顺序。

FilmCollection.includes(film_collection_films: :film)

如果只做FilmCollection.includes(:films),那么无论如何它都会选择FilmCollectionFilms然后只是扔掉它们。因此,上面的代码将完成相同数量的工作,但正确构建查询。