I have a rails 3.2 application with an easy hierarchy: A user has many clients, a client has many invoices.
I want the users to only see their own clients and invoices using scopes, by doing Client.by_user(current_user) and Invoice.by_user(current_user). For clients, I have this, which works fine:
scope :by_user, lambda { |user| where(user_id: user.id) }
However, if I try the same for invoices
scope :by_user, lambda { |user| where(client.user_id => user.id) }
it fails, telling me undefined local variable or method 'client'.
What am I doing wrong? I don't want to add user_ids to the invoices.
As #gregates said in comments, better for you to define associations for User, Client & Invoice models and then use user.clients, user.invoices, invoice.user etc.:
class User < ActiveRecord::Base
has_many :clients
has_many :invoices, through: :clients
end
class Client < ActiveRecord::Base
belongs_to :user
has_many :invoices
end
class Invoice < ActiveRecord::Base
belongs_to :client
has_one :user, through: :client
end
But if you prefer idea with scope, you should join clients table to invoices in your scope:
class Invoice < ActiveRecord::Base
...
scope :by_user, lambda { |user| joins(:client).where("clients.user_id = ?", user.id) }
...
end
Related
In my app, an Account can be owned by a Household or a User. Users can access accounts that they own, and accounts that their household owns. I've defined accessible_accounts, that gives me an array of both household and user accounts, but is there a way to get the same result as a relation, so I can chain it with further conditionals? The order is unimportant (and indeed can be set with further chaining)
class User < ActiveRecord::Base
has_many :accounts, as: :owner
belongs_to :household
def accessible_accounts
ua = accounts.to_a
ha = []
if household
ha = household.accounts.to_a
end
(ua + ha).uniq
end
end
class Household < ActiveRecord::Base
has_many :users
has_many :accounts, as: :owner
end
class Account < ActiveRecord::Base
belongs_to :owner, polymorphic: true
end
I have a couple of thoughts on this:
First, you could express the Account that User has through Household by adding the following to the Accounts model:
has_many :accounts, through: household
However, I understand that you want a Relation that represents the merge/union of the accounts that a user owns directly with the accounts they own through their associated household. If that's true, then I think the following method added to User will give you what you want:
def accessible_accounts
Account.where(
'(ownable_id = ? and ownable_type = ?) or (ownable_id = ? and ownable_type = ?)',
id, User.to_s, household_id, Household.to_s)
end
I have not tested this, but I thought I'd go ahead and share it and count on feedback if I've misunderstood something.
In a proper polymorphic association I think your structure would be as following:
class User < ActiveRecord::Base
belongs_to :household
has_many :accounts, as: :ownable
end
class Household < ActiveRecord::Base
has_many :users
has_many :accounts, as: :ownable
end
class Account < ActiveRecord::Base
belongs_to :ownable, polymorphic: true
end
By the way, do you have any speacial reasons for not using Devise and CanCan for authentication & authorization?
Could someone tell me how to simplify the following function in my Invoice model? It should return all Invoices related to a particular Client (both connected via a Projects table).
def self.search_by_client_id(client_id)
if client_id
projects = Project.where(:client_id => client_id)
Invoice.where(:project_id => projects)
else
scoped
end
end
I really can't get my head around this. Thanks for any input!
This seems like the perfect opportunity for a scope!
scope :client, lambda{|id| includes(:projects).where('projects.client_id = ?', id)}
Invoice.client(4).all # returns all invoices for the client with the specified ID.
If I understand your question correctly, you should be able to do it using ActiveRecord associations:
class Client < ActiveRecord::Base
has_many :projects
has_many :invoices, :through => :project
end
class Project < ActiveRecord::Base
has_many :invoices
belongs_to :client
end
class Invoice < ActiveRecord::Base
belongs_to :project
has_one :client, :through => :project
end
Then, all Invoices related to a particular Client:
#client.invoices
To get a Client, associated to a particular invoice:
#invoice.client
I have 3 model
class User < ActiveRecord::Base
has_many :profiles
has_many :companies, :through => :profiles
end
class Company < ActiveRecord::Base
has_many :profiles
has_many :users, :through => :profiles
end
class Profile < ActiveRecord::Base
belongs_to :company
belongs_to :user
end
when I want to query all companies form user. I just type
u = User.find(:first)
companies = u.companies
But when I want to query only companies that have
profile.is_publish == ture
What is the right way I should do
First: I should create method in User to do that
def published_companies
companies.where('profiles.is_publish' => true)
end
companies = u.published_companies
Second: I should create scope in Company
scope :published, joins(:profiles).where('profiles.is_publish' => true)
companies = u.companies.published
Third: I should create a scope in Profile
companies = u.profile.published.companies
the first way is easy one and the third is cool but I don't know How to do it.
Create the scope on Company like:
scope :published, where(:is_publish => true)
Then on user you call:
user.companies.published
To check the validness of the code you can use to_sql method.
I need to query joining tables without using the primary key(id).
class User < ActiveRecord::Base
has_one :participant
end
class Participant < ActiveRecord::Base
belongs_to :user
end
The User has 'id' and 'name'.
The Participant has 'user_id' which is User.id
I am trying to find the Participant.id by querying with User.name
What I have tried is,
participant_id = Participant.all :joins => :users, :conditions => {:name => "Susie"}
If you're just looking for a specific user's participant id, you could just go:
User.find_by_name("Susie").participant.id
Since your user has a has_one relation to Participant(which belongs to it -- so basically a one-to-one) you can just go call participant on user. ActiveRecord takes care of the join magicks for you
Try the following:
class User < ActiveRecord::Base
has_one :participant
end
class Participant < ActiveRecord::Base
belongs_to :user
scope :foo, lambda {|name|
joins(:users).
where("name = ?", name)
}
end
If you're taking user input you're going to want to probably fix joins so it uses sanitize_sql_array. See here also for some query goodness
I have a many-to-many relation between users and the channels they subscribe to. But when I look at my model dependency between user and user channels or channels and users channel instead there is a direct connection between users and channels. how do i put Users channels between to two?
User Model
class User < ActiveRecord::Base
acts_as_authentic
ROLES = %w[admin moderator subscriber]
has_and_belongs_to_many :channels
has_many :channel_mods
named_scope :with_role, lambda { |role| {:conditions => "roles_mask & #{2**ROLES.index(role.to_s)} > 0 "} }
def roles
ROLES.reject { |r| ((roles_mask || 0) & 2**ROLES.index(r)).zero? }
end
def roles=(roles)
self.roles_mask = (roles & ROLES).map { |r| 2**ROLES.index(r) }.sum
end
def role_symbols
role.map do |role|
role.name.underscore.to_sym
end
end
end
Channel Model
class Channel < ActiveRecord::Base
acts_as_taggable
acts_as_taggable_on :tags
has_many :messages
has_many :channel_mods
has_and_belongs_to_many :users
end
UsersChannel Model
class UsersChannels < ActiveRecord::Base
end
See the has_many :through documentation on the Rails guides which guides you through configuring a has_many relationship with an intervening model.
The HABTM relationship generates the UsersChannels auto-magically - if you want to access the model for the link table (add some more attributes to it for example - time_channel_watched or whatever), you'll have to change the models (and explicitly define and migrate a UsersChannel model with the attributes id:primary_key, user_id:integer, channel_id:integer) to :
class Channel < ActiveRecord::Base
has_many :users_channels, :dependent => :destroy
has_many :users, :through => :users_channels
end
class User < ActiveRecord::Base
has_many :users_channels, :dependent => :destroy
has_many :channels, :through => :users_channels
end
class UsersChannels < ActiveRecord::Base
belongs_to :user
belongs_to :channel
end
Note: since you're defining you're own link model you don't have to stay with the HABTM defined table name of UsersChannels - you could change the model name to something like "Watches". All of the above is pretty much in the Rails guide that has been mentioned.