通过多个has_many关联级别渲染部分集合

时间:2011-06-19 04:25:31

标签: ruby-on-rails ruby-on-rails-3 collections partial-views has-many-through

我正在尝试展示属于卖家产品帖子的订单集合

买方是订单中的user_id,卖方是产品中的user_id

快速:
用户有很多产品
产品有很多贴子(将产品带到多个活动中)
帖子有很多订单(其他用户可以订购)

这是我的代码

        class User < ActiveRecord::Base
          # Include default devise modules. Others available are:
          # :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
          devise :database_authenticatable, :registerable,
                 :recoverable, :rememberable, :trackable, :validatable

          # Setup accessible (or protected) attributes for your model
          attr_accessible :name, :email, :password, :password_confirmation, :remember_me,
                          :bio, :reason, :barter

                          #:email, :name, :bio, :reason, :barter, :

          has_many :products, :dependent => :destroy
          has_many :posts, :dependent => :destroy
          has_many :orders, :dependent => :destroy

            class Product < ActiveRecord::Base
                belongs_to :user
                belongs_to :club
                belongs_to :category
                has_many :posts, :dependent => :destroy

            class Post < ActiveRecord::Base
                      belongs_to :product, :include => [:user]
                belongs_to :event
                has_many :orders, :dependent => :destroy
                accepts_nested_attributes_for :orders

            class Order < ActiveRecord::Base
                belongs_to :user
                belongs_to :post
#schema
  create_table "orders", :force => true do |t|
    t.float    "quantity"
    t.float    "order_price"
    t.integer  "post_id"
    t.integer  "user_id"
    t.boolean  "payment_received"
    t.integer  "mark_for_buyer"
    t.string   "comment_for_buyer"
    t.integer  "mark_for_seller"
    t.string   "comment_for_seller"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  add_index "orders", ["post_id"], :name => "index_orders_on_post_id"
  add_index "orders", ["user_id"], :name => "index_orders_on_user_id"

  create_table "posts", :force => true do |t|
    t.float    "quantity"
    t.datetime "deadline"
    t.integer  "product_id"
    t.integer  "event_id"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "products", :force => true do |t|
    t.string   "title"
    t.text     "desc"
    t.text     "ingredients"
    t.float    "deposit"
    t.float    "cost"
    t.string   "units"
    t.float    "quantity"
    t.float    "deadline_hours"
    t.boolean  "presell_option"
    t.integer  "user_id"
    t.integer  "category_id"
    t.integer  "club_id"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

订单/ index.html.erb

        #this one works beautifully 
        <%= render :partial => "order", :collection => @orders.find_all_by_user_id(current_user.id), :locals => {:order => @order}  %>
        #this one doesn't work (has_many association resources have been tricky for me)
        <%= render :partial => "order", :collection => @orders.where(:post => {:product  => {:user_id => current_user.id}}) , :locals => {:order => @order}  %>

订单/ _order.html.erb

  <tr>
    <td><%= order.user.name %></td>
    <td><%= order.post.product.user.name %></td>
    <td><%= link_to order.post.product.title, order.post %></td>
    <td><%= number_to_currency order.order_price %></td>
    <td><%= order.quantity %></td>
    <td><%= order.updated_at.to_formatted_s(:short) %> </td>
    <td><%= order.payment_received %></td>
    <td><%= link_to 'Show', order %></td>
    <td><%= link_to 'Edit', edit_order_path(order) %></td>
    <td><%= link_to 'Destroy', order, :confirm => 'Are you sure?', :method => :delete %></td>
  </tr>

现在,一个集合非常有用,订单中有一个user_id字段,它会收集发出订单的用户并显示所有正确的订单。 另一个系列没有用,我收到了这个错误:

错误

ActiveRecord::StatementInvalid in Orders#index - No attribute named `user_id` exists for table `product`

但是有一个&#39; user_id&#39; &#39;产品中的字段&#39;表。我可以在cancan中调用它来限制对拥有订购产品的用户的订单管理,并且工作得很漂亮

can :update, Order, :post => {:product => {:user_id => user.id }}

非常感谢任何见解!

2 个答案:

答案 0 :(得分:3)

查看Law of Demeter。它解释了为什么你所做的是造成问题(没有关注点分离=意大利面条代码)。另请查看ActiveSupport::Delegate,了解如何将Demeter法应用于您的Rails应用程序,并避免使用无聊的getter和setter列表。

一些帮助你的笔记:

  1. 在控制器中进行所有ActiveRecord调用,例如where。您的视图应该有数组(或ActiveSupport :: Proxy)来简单地迭代和显示。您应该避免让视图触发数据库查询,并且永远不应该在其中保留记录获取业务逻辑。
  2. 不是无休止地链接模型关联,而是将方法从一个模型定义或委托给另一个模型。例如,代替@comment.post.title,定义并使用@comment.post_title代替(注意下划线)。这样可以避免要求您的视图具有数据库模式和模型关联的固有知识。它只需要知道评论有帖子标题,但是数据是建模的。
  3. 如果您重构代码以保持每个模型专注于单个关注点并对单个资源建模,然后以干净,划分的方式将关联层叠在一起,您肯定会避免需要有人来解决这个问题。问题(教一个人钓鱼......)。

答案 1 :(得分:0)

我发现诀窍就是这个宝石http://rubygems.org/gems/nested_has_many_through可以做到这样的事情:

class Author < User
  has_many :posts
  has_many :categories, :through => :posts, :uniq => true
  has_many :similar_posts, :through => :categories, :source => :posts
  has_many :similar_authors, :through => :similar_posts, :source => :author, :uniq => true
  has_many :posts_of_similar_authors, :through => :similar_authors, :source => :posts, :uniq => true
  has_many :commenters, :through => :posts, :uniq => true
end

class Post < ActiveRecord::Base
  belongs_to :author
  belongs_to :category
  has_many :comments
  has_many :commenters, :through => :comments, :source => :user, :uniq => true
end

这极大地简化了我的查询和集合。

这是我最终需要的所有代码。我被告知gem nested_has_many_through将成为Rails 3.1中的标准

用户模型

  has_many :products, :dependent => :destroy
  has_many :product_posts, :through => :products, :source => :posts, :uniq => true
  has_many :food_orders, :through => :product_posts, :source => :orders, :uniq => true

OrdersController

 @orders_for_my_products = current_user.food_orders.all

订单索引

<%= render :partial => "order", :collection => @orders_for_my_products, :locals => {:order => @order}  %>

这将使用最少的代码通过关系返回深层嵌套数据。我当然希望它有效率,但至少我的理智是幸免的!