Как мне написать ActiveRecord, когда есть несколько ассоциаций? - PullRequest
5 голосов
/ 18 октября 2011

Модели такие:

class Contract < ActiveRecord::Base  
  belongs_to :buyer, :class_name => 'Customer', :foreign_key => 'buyer_customer_id' 
  belongs_to :user, :class_name => 'Customer', :foreign_key => 'user_customer_id'
  belongs_to :currency
end  

class Customer < ActiveRecord::Base  
  has_many :as_buyer_in_contracts, :class_name => 'Contract', :foreign_key => 'buyer_customer_id'  
  has_many :as_user_in_contracts, :class_name => 'Contract',:foreign_key => 'user_customer_id'  
end

class Currency < ActiveRecord::Base
  has_many :contracts
end

А ниже приведены данные:

Contract
+----+-------------------+------------------+-------------+
| id | buyer_customer_id | user_customer_id | currency_id |
+----+-------------------+------------------+-------------+
|  1 |         1         |        3         |      3      |
|  2 |         2         |        2         |      2      |
|  3 |         2         |        1         |      2      |
|    |                   |                  |             |


Customer
+----+-------------------+
| id |       name        |
+----+-------------------+
|  1 |    Terry Brown    |
|  2 |    Tom Green      |
|  3 |    Kate White     |
|    |                   |

Currency
+----+-------------------+
| id |       name        |
+----+-------------------+
|  1 |        EUR        |
|  2 |        USD        |
|  3 |        JPY        |
|    |                   |

И теперь я хочу найти все контракты, которые были подписаны с клиентом по имени «Терри», например:

Contract.where("customers.name like '%Terry%'").includes(:buyer,:user)
#I want 1 and 3, but it can only get 1
Contract.where("customers.name like '%Terry%'").includes(:user, :buyer)
#If I write "user" before "buyer", then I can only get 3

Кто-то сказал мне, что это может работать так:

Contract.join(:customer).where("customers.name like '%terry%'").includes(:user,:buyer)
#It works fine.

Я пытался, и это работает. Но в дальнейшем, когда модель Контракта принадлежит другой модели, такой как currency_id, описанный выше метод не может работать снова.

Contract.join(:customer).where("customers.name like '%terry%'").includes(:currency, :user, :buyer)
#>>Mysql2::Error: Unknown column 'customers_contracts.id' in 'field list': ...

Ответы [ 2 ]

2 голосов
/ 20 октября 2011

Это потому, что вы не должны использовать соединения в сочетании с включениями. Это не было подчеркнуто достаточно (и нет никакого предупреждения в рельсах), но

выберите, объединяет, группирует, имеет и т. Д. НЕ РАБОТАЕТ с включает в себя!

Вы можете получить результаты, но только случайно. И есть вероятность, что он сломается раньше, чем позже.

Похоже, что есть некоторые несоответствия с включениями ...

Если вам нужно использовать обычное внешнее объединение с activerecord> = 3.0 (что здесь имеет место), используйте превосходный камень squeel. Это действительно дает силу Арела.

Попробуйте (с установленным squeel):

Contract.joins{buyer.outer}.joins{user.outer}.where("name like '%terry%'")

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

0 голосов
/ 20 октября 2011

Вы пытались использовать соединения: покупатель или: пользователь вместо: клиент?

Модель вашего контракта не имеет: атрибут / отношение клиента

Contract.join(:buyer).where("customers.name like '%terry%'").includes(:currency, :user, :buyer)

я предполагаю, что это будет эквивалентно чему-то вроде

Contract.join("INNER JOIN customers ON customers.id = contracts.buyer_customer_id").where("customers.name like '%terry%'").includes(:currency, :user, :buyer)

проверьте ваш лог-файл, чтобы точно определить, какой sql генерируется в каждом случае - log / development.log

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