Rails中复杂关系的建模

时间:2009-09-18 11:19:15

标签: ruby-on-rails modeling

更新

好的,我明白了。我不得不打电话给以下品种:

<%=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)这个复杂的东西是否正确放置在视图层中,还是有更好的方法将其移动到模型或控制器?

感谢。

以下原始问题现已解决......

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

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

我需要创建一个应用程序:

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

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

根据我收到的第一个答案,我修改了我的模型,如下面的答案中所述。每个品种属于一种产品,即富士仅属于苹果产品,它是产品表中的独特ID。并且,产品具有许多品种,即苹果产品可能具有5或10种不同的品种。

然而,它变得更加复杂,因为每个用户可能拥有不同的产品/品种组合。例如,Topps杂货店(用户)可能拥有富士和麦金托什(品种)的苹果(产品)。但是,Publix杂货店(用户)可能拥有红色美味的苹果(产品)和盛会(品种)。

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

当我在show视图中尝试下面列出的代码时,我收到以下错误:#:

的未定义方法`user_product'
<% @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 %>

它在我的每个记录的视图中显示以下内容:#<User_product:0x4211bb0>

2 个答案:

答案 0 :(得分:0)

他们认为,与产品的关系并不多。 你有它的特定品种(如果有的话)的产品。所以为了跟踪它,我会使用额外的模型。这样你就可以保持产品和品种之间的关系。

模特:

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. 因为它基本上是一个观看事物,所以最好将它放在视图中。您当然可以使用控制器或模型预先收集所有产品和品种:
  3. 将所有逻辑移动到控制器和模型可能不是一个好主意,因为他们不应该知道(或关心)你想如何显示数据。所以这只是准备它的问题。

    @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
    

答案 1 :(得分:0)

MikeH

您可能在此处输入错误,@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。

只是一个想法。