рельсы Активный выбор записи - PullRequest
3 голосов
/ 01 февраля 2011

В: выбор всех постов и связанных комментариев в одном запросе, загрузка комментариев в p.comments, но выбор только заголовка из комментариев

Пост

#db
create_table :posts do |t|
  t.string :title
  t.text :text

  t.timestamps
end

#modlel
class Post < ActiveRecord::Base
  has_many :comments
end

Комментарий

#db
create_table :comments do |t|
  t.string :title
  t.text :text
  t.integer :post_id

  t.timestamps
end

#model
class Comment < ActiveRecord::Base
  belongs_to :post
end

Что мне нужно: выделите все посты и связанные комментарии в одном запросе и загрузите отношение комментариев в p.comments, но выберите только заголовок из комментариев:

posts = Post.includes(:comments).select("posts.*, comments.title") 
posts.each do |p|
    puts(p.title)
    puts(p.text)
    p.comments.each do |c| 
        puts(c.title)
    end
end

Если первая часть выполнена.включает в себя:

posts = Post.includes(:comments).select('posts.*, comments.title')
posts.first.comments.loaded? # true

=> SELECT posts. id AS t0_r0, posts. title AS t0_r1, posts. text AS t0_r2, posts. created_at AS t0_r3, posts. updated_at AS t0_r4, comments. id AS t1_r0, comments. title AS t1_r1, comments. text AS t1_r2, comments. post_id AS t1_r3, comments. created_at AS t1_r4, comments. updated_at AS t1_r5 ОТ posts ВЛЕВО НАРУЖНОЕ СОЕДИНЕНИЕ comments ВКЛ comments. post_id = posts. id

полностью игнорирует выбор.

Хорошо, давайте попробуем присоединиться:

Post.joins(:comments).select('posts.*, comments.title')

=> ВЫБРАТЬ сообщения. *, Comments.title ОТ posts ВНУТРЕННЕЕ СОЕДИНЕНИЕ commentsON comments. post_id = posts. id

лучше, но нам нужен псевдоним таблицы -- Rails не обрабатывает отношения (почему?)

posts = Post.joins(:comments).select('posts.*, comments.title as comments_title').first

=> ВЫБРАТЬ сообщения. *, Comments.title как comments_title ОТ posts INNER JOIN comments ON comments. post_id =posts. id

posts.first.comments.loaded? # false
posts.first.comments_title # "Comment 1.1"

Хорошо, давайте попробуем ARel

p = Post.arel_table
c = Comment.arel_table
posts = Post.find_by_sql(
    p.join(c).on(c[:post_id].eq(p[:id])) \
     .project(p[:id],p[:title],c[:id],c[:title]).to_sql
)

=> SELECT posts. id, posts. title, comments. id, comments. title ОТ posts ВНУТРЕННЕЕ СОЕДИНЕНИЕ comments ВКЛ comments. post_id = posts. id

та же история - ActiveRecordне обрабатывает отношения.

есть предложения?

UPD

  1. если этот вопрос не имеет прямого решения, может бытьЕсть способ использовать некоторые методы AR для преобразования результирующего запроса.Потому что AR использует ARel, затем вызывает для него find_by_sql, а затем совершает магию, которую я не могу найти, когда и когда она была выполнена, и альт: генерируемые псевдонимы и ассоциации загрузки.

  2. Может быть, естьтакое решение для отношений один-к-одному?

    posts = Post.select ('posts.id, posts.title, comments.id, comments.title'). includes (: comment) posts.first.comment.loaded?# true

=> SELECT posts. id AS t0_r0, posts. title AS t0_r1, posts. text AS t0_r2, posts. created_at AS t0_r3, posts. updated_at AS t0_r4, comments. id AS t1_r0, comments. title AS t1_r1, comments. text AS t1_r2, comments. post_id AS t1_r3, comments. created_at AS t1_r4, comments. updated_at AS t1_r5 ОТ posts ВЛЕВО НАРУЖНОЕ СОЕДИНЕНИЕ comments ВКЛ. comments. post_id = posts. id

Ответы [ 3 ]

3 голосов
/ 01 февраля 2011

Боюсь, у меня нет предложений, потому что все описанные вами сценарии работают как задумано.

  • : includes => eager loading
  • : joins =>НЕ жду загрузки

Поэтому, если вы хотите, чтобы комментарии загружались напрямую, вы должны использовать include, объединения никогда не будут работать.

Следующее, жадная загрузка работает из-за определенного используемого синтаксиса selectв запросе SQL.Другими словами, указанный вами выбор будет игнорироваться при использовании вместе с включениями, и все столбцы будут загружены по умолчанию.

Я думаю, что описана самая последняя попытка добавить нужную вам функциональность в этом билете , где это решается добавлением параметра исключений в вызове include.Кажется, он еще не получил никакого ответа.

Лично я бы пошел на первый пример, который вы указали с помощью include, но пропустите select и согласитесь с тем фактом, что загружен comment.text.Лучше, чем любой другой сценарий, который я могу придумать.

0 голосов
/ 19 января 2013

Вы также можете использовать необработанный запрос SQL с активной записью, просто выполнив следующие действия:

post_comment = Post.find_by_sql("SELECT p.*, c.title AS comment_title FROM posts p JOIN comments c ON p.id=c.post_id").first
post_comment.comment_title

Немного хакерский, но, похоже, работает. ActiveRecord также предоставляет атрибут соединения, чтобы вы могли выполнять еще более сложные запросы. http://apidock.com/rails/ActiveRecord/Base/connection

0 голосов
/ 20 марта 2011

одним из решений является переключение на DataMapper вместо ActiveRecort. Это намного лучше, чем AR для меня. Использовать DataMapper вместо ActiveRecord

...