#ruby-on-rails-3 #activerecord #associations
#ruby-on-rails-3 #activerecord #ассоциации
Вопрос:
Модели такие:
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': ...
Ответ №1:
Это потому, что вы не должны использовать объединения в сочетании с включениями. Это недостаточно подчеркнуто (и в rails нет предупреждения), но
выбрать, объединить, сгруппировать, иметь и т. Д. НЕ РАБОТАЙТЕ С включениями!
Вы можете получить результаты, но только случайно. И скорее всего, он сломается раньше, чем позже.
Кажется, есть также некоторая несогласованность с включениями…
Если вам нужно использовать обычное внешнее соединение с activerecord> = 3.0 (как в данном случае), используйте отличный камень squeel.Это действительно дает силу Arel.
Попробуйте (с установленным squeel) :
Contract.joins{buyer.outer}.joins{user.outer}.where("name like '%terry%'")
Готовое соединение выполняет только внутреннее соединение, что исключает непересекающуюся таблицу, что делает вашу цель здесь невозможной: покупатель и пользователь могут быть взаимоисключающими…
Комментарии:
1. НЕ РАБОТАЕТ? Я использовал их вместе в других случаях, вы имеете в виду, что он может сломаться в любой момент?
2. Это хороший кандидат на корень проблемы, но ваш ответ вначале немного расплывчатый (select и т. Д. Не Работают с включениями, несоответствие). Не могли бы вы добавить более четкое объяснение этих моментов и / или указать на авторитетное объяснение?
3. все еще в отпуске… попытаюсь немного подкрепить свой ответ, когда вернусь.
Ответ №2:
вы пробовали использовать joins :buyer или :user вместо :customer ?
в вашей контрактной модели нет атрибута / отношения:customer
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
Комментарии:
1. извините, я забыл добавить отношение модели… И файл журнала составляет около 40 или 50 строк, потому что мое фактическое состояние очень сложное. Я был полностью смущен ими.