У меня есть следующие ассоциации:
class Certificate < ApplicationRecord
has_many :certificates_users
has_many :users, :through => :certificates_users
end
class CertificatesUser < ApplicationRecord
belongs_to :user
belongs_to :certificate
end
class User < ApplicationRecord
has_many :certificates_users, :dependent => :delete_all
has_many :certificates, :through => :certificates_users
end
Я пытаюсь получить список сертификатов, а затем сгруппировать его по пользователю. По сути, перечислите всех пользователей и покажите под ними подсписок их сертификатов. Вот как UI выглядит как Просто нажмите «Мэр» на столе.
Вот структура таблицы CertificatesUser
=> CertificatesUser(certificate_id: integer, user_id: integer, expiry_date: date, certificate_number: string, renewal_date: date, ispublic: integer)
После этого другого отвечать я смог разработать следующие возможные решения,
Certificate.joins(:users).group('users.id').select('users.id')
User.joins(:certificates).group('certificates.id').select('certificates.id, certificates.name')
Проблема здесь в том, что большинство полей, которые мне нужны, находятся на certificates_users
, а не в таблице certificates
.
Это еще одна попытка заставить это работать, но безуспешно - CertificatesUser.group(:user_id).select('user.id')
Какие еще возможные решения этой проблемы?
Желаемый результат
Спасибо, @ArupRakshit за ваш ответ. Я приложил изображения желаемого результата. Имя пользователя и адрес электронной почты из таблицы пользователя. ИЛИ мы можем получить к нему доступ через пользовательский метод в таблице соединения. Таким же образом мы можем получить доступ к имени сертификата в первом столбце с помощью метода сертификата в таблице соединений.
Хорошо, добавлю ответ, если останется без ответа .. :)
Хорошо. @ArupRakshit Я буду ждать, спасибо.
ты можешь сделать
@users = User.includes(certificates_users: [:certificate])
тогда вы можете пропустить их
@users.each do |u|
u.certificates_users.each do |cu|
cu.certificate_number
cu.certificate.some_column
...
end
end
получать только пользователей с сертификатами
@users = User.includes(certificates_users: [:certificate]).where("certificates_users.user_id = users.id")
Спасибо, @pavittar за уделенное время. Эта команда - User.includes(certificates_users: [:certificate]
вернула всех пользователей в системе, это совсем не желаемое решение. Кстати, вы проверяли ссылку на пользовательский интерфейс, которым я поделился?
Я не вижу проблемы с решением Паваттара. Вам нужны сертификаты для всех пользователей, вы просто хотите обернуть сертификаты для каждого пользователя в div
с уникальным идентификатором. Итак, <% @users.each do |u| %>;<div id='certificates<%=u.id'>; <% u.certificates_users.each do |cu|%>;<%= u.certificate_number %>; <% end %>;</div>;<% end %>
@mayorsanmayor, если вы не хотите, чтобы все пользователи применяли какое-то смещение и ограничение.
Основываясь на определении отношения в ваших моделях, ниже должно работать:
CertificatesUser.joins(:user, :certificate)
.select('user_id, users.name, users.email, certificates.name')
.group_by(&:user_id)
Вы используете .group_by(&:user_id)
для группировки полученных данных на основе user_id в массиве json, как показано ниже:
Пример результата:
[1, [{ user_id: 1, certificate_id: 29, ....},{ user_id: 1, certificate_id: 22, ....}] ,
2, [{ user_id: 2, certificate_id: 34, ....},{ user_id: 2, certificate_id: 56, ....}] ,
3, [{ user_id: 3, certificate_id: 29, ....},{ user_id: 3, certificate_id: 12, ....}] ]
NB:
Вы можете добавлять поля из пользователи или сертификаты по своему усмотрению, но убедитесь, что вы используете их в множественное число, а не в единственное число.
Спасибо @Shiko за ваше решение. Но это не сработало. Вот ошибка, которую я получил: ActiveRecord::ConfigurationError (Can't join 'CertificatesUser' to association named 'certificates'; perhaps you misspelled it?)
Потом попробовал вот это - Certificate.joins(:users).select('user_id, users.first_name, users.email, certificates.name').group(:user_id).group_by(&:user_id)
и получил вот эту ошибку. ActiveRecord::StatementInvalid (PG::GroupingError: ERROR: column "users.first_name" must appear in the GROUP BY clause or be used in an aggregate function)
@mayorsanmayor извините, моя проблема, нет необходимости в group () на стороне db, проверьте обновленный ответ
Спасибо, @Shiko, я полностью изменил свой подход. Я очень ценю, что вы нашли время подумать над вопросом и опубликовать ответ. Большое тебе спасибо.
Это своего рода примечание, но название вообще не имеет смысла - UserCertificate
имел бы гораздо больше смысла.
Спасибо, @max, я нашел лучшее решение. Проверьте мой ответ ниже.
@max правильно, будет иметь смысл, если UserCertificate.
Я смог заставить это работать, изменив свой подход и сделав это как можно проще.
В контроллере у меня такой
@users_and_certificates = CertificatesUser.all.each_with_object({}) { |item, hash| hash[item.user_id] = item }
А затем в шаблоне я запрашиваю следующее:
.card-body.table-responsive
%table.table.table-hover.table-valign-middle
%thead
%tr
%th Name
%th E-mail
%tbody
- @users_and_certificates.each do |user_id, certificate_object|
%tr{"data-target" => "#certificate_#{certificate_object.certificate_id}", "data-toggle" => "collapse"}
- user = User.find(user_id)
%td
= user.full_name
%td
= user.email
%tr
%td.card-body.table-responsive.collapse{'id' => "certificate_#{certificate_object.certificate_id}"}
%table.table.table-hover.table-valign-middle
%thead
%tr
%th Certificate
%th Certificate No.
%th Expiry Date
%th Certificate
%tbody
%tr
- certificate = Certificate.find(certificate_object.certificate_id)
%td
= certificate.name
%td
= certificate_object.certificate_number
%td
= certificate_object.expiry_date
%td Show
Пока это работает отлично. Если у кого-то есть лучший подход, пожалуйста, не стесняйтесь оставлять комментарии или отвечать.
Спасибо.
Я могу помочь, но вам нужно указать, какой последний столбец должен получить запрос .. :) Прямо сейчас я запутался