2010-08-06 3 views
4

Кажется, существует несколько способов обработки множественной ассоциации внешних ключей. Каждый путь, к которому я подошел, имеет свои обратные спины, и, поскольку я новичок в Rails, я убежден, что другие сталкиваются с похожим сценарием, и я, вероятно, работаю над чем-то, давно решенным.Rails - Ассоциация с несколькими индексами

Мой вопрос:

Что бы быть эффективным способом обработки множественного индекса ключа связи, сохраняя при этом все остальные Rails SQL модификаторы (например: включить и т.д.)?

Мой сценарий:

У меня есть таблица ассоциации следующим образом (упрощенный), который используется для подключения людей к другим людям с помощью ссылки:

People 
+----+-----------+ 
| id | name  | 
+----+-----------+ 
| 1 | Joe  | 
+----+-----------+ 
| 2 | Sally  | 
+----+-----------+ 
| 3 | Bob  | 
+----+-----------+     

Links 
+----+-----------+---------+ 
| id | origin_id | rcvd_id | 
+----+-----------+---------+ 
| 1 | 2   | 1  | 
+----+-----------+---------+ 
| 2 | 1   | 3  | 
+----+-----------+---------+       
| 3 | 3   | 2  | 
+----+-----------+---------+       

Из строки 1 из вышеуказанных ссылок таблицу, можно видеть, что Лицо (Sally = 2) связано с другим Лицом (Joe = 1).

Мне легко найти все ссылки на Лица, если мой внешний ключ был «origin_id». Но это означало бы только «Люди, получающие связь». В моем сценарии мне нужно видеть все ссылки независимо от того, были ли они созданы или получены Лицом. Если, например, я должен был попросить все ссылки Салли (Sally = 2), то результат я хотел бы:

Links 
+----+-----------+---------+ 
| id | origin_id | rcvd_id | 
+----+-----------+---------+ 
| 1 | 2   | 1  | 
+----+-----------+---------+       
| 3 | 3   | 2  | 
+----+-----------+---------+       

Поэтому у меня есть 2 ключа индекса, как «origin_id» и «rcvd_id».

Один из способов это может быть решена с помощью является метод:

class Person < ActiveRecord::Base 
    has_many :link_origins, :class_name => "Link", :foreign_key => :origin_id, :dependent => :destroy 
    has_many :link_rcvds, :class_name => "Link", :foreign_key => :rcvd_id, :dependent => :destroy 

def links 
    origin_person + rcvd_person 
end 

Однако, это не эффективно. Например, для этого требуется сбор всей коллекции из базы данных, и только тогда работает метод paginate (я использую жемчужину will_paginate), которая побеждает точку, поскольку paginate ускоряет процесс, ограничивая количество названных записей. Не ограничивайте записи после завершения всей коллекции.

Кроме того, вышесказанное не позволит мне позвонить, например, Joe.links (: first) .origin_id.name. Не совсем этот код, но это означает, что я не мог вызывать данные Person о origin_id выбранной ссылки, поскольку метод ссылок не знает, что origin_id связан с таблицей People.

Пока что наиболее работоспособным решением является: finder_sql.

class Person < ActiveRecord::Base 
    has_many :links, :finder_sql => 'SELECT * FROM links WHERE (links.origin_id = #{id} or links.rcvd_id = #{id})' 

Это дает все ссылки, где person_id матчей либо Links.origin_id или Links.rcvd_id.

Недостатком этой опции является то, что использование: finder_sql исключает все другие модификаторы sql, поскольку Rails не знает, как анализировать и модифицировать SQL, который вы предоставляете. Например, я не смог бы использовать параметр include: with: finder_sql.

Итак, прямо сейчас я использую: finder_sql, solution. Но, похоже, может быть, что эта ассоциация не может быть выполнена таким образом, что мне не нужен: finder_sql. Например, существует ли способ написать пользовательскую строку sql, сохраняя при этом модификаторы sql Rails, которые снабжают Active Record.

Любые идеи по вышесказанному?

ответ

3

Я нашел решение этого вопроса, однако оказалось, что я, вероятно, задал неправильный вопрос. Я не нашел, чтобы иметь несколько индексных ключей, как я просил, не внедряя какой-то пользовательский sql, который разбивает разные помощники рельсов.

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

belongs_to :rcvd_person, :class_name => 'Person', :foreign_key => :rcvd_id 
belongs_to :origin_person, :class_name => 'Person', :foreign_key => :origin_id 

И пользовательских SQL заявление:

class Person... 
    has_many :links, :finder_sql => 'SELECT * FROM links WHERE origin_id = #{id} OR rcvd_id = #{id}' 
end 

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

<% person.links.each do |link| %> 

    <% if link.origin_id == person.id %> 
    <%= link.rcvd_person.given_name %> 
    <% else %> 
    <%= link.origin_person.given_name %> 
    <% end %> 

<% end %> 
1

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

Однако, если вы просто хотите person.links, Rails 3 обеспечивает способ, который лучше, чем: finder_sql

class Link 
    def self.by_person(person) 
    where("origin_id => :person_id OR rcvd_id => :person_id", :person_id => person.id 
    end 
end 

class Person < ActiveRecord::Base 
    # You can include the has_many relationships if you want, or not 

    def links 
    Link.by_person(self) 
    end 
end 

Это позволяет делать такие вещи, как @ person.links.limit (3) (который в настоящее время кажется, сломаны при использовании: finder_sql)

+0

Спасибо Lelon. Я еще не прыгнул Rails3 (я знаю, смущаю), но в ближайшее время посмотрю на это. – Oscar

Смежные вопросы