Rails - ассоциация с несколькими индексами - PullRequest
5 голосов
/ 06 августа 2010

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

Мой вопрос:

Каким был бы эффективный способ обработки связи с несколькими индексными ключами, при этом сохраняя все другие модификаторы sql Rails (такие как: include и т. Д.)?

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

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

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 приведенной выше таблицы ссылок можносм., что Person (Sally = 2) связан с другим Person (Joe = 1).

Мне легко найти все ссылки Persons, если мой внешний ключ был "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 выбранной ссылки, так как метод links не знает, что 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 с: finder_sql.

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

Есть идеи по поводу вышеизложенного?

Ответы [ 2 ]

3 голосов
/ 16 августа 2010

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

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

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 голос
/ 01 декабря 2010

Я не уверен, что вы действительно можете поддерживать связь с несколькими ключами в одной таблице, потому что 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)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...