How do you concatenate two active record results to return a new result that can further be filtered?

大憨熊 提交于 2019-12-07 13:15:37

问题


Imagine the scenario...

#models/user.rb
class User < ActiveRecord::Base
    has_many :accounts, :conditions => { :active => 1 }
end

#models/account.rb
class Account < ActiveRecord::Base
    belongs_to :user

    def public_accounts
        Account.all :conditions => { public => true }
    end
end

Now imagine I want to concatenate User(:id).accounts with Account.public_accounts to show a list of all accounts available to a user.

So you'd think I'd be able to update the User model to look like this.

#models/user.rb
class User < ActiveRecord::Base
    has_many :accounts, :conditions => { :active => 1 }

    def all_accounts
        self.accounts + Account.public
    end
end

However, now I won't be able to use the all() method since it's no longer of that type of object.

In the controller I'd like to do this...

#controllers/accounts_controller.rb
def search_all
    User.find(params[:user_id]).all_accounts.all(
        :offset => params[:offset],
        :limit => params[:limit]
    )
end

Thoughts?

Update #1: Scope's won't work for my scenario. I simplified my scenario to try and get my point across. As stated I need a way to combine two active record results and retain the ability to further filter them in my controller.

So the question is, "Why?" The reason is, I am trying to combine two sets of records to form a complete collection and one of the collections is not associated with the user at all.

I have refactored the above scenario to try and show a more precise example without getting overly complicated.


回答1:


jklina's answer is correct, it's best to use scopes in this situation. Scopes provide a sugary syntax and are more readable. I'll elaborate on the setup:

class User < AR::Base
  has_many :accounts
end

class Account < AR::Base
  belongs_to :user

  scope :active, where(:active => true)
  scope :inactive, where(:active => false)
end

You would then access the account scopes as jklina showed: User.find(1).accounts.active, etc. Accessing all of a user's accounts like: User.find(1).accounts.

UPDATE:

I fixed some mistakes and added more below.

Based on the updates to your question, I think you need to make the public method a method on the class:

class Accounts < AR::Base
  ...
  # This is essentially a scope anyways
  def self.public
    where(:public => true)
  end
end

class User < AR::Base
  ...
  # This should return all the users accounts
  # and any public accounts
  def all_accounts
    Account.where("user_id = ? OR public is true", self.id)
  end
end



回答2:


This might be a good scenario to use scopes.

You can define active and inactive scopes in the Account model and then use the following:

User.accounts
User.accounts.active
User.accounts.inactive

You can even chain scopes together, so you could do something like:

User.accounts.active.paid_up



回答3:


Lets look at the return values in the chain:

User.find(params[:user_id]) # returns an instance of User class
User.find(params[:user_id]).all_accounts # returns an array

The Array class doesn't have an instance method called all that's why you are seeing this error. This is not a bug.

Why don't you try this:

class User
  has_many :accounts, :conditions => { :active => 1 }
  has_many :all_accounts :conditions => ["(active = ? OR public = ?)", 
                           true, true]
end

Now you can:

User.find(params[:user_id]).all_accounts.all(:limit => 10, :offset => 2)



回答4:


You're try to access two distinct tables and apply LIMIT/OFFSET to them as a combined union. That aint gonna happen unless you logically combine them at the SQL layer, not at the ActiveRecord layer.

Sounds like writing out the SQL, maybe using a UNION and then using find_by_sql might be your best best.



来源:https://stackoverflow.com/questions/7437874/how-do-you-concatenate-two-active-record-results-to-return-a-new-result-that-can

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!