rails scope based on models parent attribute - ruby-on-rails

I have 2 models. line_item and account.
line_item belongs to an account.
account has a column is_active.
I'm looking for a way to write a Rails scope to find all line_items where their account is_active = true
Something like
LineItem.should_display

EDIT
class LineItem < ActiveRecord::Base
scope :should_display, -> { joins(:account).where(accounts: {is_active: true}) }
end
This produces the same result as adding the following class method in your LineItem model.
def self.should_display
joins(:account).where(accounts: {is_active: true})
end
I think you can find more information in the Rails guides for Active Record Querying: http://guides.rubyonrails.org/active_record_querying.html

Related

How to create a scope based on user.photos.count

I have a User model and a Photo model. If I go into Heroku Console, I can search the database, for example:
u = User.find(2)
u.photos.count
=> 25
I want to create a scope in my User model so that I can sort the users based on their photos.count number and then paginate the users.
Class User
scope :photocount ??????
UsersController
def index
#users = User.photocount.paginate(page: params[:page])
end
What you want is counter_cache. Add a column photos_count to user model and in Photo model:
belongs_to :user, counter_cache:true
Then you can User.order(:photos_count).page(params[:page])
See more about counter cache in rails guides here
Scope will look like:
scope :by_photos_count, ->{ order(:photos_count) }
Without counter cache it is still possible, but will be very inefficient:
User.joins('join (select user_id, count(*) as sort from photos group by user_id) as sort ON users.id=sort.user_id').order('sort.sort').page(params[:page])

Rails how to access child model attributes in where

Let's say I have two models:
Campaign and Qualification. Campaign has_one :qualification and Qualification belongs_to :campaign.
Let's say that Qualification has a male_gender boolean attribute, what's the most effective way to find all campaigns that have a qualification with male_gender == true
One way would be to loop through qualification with male_gender indexed. Create an array of campaign_ids, and then get campaigns with those ids, but this seems not very effective.
Ideally I'd like to do something like Campaign.where("qualification.male_gender: true")
Is that possible? Is there a better way to do this?
You certainly can do this. Using a join you can reference the other table in your where query:
Campaign.joins(:qualification).where(qualifications: {male_gender: true})
Rich Peck edit
You could also put this into a scope:
#app/models/campaign.rb
class Campaign < ActiveRecord::Base
scope :male_gender, -> { joins(:qualification).where(qualifications: {male_gender: true}) }
end
... which would allow you to call #campaigns = Campaign.male_gender

Rails how to describe many to many restriction

I have this :
model listing:
has_many: restrictions
model user:
has_many: restrictions
model restriction:
belongs_to: :user
belongs_to: :listing
By default all users are allowed to see all listings because there is no relation between listings and users, but I want to restrict users per listing if there is a record on restriction table.
For example:
If the record restrictions.user_id: 2 and restrictions.listing_id: 5 exits, the user with id 2 will see all listings except the listing with id 5
How can I do/describe that with rails?
What comes to mind is having a scope in Listing model which would exclude the restricted listing:
scope :accessible_listings, -> { where('id != ?',Restriction.where(user_id: current_user.id).map(&:listing_id)) }
I don't this a has_many association, maybe you can define a method to do what you want
class User
def listings
Listing.where.not(id: ((self.restrictions.map &:listing).uniq.map &:id))
end
end
As #nesiseka answered, with a little changes, using rails where.not
scope :accessible_listings, ->(user) { where.not(id: Restriction.where(user_id: user.id).map(&:listing_id) ) }

Scoping a class method to current_user

I'm working on implementing a tagging system and I'm having problem querying for tagged objects with a scope.
For example, I would like to find all the user's items with a certain tag. With a class method I can currently find all the objects:
def self.tagged_with(name)
Tag.find_by_name(name).items
end
However, this has a problem. If I were to do something like: current_user.items.tagged_with(name) won't this existing method return ALL the items and not just items owned by the current_user? I suppose this is a simply querying issue but I can't figure out how to change a class method into something called on a collection. I have tried going the opposite way, to get a the collection through the tags, something like... tag.items.where(:user_id => current_user.id) but in this case, it's a many-to-many relationship and I haven't been able to get on thumb on this either.
What's the proper way to restrict a query like this?
Create an association on your User class that points to your Tag class.
class User < ActiveRecord::Base
has_many :tags
end
Then you can do:
current_user.tags.where(...)
If you don't already have an association in place, you'll need to create a migration to have the tags table reference your users table with a foreign key.
I think this will help you:
class Account < ActiveRecord::Base
has_many :people do
def find_or_create_by_name(name)
first_name, last_name = name.split(" ", 2)
find_or_create_by_first_name_and_last_name(first_name, last_name)
end
end
end
person = Account.first.people.find_or_create_by_name("David Heinemeier Hansson")
person.first_name # => "David"
person.last_name # => "Heinemeier Hansson"
So, basically you can define your method tagged_with directly into the association!
This example is took from the documentations ActiveRecord::Associations

Is there an ActiveRecord equivalent to using a nested subquery i.e. ... where NOT IN(select...)?

I have 3 models: Category, Account, and SubAccount
The relations are:
Accounts has_many :sub_accounts
Categories has_many :sub_accounts
I wanted to get a list of all Categories that are not used by a given account.
My method in the Category model currently looks like:
class Category < ActiveRecord::Base
def self.not_used_by(account)
Category.find_by_sql("select * from categories where id not in(select category_id from sub_accounts where account_id = #{account.id})")
end
end
My question is, is there a cleaner alternative than using SQL?
NB. I am currently using Rails 3(beta)
You could move the method to the account model and use more of ActiveRecord by doing something like:
class Account < ActiveRecord::Base
def unused_categories
Category.where("id NOT IN (?)", sub_accounts.map(&:category_id))
end
end
Then you could do something like:
Account.first.unused_categories
AR does not do this out of the box. You may also want to check the excellent SearchLogic gem for a programatic approach.
search = Category.search
search.id_not_in sub_accounts.map(&:category_id)
search.name_equals "auto"
search. ..# other conditions
search.all
Try MetaWhere. http://metautonomo.us/projects/metawhere
You'll need my fork of Arel installed until the changes get merged (soon!) but with it installed you can do something like:
Category.where(:id.not_in => sub_accounts.map(&:category_id))

Resources