i am really struggling with SQL search queries today. Can one kindly tell me how i write a scope that display events with no payments.
event.rb
has_many :payments
payment.rb
belongs_to :event
i tried writing the below scope in the terminal:
events.joins(:payments).where("event.payments.empty?")
i also tried:
events.where("payments.empty?")
i am very unsure how to write a scope that displays an object with an empty array
This should work
Event.includes(:payments).where(payments: { event_id: nil })
Related
How can I access my related records?
class Post < ActiveRecord::Base
has_many :post_categories
has_many :categories, through: :post_categories
class Categories < ActiveRecord::Base
has_many :post_categories
has_many :post, through: :post_categories
class PostCategories < ActiveRecord::Base
belongs_to :post
belongs_to :category
PostCategories table has id, posts_id, and categories_id columns.
id | posts_id | categories_id
1. | 2 | 3
2. | 2 | 4
What I want is: to get posts related to a category. like: all Posts where in x category.
Yep, this is an easy one.
one_or_more_categories = # Category.find... or Category.where...
posts = Post.joins(:categories).where(category: one_or_more_categories)
Rails is clever enough to take either a model or a query that would find some data and turn that into an efficient appropriate query, that might be a subquery. Trying things out in the Rails console (bundle exec rails c) is a good way to see the generated SQL and better understand what's going on.
(EDIT: As another answer points out, if you've already retrieved a specific Category instance then you can just reference category.posts and work with that relationship directly, including chaining in .order, .limit and so-on).
Another way to write it 'lower level' would be:
Post.joins(:categories).where(category: {id: one_or_more_category_ids})
...which is in essence what Rails will be doing under the hood when given an ActiveRecord model instance or an ActiveRecord::Relation. If you already knew the e.g. category "name", or some other indexed text column that you could search on, then you'd adjust the above accordingly:
Post.joins(:categories).where(category: {name: name_of_category})
The pattern of joins and where taking a Hash where the join table name is used as a key with values nested under there can be taken as deep as you like (e.g. if categories had-many subcategories) and you can find more about that in Rails Guides or appropriate web searches. The only gotcha is the tortuous singular/plural stuff, which Rails uses to try and make things more "English-y" but sometimes - as in this case - just creates an additional cognitive burden of needing to remember which parts should be singular and which plural.
Not sure if this answers it but in ActiveRecord your Post will have direct access to your Category model and vice versa. So you could identify the category you want the posts from in a variable or an instance variable, and query #specific_category.posts. If you are doing this in your controller, you could even do it in before_action filter. If you are using it in serializers its not much different.
You could also create a scope in your Post model and use either active record or raw SQL to query specific parameters.
You also have an error in your Category model. Has many is always plural so it would be has_many :posts, through: :post_categories
Get the category object and you can directly fetch the related posts. Please see the following
category = Category.find(id)
posts = category.posts
Since you have already configured the has_many_through relation, rails will fetch post records related the category.
I would like to use the includes method with the related element of my Post
My Post can be associated with different type of element. And I use a value :cat to knows witch kind of element is associated.
The value work as this (cat: (1 => Message, 2=>Question, 3=>Task, 4=>Event) with the association has_one
Example : If post.cat == 3, I can call the task related with a method post.task
Now, I would like to optimize the SQL requests of my Post/Index with the method includes. But is not working for the moment. Can you help me to find the error of my code ?
Post_controller :
def index
#posts = current_user.posts
#posts.each do |post|
if post.cat == 3
#task = post.task.includes(:users)
elsif post.cat == 4
#event = post.event.includes(:reminds)
end
end
end
Error: undefined method `includes'
Edit :
Post_model:
class Post < ApplicationRecord
has_one :post_message, dependent: :destroy
has_one :question, dependent: :destroy
has_one :task, dependent: :destroy
has_one :event, dependent: :destroy
end
Task_model :
class Task < ApplicationRecord
belongs_to :post
has_many :users_task, dependent: :destroy
has_many :users, through: :users_task
end
Why are you using #posts.each ?
For me, the best solution for that is to find all the posts whith the defined cat to run the includes method. In your case, it would be like that :
#posts.where(cat: 1).includes(:message)
#posts.where(cat: 2).includes(:question)
#posts.where(cat: 3).includes(task: :users)
#posts.where(cat: 4).includes(event: :reminds)
Well, after many tries, I opted for a scope method to run the includes method. It's not a really elegant solution, but I think it's the best in my case.
So I'm preparing the scopes in my Post_Model:
scope :with_tasks, -> { where(cat: 3).includes(:user).includes(task: :users) }
scope :with_events, -> { where(cat: 4).includes(:user).includes(event: :reminds) }
And after, I render them in my index action like this :
#posts = current_user.posts.with_tasks + current_user.posts.with_events
So the code is generating 2 SQL Requests to find the posts (one for each category).
I think there is a way to join all that directly into a new global scope, but I don't know how. So if there is anyone knows that, he can edit the answer
Enjoy !
If you're getting an undefined method: 'includes' error, it means that either post.task or post.event are not returning ActiveRecord objects like your code is expecting. Are you sure there will always be values set for .task or .event at that point in execution? Are there any cases where that value might be nil or blank?
By the way, have you heard about 'polymorphic associations'? Defining an association as polymorphic allows you to associate records of arbitrary types with a specific column (by storing both object ID and class name on each record behind the scenes). It seems like this exactly matches your use case. It would be much easier to use the built-in mechanism than trying to do all the if-then switching based on category in your code.
I am building a Rails 5 app and in this app I got two models.
First one is called Timeoff and second one is called Approval.
I want to get all Timeoff objects that got no approvals.
The time off model
class Timeoff < ApplicationRecord
scope :not_approved, -> { self.approvals.size > 0 }
has_many :approvals, as: :approvable, dependent: :destroy
end
The Approval model
class Approval < ApplicationRecord
belongs_to :approvable, polymorphic: true
end
I am calling it like this
Timeoff.not_approved
I get the error
NoMethodError: undefined method `approvals' for #<Class:0x007f9698587830>
You're trying to call approvals in the class context, but it actually belongs to an instance of Timeoff. For example:
Timeoff.approvals # doesn't work
Timeoff.first.approvals # works
That's why you get the undefined method error.
But I think you want a database query here. You could go two ways - that I know of:
Make two queries: find the timeoffs that have approvals and then query for the other ones using NOT IN
timeoff_ids = Approval.where(approvable_type: 'Timeoff').pluck(:approvable_id)
Timeoff.where.not(id: timeoff_ids)
This may get really slow if your tables are big.
Or you could do a join on the approvals table and filter to where the id is null:
Timeoff.joins("LEFT JOIN approvals ON timeoffs.id = approvals.approvable_id AND approvals.approvable_type = 'Timeoff'").where("approvals.id IS NULL")
This should also work, and may be faster - but you should measure with your own data to be sure.
Also, take a look at this question: How to select rows with no matching entry in another table? there is a complete explanation of the second query and some other ways to solve it.
The Setup
I have an STI setup like so:
class Transaction < ActiveRecord::Base
belongs_to :account
scope :deposits, -> { where type: Deposit }
end
class Deposit < Transaction
scope :pending, -> { where state: :pending }
end
class Account < ActiveRecord::Base
has_many :transactions
end
If I call:
> a = Account.first
> a.transactions.deposits
...then I get what I expect, a collection of Deposit instances, however if I look at the class of what's returned:
> a.transactions.deposits.class
...then it's actually not a Deposit collection, it's still a Transaction collection, ie. it's a Transaction::ActiveRecord_AssociationRelation
The Problem
So, to the problem, if I then want to call one of the Deposit scopes on that collection it fails:
> a.transactions.deposits.pending
NoMethodError: undefined method `pending' for #<Transaction::ActiveRecord_Associations_CollectionProxy:0x007f8ac1252d00>
Things I've Checked
I've tried changing the scope to Deposit.where... which had no effect, and also to Deposit.unscoped.where... which actually returns the right collection object, but it strips all the scope, so I lose the account_id=123 part of the query so it fails on that side.
I've checked this and the problem exists for both Rails 4.1 and 4.2. Thanks for any pointers on how to make this work.
I know there's a workaround, but...
I know I could work around the issue by adding a has_many :deposits into Account, but I'm trying to avoid that (in reality I have many associated tables and many different transaction subclasses, and I'm trying to avoid adding the dozens of extra associations that would require).
Question
How can I get what's returned by the deposits scope to actually be a Deposit::ActiveRecord_Association... so that I can chain my scopes from Deposit class?
I created an isolated test for your issue here:https://gist.github.com/aalvarado/4ce836699d0ffb8b3782#file-sti_scope-rb and it has the error you mentioned.
I came across this post from pivotal http://pivotallabs.com/merging-scopes-with-sti-models/ about using were_values in a scope to get all the conditions. I then used them on unscope to force the expected class, basically this:
def self.deposits
conditions = where(nil).where_values.reduce(&:and)
Deposit.unscoped.where(conditions)
end
This test asserts that it returns a Deposit::ActiveRecord_Relation https://gist.github.com/aalvarado/4ce836699d0ffb8b3782#file-sti_scope2-rb
Update
You can also write this as a scope if you prefer:
scope :deposits, -> { Deposit.unscoped.where where(nil).where_values.reduce &:and }
As a quick workaround you can do > a.transactions.deposits.merge(Deposit.pending), but can't think of a different way of solving it. I'll think and try more options later and come back if I find anything.
You might want to say that an Account has_many :deposits
class Account < ActiveRecord::Base
has_many :transactions
has_many :deposits
end
Then you should be able to query
a.deposits.pending
iOS developer learning Rails here. Trying to query active record for records based on a has_many relation's property. Apologies if this is simple but I just can't figure it out. I've read about and have been trying to use scope, .where, .joins, but there are so many contradicting posts and blogs online I'm unsure which to use and what's correct...
On to the problem:
I have two ActiveRecord models:
class User < ActiveRecord::Base
has_many :items
end
and
class Item < ActiveRecord::Base
belongs_to :user
end
An item has a property title, I am trying to find all of the Users that have an item with a title that is similar to some search parameter in string format.
I have managed to do such using a search for items and then .map like this:
users_owning_item_in_search_parameter = Item.where{ (title =~ my{#search_param + "%"}) }.map! { |i| i.user }
(that syntax comes from the squeel gem.)
But that command returns an Array when I want an ActiveRecord::Relation, because I need to do some further filtering that requires this instance type.
Any help much appreciated.
I think you're looking for something like this:
User.joins(:items).where('items.title LIKE ?', "#{#search_param}%")
You'll have to modify it a bit if you want to take advantage of squeel.