См. Обновления в нижней части вопроса.
Мне пришлось внести некоторые изменения в свое приложение, чтобы добавить новые функциональные возможности, и мои изменения, похоже, нарушили опцию: uniq, которая ранее работала отлично.
Вот установка:
#User.rb
has_many: products,: through =>: seasons,: uniq => true
has_many: варианты,: через =>: сезоны,: uniq => true
has_many: сезоны
#product.rb
has_many: сезоны
has_many: пользователи,: through =>: сезоны,: uniq => true
has_many: разновидности
#season.rb
принадлежат: продукт
принадлежат: сорт
принадлежат: пользователь
named_scope: by_product_name,: joins =>: product,: order => 'products.name'
#variety.rb
принадлежат: продукт
has_many: сезоны
has_many: пользователи,: through =>: сезоны,: uniq => true
Сначала я хочу показать вам предыдущую версию представления, которая сейчас ломается, чтобы у нас была базовая линия для сравнения. Представление ниже показывает список продуктов и разновидностей, которые принадлежат пользователю. В обеих версиях ниже я назначил пользователю одни и те же продукты / сорта, поэтому в журналах будет использоваться один и тот же вариант использования.
#user/show
<% @user.products.each do |product| %>
<%= link_to product.name, product %>
<% @user.varieties.find_all_by_product_id(product.id).each do |variety| %>
<%=h variety.name.capitalize %></p>
<% end %>
<% end %>
Это работает. Он отображает только один из каждого продукта, а затем отображает сорта каждого продукта. В журнале ниже, ID продукта имеет 3 связанных сорта. И код продукта 43 не имеет ни одного.
Вот вывод журнала для кода выше:
Product Load (11.3ms) SELECT DISTINCT `products`.* FROM `products` INNER JOIN `seasons` ON `products`.id = `seasons`.product_id WHERE ((`seasons`.user_id = 1)) ORDER BY name, products.name
Product Columns (1.8ms) SHOW FIELDS FROM `products`
Variety Columns (1.9ms) SHOW FIELDS FROM `varieties`
Variety Load (0.7ms) SELECT DISTINCT `varieties`.* FROM `varieties` INNER JOIN `seasons` ON `varieties`.id = `seasons`.variety_id WHERE (`varieties`.`product_id` = 1) AND ((`seasons`.user_id = 1)) ORDER BY name
Variety Load (0.5ms) SELECT DISTINCT `varieties`.* FROM `varieties` INNER JOIN `seasons` ON `varieties`.id = `seasons`.variety_id WHERE (`varieties`.`product_id` = 43) AND ((`seasons`.user_id = 1)) ORDER BY name
Хорошо, все вышеперечисленное - предыдущая версия, которая прекрасно работала. В новой версии я добавил несколько столбцов в таблицу соединений с именем seasons
и создал несколько пользовательских методов, которые запрашивают эти столбцы. В результате я внес следующие изменения в код представления, который вы видели выше, чтобы получить доступ к этим методам на модели seasons
:
<% @user.seasons.by_product_name.each do |season| %>
<%= link_to season.product.name, season.product %>
#Note: I couldn't get this loop to work at all, so I settled for the following:
#<% @user.varieties.find_all_by_product_id(product.id).each do |variety| %>
<%=h season.variety.name.capitalize %>
<%end%>
<%end%>
Вот вывод журнала для этого:
SQL (0.9ms) SELECT count(DISTINCT "products".id) AS count_products_id FROM "products" INNER JOIN "seasons" ON "products".id = "seasons".product_id WHERE (("seasons".user_id = 1))
Season Load (1.8ms) SELECT "seasons".* FROM "seasons" INNER JOIN "products" ON "products".id = "seasons".product_id WHERE ("seasons".user_id = 1) AND ("seasons".user_id = 1) ORDER BY products.name
Product Load (0.7ms) SELECT * FROM "products" WHERE ("products"."id" = 43) ORDER BY products.name
CACHE (0.0ms) SELECT "seasons".* FROM "seasons" INNER JOIN "products" ON "products".id = "seasons".product_id WHERE ("seasons".user_id = 1) AND ("seasons".user_id = 1) ORDER BY products.name
Product Load (0.4ms) SELECT * FROM "products" WHERE ("products"."id" = 1) ORDER BY products.name
Variety Load (0.4ms) SELECT * FROM "varieties" WHERE ("varieties"."id" = 2) ORDER BY name
CACHE (0.0ms) SELECT * FROM "products" WHERE ("products"."id" = 1) ORDER BY products.name
Variety Load (0.4ms) SELECT * FROM "varieties" WHERE ("varieties"."id" = 8) ORDER BY name
CACHE (0.0ms) SELECT * FROM "products" WHERE ("products"."id" = 1) ORDER BY products.name
Variety Load (0.4ms) SELECT * FROM "varieties" WHERE ("varieties"."id" = 7) ORDER BY name
CACHE (0.0ms) SELECT * FROM "products" WHERE ("products"."id" = 43) ORDER BY products.name
CACHE (0.0ms) SELECT count(DISTINCT "products".id) AS count_products_id FROM "products" INNER JOIN "seasons" ON "products".id = "seasons".product_id WHERE (("seasons".user_id = 1))
CACHE (0.0ms) SELECT "seasons".* FROM "seasons" INNER JOIN "products" ON "products".id = "seasons".product_id WHERE ("seasons".user_id = 1) AND ("seasons".user_id = 1) ORDER BY products.name
CACHE (0.0ms) SELECT * FROM "products" WHERE ("products"."id" = 1) ORDER BY products.name
CACHE (0.0ms) SELECT * FROM "products" WHERE ("products"."id" = 1) ORDER BY products.name
CACHE (0.0ms) SELECT * FROM "varieties" WHERE ("varieties"."id" = 8) ORDER BY name
У меня две проблемы:
(1) Опция: uniq не работает для products
. На странице отображаются три разные версии одного и того же продукта.
(2) Опция: uniq не работает для varieties
. У меня еще нет настроенной проверки, и если пользователь вводит одно и то же разнообразие дважды, оно появляется на странице. В предыдущей рабочей версии это было не так.
В результате мне нужен только один продукт для любого идентификатора, и все разновидности, связанные с этим идентификатором, отображаются вместе с таким уникальным продуктом.
Одна вещь, которая бросается в глаза, это вызов sql в самом последнем выводе журнала. Это добавляет 'count' к отдельному вызову. Я не уверен, почему это происходит, или это может быть признаком проблемы. Я нашел этот неразрешенный билет на маяк, который, кажется, потенциально может быть связан, но я не уверен, что это та же самая проблема: https://rails.lighthouseapp.com/projects/8994/tickets/2189-count-breaks-sqlite-has_many-through-association-collection-with-named-scope
Обновление
Я думаю, проблема в том, что named_scope вызывается один раз для каждого сезона. В named_scope должно быть что-то, что сужает возвращаемые продукты по идентификатору сезона.
Что сейчас происходит:
пользователь = получить меня пользователь
seasons = получить мне пользовательские сезоны (скажем, есть 3 сезона для пользователя)
продукты = получить мне продукты
продукты + = получить мне продукты
продукты + = получить мне продукты
Дайте мне каждый из продуктов
То есть, дело не в том, что uniq ломается, а в том, что в названной области нет разделителя. (Я думаю).
Я попробовал следующее, но выдает это исключение: список нечетных чисел для Hash
named_scope :by_product_name, lambda { |seasons| { season_ids = seasons.map { |season| season.id }; :joins => :product, :conditions => { :seasons { :id => season_id } } :order => 'products.name' } }
Идеи
Обновление № 2
Хорошо, теперь я думаю, может быть, это не названная область действия вообще.
В #user/show
я просто изменил цикл, чтобы обойти названную область:
<% @user.seasons.each do |season| %>
<%= link_to season.product.name, season.product %>
#Note: I couldn't get this loop to work at all, so I settled for the following:
#<% @user.varieties.find_all_by_product_id(product.id).each do |variety| %>
<%=h season.variety.name.capitalize %>
<%end%>
<%end%>
Выше не использует именованную область, но я все еще получаю тот же результат. Другими словами, я все еще вижу все экземпляры каждого продукта вместо одного.
Код выше, который создает первый цикл, совпадает с моим исходным кодом, который я перечислил вверху этого вопроса. Разница в том, что этот код циклически проходит по seasons
, чтобы достичь products
, тогда как мой исходный код проходит по products
. Эта разница в том, где проблема скрывается, но я не знаю, как это исправить.
Кроме того, я упомянул в своем первоначальном вопросе, что я также не смог заставить работать цикл сортов. Вы можете увидеть строку с комментариями в коде прямо выше. При циклическом прохождении сезонов вместо продуктов, когда Rails попадает в этот цикл разновидностей, он выдает ошибку имени:
undefined local variable or method `product'
Похоже, это может быть еще одним симптомом той же проблемы?
Есть еще идеи?