Rails 3 - 处理控制器中嵌套资源查询的最佳方法?

时间:2011-09-08 02:20:18

标签: ruby-on-rails ruby ruby-on-rails-3 activerecord nested-resources

如果我对Rails 3了解的一件事是,如果我很难做某事,我可能做错了。所以我正在寻求帮助。

我有一些与多对多关系相关的模型。

我能够在没有问题的情况下在模型中创建关联。我的问题在于如何构建控制器以使用这些关系。如果你没看到我要去的地方,我会试着举个例子。

例如......

class Account < ActiveRecord::Base
    has_many :locations
end

class Contact < ActiveRecord::Base
    has_many :locations
end

class Location < ActiveRecord::Base
    has_and_belongs_to_many :accounts
    has_and_belongs_to_many :contacts
end

假设我有以上型号。这将是我的资源......

resources :accounts do
    resources :locations
end

resources :contacts do
    resources :locations
end

resources :locations do
    resources :accounts
    resources :contacts
end

所以只是为了缩短这一点,让我们说我想要一个帐户所有位置的列表。上述路线可能是账号/ 1 /位置。因此登陆我的地点#index。

希望我此时没有搞砸我的例子,但是建立这个行动的最佳方法是什么,因为它确实有多个工作......至少是帐户,联系人和所有地点的位置。 / p>

所以我最终得到这样的东西......

class LocationController < ApplicationController
    def index
        if params[:account_id]
            @locations = Location.find_all_by_account_id(params[:account_id])
        elsif params[:contact_id]
            @locations = Location.find_all_by_contact_id(params[:account_id])
        else
            @locations = Location.all
        end

        respond_with @locations
    end
end

更新#1:澄清一下,因为我得到了一些答案,表明我改变了我的模型关系。我正在使用遗留系统,在此系统中我无法改变关系。最终我的目标是清理数据库和关系,但现在我不能。所以我需要找到一个适合这种配置的解决方案。

3 个答案:

答案 0 :(得分:6)

你当前的方法不是DRY,如果你想在索引上强加额外的范围,会让你头疼;例如通过字段进行分页,排序或搜索。

考虑另一种选择:注意你的if / elsif / else条件本质上只是找到发送find的查找范围?为什么不把这个责任转移到一个方法呢?从而简化您的操作并删除冗余代码。

def index
  respond_with collection
end

def show
  respond_with resource
end

protected

# the collection, note you could apply other scopes here easily and in one place,
# like pagination, search, order, and so on.
def collection
  @locations ||= association.all
  #@locations ||= association.where(:foo => 'bar').paginate(:page => params[:page])
end

# note that show/edit/update would use the same association to find the resource
# rather than the collection
def resource
  @location ||= association.find(params[:id])
end

# if a parent exists grab it's locations association, else simply Location
def association
  parent ? parent.locations : Location
end

# Find and cache the parent based on the id in params. (This could stand a refactor)
#
# Note the use of find versue find_by_id.  This is to ensure a record_not_found
# exception in the case of a bogus id passed, which you would handle by rescuing
# with 404, or whatever.
def parent
  @parent ||= begin
    if id = params[:account_id]
      Account.find(id)
    elsif id = params[:contact_id]
      Contact.find(id)
    end
  end
end

inherited_resources是一个很好的宝石,可以干净地处理这样的场景。由Jose Valim(Rails)撰写。我相信它应该与HABTM合作,但说实话,如果我尝试过它,我就不会积极。

上面的例子基本上是inherited_resources的工作方式,但大多数它在幕后工作,你只需要覆盖方法。如果它适用于HABTM(我认为它应该),你可以写出你当前的控制器:

class LocationController < InheritedResources::Base
  belongs_to :contact, :account, :polymorphic => true, :optional => true
end

答案 1 :(得分:4)

您不应该提供多种方式来获取相同的资源。不应该将资源的1对1映射到关联。

您的路线文件应如下所示:

resources :accounts
resources :contacts
resources :locations

REST的重点是每个资源都有一个唯一的地址。如果您确实只希望在给定位置公开帐户/联系人,请执行以下操作:

resources :locations do
    resources :accounts
    resources :contacts
end

但你绝对不应该同时提供嵌套帐户/位置和位置/帐户路线。

答案 2 :(得分:0)

我看到的方式是,由于帐户和联系人似乎有类似的行为,因此使用单表继承(STL)并拥有另一个资源(例如User)是有意义的。

这样你可以做到这一点......

class User < ActiveRecord::Base
    has_many :locations
end
class Account < User
end

class Contact < User
end

class Location < ActiveRecord::Base
    has_and_belongs_to_many :user
end

资源保持不变......

resources :accounts do
    resources :locations
end

resources :contacts do
    resources :locations
end

resources :locations do
    resources :accounts
    resources :contacts
end

然后,您可以使用相同的方式访问位置,而与作业类型无关。

class LocationController < ApplicationController
    def index
        if params[:user_id]
            @locations = Location.find_all_by_account_id(params[:user_id])
        else
            @locations = Location.find_all_by_id(params[:account_id])
        end

        respond_with @locations
    end
end

这样你的代码就变得可重用,可扩展,可维护,而且我们被告知的所有其他好东西都很棒。

希望它有所帮助!