Проблемы с опцией: uniq => true / Distinct в ассоциации has_many_through с именованной областью (Rails) - PullRequest
1 голос
/ 14 марта 2010

См. Обновления в нижней части вопроса.

Мне пришлось внести некоторые изменения в свое приложение, чтобы добавить новые функциональные возможности, и мои изменения, похоже, нарушили опцию: 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'  

Похоже, это может быть еще одним симптомом той же проблемы?

Есть еще идеи?

1 Ответ

0 голосов
/ 15 марта 2010

Я считаю, что проблема в форматировании лямбды. Я, очевидно, не могу запустить SQL, но следующая лямбда создает соответствующий хеш:

lambda { |seasons| season_ids = seasons.map { |season| season.id }; { :joins => :product, :conditions => { :seasons => { :id => season_ids } }, :order => 'products.name' } }

Вывод этого вызова с двумя сезонами с идентификаторами 1 и 2:

{:joins=>:product, :conditions=>{:seasons=>{:id=>[1, 2]}}, :order=>"products.name"}
...