has_many 中 :unique => true/Distinct 选项的问题,通过关联/命名范围(Rails)
Posted
技术标签:
【中文标题】has_many 中 :unique => true/Distinct 选项的问题,通过关联/命名范围(Rails)【英文标题】:Problems with :uniq => true/Distinct option in a has_many_through association w/ named scope (Rails) 【发布时间】:2010-03-14 12:55:19 【问题描述】:查看问题底部的更新。
我不得不对我的应用程序进行一些调整以添加新功能,而我的更改似乎破坏了以前完美运行的 :uniq 选项。
设置如下:#User.rb
has_many :products, :through => :seasons, :uniq => true
has_many :varieties, :through => :seasons, :uniq => true
has_many :季节
#product.rb
has_many :四季
has_many :users, :through => :seasons, :uniq => true
has_many :品种
#season.rb
属于_to:产品
属于_to :variety
属于_to :user
named_scope :by_product_name, :joins => :product, :order => 'products.name'
#variety.rb
属于_to:产品
has_many :四季
has_many :users, :through => :seasons, :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 1 有 3 个关联品种。产品 ID 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
。我还没有对此设置验证,如果用户两次输入相同的品种,它确实会出现在页面上。在以前的工作版本中,情况并非如此。
我需要的结果是任何给定 ID 仅显示一个产品,并且与该 ID 关联的所有品种与此类唯一产品一起显示。
让我印象深刻的一件事是最近的日志输出中的 sql 调用。它将“计数”添加到不同的调用中。我不确定它为什么这样做,或者它是否可能表明存在问题。我发现这张未解决的灯塔票似乎可能相关,但我不确定它是否是同一个问题:https://rails.lighthouseapp.com/projects/8994/tickets/2189-count-breaks-sqlite-has_many-through-association-collection-with-named-scope
更新
我认为问题在于,named_scope 每个季节都被调用一次。 named_scope 中需要有一些东西可以按季节 ID 缩小返回的产品范围。
现在发生的事情是:
用户 = 给我用户 季节 = 获取用户的季节(例如,用户有 3 个季节) 产品 = 给我产品 产品 += 给我产品 产品 += 给我产品
给我每个产品
所以发生的不是 uniq 被破坏,而是命名范围上没有分隔符。 (我认为)。
我尝试了以下方法,但它抛出了这个异常:散列的奇数列表
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】:我认为问题在于 lambda 的格式。我显然无法运行 SQL,但以下 lambda 确实创建了一个适当的哈希:
lambda |seasons| season_ids = seasons.map |season| season.id ; :joins => :product, :conditions => :seasons => :id => season_ids , :order => 'products.name'
ID 为 1 和 2 的两个季节的调用的输出是:
:joins=>:product, :conditions=>:seasons=>:id=>[1, 2], :order=>"products.name"
【讨论】:
运行此程序时出现 No Method 错误...当您没有预料到它时,您有一个 nil 对象!您可能期望有一个 Array 的实例。评估 nil.map 时发生错误以上是关于has_many 中 :unique => true/Distinct 选项的问题,通过关联/命名范围(Rails)的主要内容,如果未能解决你的问题,请参考以下文章
Rails:为啥“has_many ...,:通过=> ...”关联导致“NameError:未初始化的常量...”
接受_nested_attributes_for with has_many => :through Options