2015-03-31 2 views
13

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

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 уже находится на 4-й основной версии. Может, я что-то упустил?

ответ

5

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

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

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