У меня есть следующие ассоциации:
class User < ApplicationRecord
has_many :orders
end
class Order < ApplicationRecord
belongs_to :user
has_one :invoice
end
class Invoice < ApplicationRecord
belongs_to :order
has_one :receipt
end
class Receipt < ApplicationRecord
belongs_to :invoice
end
Я хочу вернуть все квитанции по всем заказам пользователя.
Если бы отношения были один на один на протяжении всего пути, это было бы просто:
user = User.find(...)
user.order.invoice.receipt
Поскольку заказов много, мне хочется нанести на карту и сгладить. Я не хочу этого делать. Кажется, я не могу найти источники, которые помогли бы мне по-настоящему разобраться в этой концепции. Нужно ли мне определять отношение through
? мне нужно использовать .joins
?
Как я могу вернуть квитанцию о всех заказах, принадлежащих конкретному пользователю?
Это должно сработать для вас:
Receipt.joins(invoice: { order: :user }).where(users: { id: user_id })
Да, вам нужно использовать объединения, чтобы сгенерировать правильный запрос (объедините все таблицы по пути и добавьте свое пользовательское ограничение).
Другой вариант - использовать подзапрос.
users = User.where(id: user_id)
orders = Order.where(user_id: users)
invoices = Invoice.where(order_id: orders)
receipts = Receipt.where(invoice_id: invoices)
Вышеупомянутое должно выполнять один запрос при загрузке receipts
. При копировании в терминал суффикс строк с ;nil
предотвращает загрузку коллекций методом осмотреть (который используется для отображения возвращаемого значения терминала).
Примечание: Обычно вам нужно указать атрибут для выбора. Однако атрибут я бы (первичный ключ) выбирается неявно, не выбирая атрибут.
orders = Order.where(user_id: users)
# is the same as
orders = Order.where(user_id: users.select(:id))
Помните об этом, когда вам нужно создать подзапрос на основе другого атрибута, кроме я бы. Например, при изменении направления подзапроса.
users = User.where(id: orders.select(:user_id))
Вы даже можете опустить первую строку и предоставить
user_id
прямо в ограничение orders куда.orders = Order.where(user_id: user_id)