【问题标题】:Modeling Complex Relationships in Rails在 Rails 中建模复杂的关系
【发布时间】:2009-09-19 21:22:53
【问题描述】:

更新

好的,我想通了。我不得不调用以下品种:
<%=h @user.varieties.find_by_product_id(product.id).name %>

这是我的两个后续问题:

(1) 这是否会因为我没有调用连接模型而导致编辑/删除记录时出现问题?我看过一些 Ryan Bates 视频,他强调了这一点,但我在尝试在这里引用连接模型时遇到了麻烦。也就是说,上述代码是否应该通过user_products调用?

如果我使用以下引用连接表的代码,我只能让它显示连接表中的variety_id(因为连接表中没有品种名称列)。我不知道如何让这段代码引用连接表中的variety_id,然后转到品种表以从“名称”列中获取品种的实际名称。

<% @user.products.each do |product| %>
   <% @user.user_products.find_by_product_id(product.id).variety_id %>
<% end %> 

(2) 这些复杂的东西是否正确放置在视图层中,或者有没有更好的方法将其移动到模型或控制器中?

谢谢。

下面的原始问题现已解决...

我有以下型号:
- 用户
- 产品
- 品种 - 用户产品

这是我正在尝试做的真实世界版本。假设用户是一家杂货店。产品是水果,如苹果。品种是苹果的类型,如富士和麦金托什。

我需要创建一个应用,其中:

  • 用户可以将多种类型的产品添加到他的页面。产品可以包含多个品种,但用户不需要包含任何品种。例如,Topps Grocery Store 可以将苹果添加到他们的页面。如果这就是他们想要展示的全部内容,那应该没问题。但是,他们也可以通过包括他们携带的苹果类型来添加更多细节,如富士、麦金托什等。品种不能只是一个详细的产品。换句话说,我不能让每个产品都像苹果-富士,苹果-麦金托什。它们需要是两个独立的模型。

  • 在用户页面(即“显示”视图)上,我需要能够同时显示产品和品种(如果有)。系统需要了解品种与该特定用户的特定产品相关联。

在收到第一个答案后,我修改了我的模型,如下面的答案所述。每个品种属于一个产品,即 fuji 只属于苹果产品,这是产品表中的一个不同的 id。而且,一个产品有很多品种,即苹果产品可能有 5 或 10 个不同的品种。

但是,它变得更加复杂,因为每个用户可能有一组不同的产品/品种组合。例如,Topps 杂货店(用户)可能有 fuji 和 mcintosh(品种)的苹果(产品)。但是,Publix 杂货店(用户)可能有红色美味的苹果(产品)和嘎拉(品种)。

在用户页面上,如果用户选择了任何品种,我希望能够调用用户携带的每个产品,然后显示与每个产品相关的品种。

当我在显示视图中尝试下面列出的代码时,我收到以下错误:undefined method `user_product' for #:

<% @user.products.each do |product| %>
   <% @user.user_products.find_by_product_id(product.id).varieties %>
<% end %>

另一方面,当我尝试您给我的另一个选项(如下所列)时,页面加载并且 sql 查询似乎在日志中正确,但页面上没有显示任何品种,这很奇怪,因为我检查了三次并且数据库中有应该匹配查询的记录。详情如下...

<% @user.products.each do |product| %>
   <% @user.varieties.find_by_product_id(product.id) %>
<% end %>    

此代码运行以下 sql 查询:

User Load (0.7ms) SELECT * FROM "users" WHERE ("users"."id" = 2)

Variety Load (0.5ms) SELECT "varieties".* FROM "varieties" INNER JOIN "user_product" ON "varieties".id = "user_products".variety_id WHERE (("seasons".user_id = 2))

在布局/应用程序中呈现模板
渲染用户/显示

Product Load (0.7ms) SELECT "products".* FROM "products" INNER JOIN "user_product" ON "products".id = "user_products".product_id WHERE (("user_products".user_id = 2))

Variety Load (0.4ms) SELECT "varieties".* FROM "varieties" INNER JOIN "user_product" ON "varieties".id = "user_products".variety_id WHERE ("varieties"."product_id" = 1) AND (("user_products".user_id = 2)) LIMIT 1

Variety Load (0.2ms) SELECT "varieties".* FROM "varieties" INNER JOIN "user_products" ON "varieties".id = "user_products".variety_id WHERE ("varieties"."product_id" = 2) AND (("user_products".user_id = 2)) LIMIT 1

在上面的这种情况下,我正在查看的用户是 user_id=2, he does have product_id=1 and product_id=2 in the database. And, in the user_products table, I do have a few records that list this user_id connected to each of these product_ids and associated with some variety_ids. 所以看起来我应该在我的显示视图上显示一些结果,但我什么也没得到。

最后,当我尝试以下操作时:

<% @user.products.each do |product| %>
   <%=h @user.user_products.find_by_product_id(product.id) %>
<% end %>

它在我的视图中为每条记录显示以下内容:#&lt;User_product:0x4211bb0&gt;

【问题讨论】:

  • 我将尝试添加一些示例,说明如何进行更新/删除。但请记住,即使是 UserProduct 也是具有关联的模型。也就是说,而不是 做 (记住 user_products 是属于 varity 的表,所以必须是单数)

标签: ruby-on-rails modeling


【解决方案1】:

我认为他们与产品没有多对多的关系。 您拥有该特定品种的产品(如果有的话)。所以为了跟踪它,我会使用一个额外的模型。这样您也可以保持产品和品种之间的关系。

模型:

class User < ActiveRecord::Base
  has_many :user_products, :dependent => :destroy
  has_many :products, :through => :user_products
  has_many :varieties, :through => :user_products
end

class UserProduct < ActiveRecord::Base
  belongs_to :user
  belongs_to :product
  belongs_to :variety
end

class Product < ActiveRecord::Base
  has_many :varieties
end

class Variety < ActiveRecord::Base
  belongs_to :product
end

控制器:

class UserProductsController < ApplicationController
    before_filter do 
        @user = User.find(params['user_id'])
    end


    def create
      product = Product.find(params['product_id'])
      variety = Variety.find(params['variety_id']) if params['variety_id']

      @user_product = UserProduct.new
      @user_product.user = user
      @user_product.product = product
      @user_product.variety = variety
      @user_product.save
    end

    def destroy
        # Either do something like this:
        conditions = {:user_id => @user.id}
        conditions[:product_id] = params[:product_id] if params[:product_id]
        conditions[:variety_id] = params[:variety_id] if params[:variety_id]

        UserProduct.destroy_all conditions
    end
end

意见: 如果您对将品种分组到不同的产品中不感兴趣,只需将它们作为一个列表就可以了:

# users/show.html.erb
<%= render @user.user_products %>

# user_products/_user_product.html.erb
<%= h user_product.product.name %> 
<%= h user_product.variety.name if user_product.variety %>

否则必须添加一些更复杂的东西。 其中一些可能可以通过使用关联代理来放入模型和控制器,但我无法帮助您

# users/show
<% for product in @user.products do %>
    <%= product.name %>
    <%= render :partial => 'variety', 
        :collection => @user.varieties.find_by_product_id(product.id) %>
<% end %>

# users/_variety
<%= variety.name %> 

将其拆分为部分当然是不必要的(在此示例中,可能有点荒谬),但它有助于分离不同的部分,特别是如果您想添加更多内容来显示。

并回答您的问题:

  1. 由于 UserProduct 是一个连接模型,您实际上不需要跟踪它的个人 ID。您拥有用户、产品和品种(在这些情况下它存在)。这就是您所需要的,该组合是独一无二的,因此您可以找到要删除的记录。此外,永远不应该编辑连接模型(前提是您在其中没有比这些字段更多的属性),它可以创建或删除,因为它所做的只是保持关联。
  2. 由于它基本上是一个查看对象,因此最好将其放置在视图中。您当然可以使用控制器或模型预先收集所有产品和品种,方法如下:

将所有逻辑移至控制器和模型可能不是一个好主意,因为它们不应该知道(或关心)您希望如何显示数据。所以只要做好准备就好了。

@products_for_user = []
@user.products.each do |product|
 collected_product = {:product => product, 
                      :varieties => @user.varieties.find_by_product_id(product.id)}
  @products_for_user << collected_product
end

【讨论】:

  • 感谢您的跟进,吉米。很有帮助。
【解决方案2】:

迈克

你可能只是在这里打错了,@user.user_product 应该是@user.user_products

当我尝试下面列出的代码时 显示视图,我得到以下 错误:未定义的方法“user_product” 对于#:

<% @user.products.each do |product| %>  
  <% @user.user_product.find_by_product_id(product.id).varieties %>  
<% end %>

您还可以考虑分层类型系统,其中只有“Fuji”、“Macintosh”和“Apples”等产品。

“Fuji”和“Macintosh”然后将列“parent_id”设置为“Apples”的 id。

只是一个想法。

【讨论】:

  • 谢谢。这实际上是我在问题中的错误输入。关于分层系统的有趣建议。我没有想到这一点。我需要查看它以了解它是如何实现的。
猜你喜欢
  • 2013-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-07
  • 1970-01-01
  • 2017-08-04
相关资源
最近更新 更多