Подтвердить что ты не робот

Rails-запрос объединяет таблицу объединений с псевдонимом

У меня есть модель Edge, которая принадлежит другой модели Node дважды через разные внешние ключи:

def Edge < ActiveRecord::Base
    belongs_to :first, class_name: 'Node'
    belongs_to :second, class_name: 'Node'
end

И я хочу выполнить этот запрос с помощью ActiveRecord:

SELECT * FROM edges INNER JOIN nodes as first ON first.id = edges.first_id WHERE first.value = 5

Я нашел способ присоединиться к ассоциации с помощью метода .joins():

Edge.joins(:first)

Но это вызывает запрос с использованием имени таблицы, а не имени ассоциации, поэтому в методе .where() я должен явно использовать имя таблицы, которое нарушает абстракцию ассоциации.

Edge.joins(:first).where(nodes: {value: 5})

Я также могу явно использовать SQL-запрос в методе .joins() для определения псевдонима модели:

Edge.joins('INNER JOIN nodes as first ON nodes.id = edges.first_id')

Но это ломает еще большую абстракцию.

Я думаю, что должен быть способ автоматического определения псевдонима таблиц при соединении. Или, может быть, способ написать такую ​​функцию сам. Что-то вроде:

def Edge < ActiveRecord::Base
    ...
    def self.joins_alias
        # Generate something like 
        # joins("INNER JOIN #{relation.table} as #{relation.alias} ON #{relation.alias}.#{relation.primary_key} = #{table}.#{relation.foreign_key}")
    end
end

Но я не мог найти никакой информации о доступе к информации о конкретных отношениях, таких как имя, внешний ключ и т.д. Итак, как я могу это сделать?

Также мне кажется странным, что такая очевидная особенность настолько сложна, что даже Rails уже на четвертой основной версии. Может, я что-то упустил?

4b9b3361

Ответ 1

Что касается Rails 4.2.1, я считаю, что вы просто не можете предоставить псевдоним при использовании joins из ActiveRecord.

Если вы хотите запросить ребра первым node, вы можете сделать это так же, как вы заявили:

Edge.joins(:first).where(nodes: {value: 1})
SELECT "edges".* FROM "edges" INNER JOIN "nodes" ON "nodes"."id" = "edges"."first_id" WHERE "nodes"."value" = 1

Но если вам нужно запросить использование обоих узлов, вы все равно можете использовать joins следующим образом:

Edge.joins(:first, :second).where(nodes: {value: 1}, seconds_edges: {value: 2})
SELECT "edges".* FROM "edges" INNER JOIN "nodes" ON "nodes"."id" = "edges"."first_id" INNER JOIN "nodes" "seconds_edges" ON "seconds_edges"."id" = "edges"."second_id" WHERE "nodes"."value" = 1 AND "seconds_edges"."value" = 2