Rails .where statement with nested resources - ruby-on-rails

I'm struggling with a .where statement in an index action.
In my Deals controller, i'd like to list all the deals where the bank of the current_user is participating.
Below are my models :
class User < ActiveRecord::Base
belongs_to :bank
end
class Deal < ActiveRecord::Base
has_many :pools
end
class Pool < ActiveRecord::Base
belongs_to :deal
has_many :participating_banks, dependent: :destroy
has_many :banks, through: :participating_banks
end
class ParticipatingBank < ActiveRecord::Base
belongs_to :pool
belongs_to :bank
end
Here is my Deals Controller Index action :
def index
#deals = Deal.all
end
I don't find any way to say : 'I only want to see a deal if this deal has, at least, one pool where the current_user.bank has been added'.
Any idea?
Many thanks :)

You should do inner join and query joined table for id. You can easily do it in Rails by:
def index
#deals = Deal.joins(pools: :banks).where(banks: { id: current_user.bank_id })
end

Related

How to search in Ruby on Rails

I have the three models:
class Joinedtravel < ApplicationRecord
belongs_to :travel
belongs_to :user
end
class Travel < ApplicationRecord
has_many :joinedtravels
belongs_to :user
end
class User < ApplicationRecord
has_many :joinedtravels
has_many :travels
end
How can I obtain all travels that a user has joined in the past?
I did something like that:
#user = User.find(id)
#past_travels = Travel.where('travels.data < ?', DateTime.now)
#all_joinedtravels = #user.joinedtravels.travels
but i don't kwon how to correctly join the results.
First you need to fix the relationship
class Joinedtravel < ApplicationRecord
belongs_to :travel
belongs_to :user
end
class Travel < ApplicationRecord
has_many :users, through: joinedtravels
has_many :joinedtravels
end
class User < ApplicationRecord
has_many :travels, through: joinedtravels
has_many :joinedtravels
end
Then you can simply search it using
User
.find(id)
.travels
.where('travels.data < ?', DateTime.now)
This should work:
#user = User.find(id)
#past_joinedtravels = #user.joinedtravels.joins(:travels).where('travels.date < ?', DateTime.now)
Try this in the console, and pay attention to the sql produced. That will show you possible errors.
The travelsin the joins clause is the model name. The travelsin the where clause must be the literal database table name, which I just guessed.
Seems to me you'd be better off using a has_and_belongs_to_many relations and a join table to join the User and Travel models as long as you're not including any additional information in the JoinedTravel model?
https://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association
class Travel < ApplicationRecord
has_and_belongs_to_many :user
end
class User < ApplicationRecord
has_and_belongs_to_many :travels
end
#user = User.find(id)
#past_travels = Travel.where('travels.data < ?', DateTime.now)
#user_travels = #user.travels
You could then see if a user has any travels:
#user.travels.present?

Rails use will_paginate with combined query results

In my application I have a customers model that has many payments and invoices.
# customer.rb
class Customer < ActiveRecord::Base
has_many :payments
has_many :invoices
end
# payment.rb
class Payment < ActiveRecord::Base
belongs_to :customer
end
# invoice.rb
class Invoice < ActiveRecord::Base
belongs_to :customer
end
In the customers show template I am combining all Invoices and Payments and storing them in the #transactions instance variable.
class CustomersController < ApplicationController
def show
#customer = Customer.find(params[:id])
payments = Payment.where(customer: #customer)
invoices = Invoice.where(customer: #customer)
#transactions = payments + invoices
end
I want to paginate #transactions using will_paginate. Doing this doesn't work:
#transactions.paginate(page: params[:page])
What is the best way to accomplish this?
Best way is to create a third Table of Transactions with polymorphic association as transactionable. And paginate over transactions.
http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
First Learn polymorphic association
class Transaction < ActiveRecord::Base
belongs_to :transactionable, polymorphic: true
end
class Invoice < ActiveRecord::Base
has_many :transactions, as: :transactionable
end
class Payment < ActiveRecord::Base
has_many :transactions, as: :transactionable
end
Other way which is not good to paginate both or use paginate array.

Rails activerecords with nested includes

I have the following models:
class BusinessProcess < ActiveRecord::Base
has_many :todos
end
class Todo < ActiveRecord::Base
has_one :row
end
class Row < ActiveRecord::Base
has_many :users
end
How can I count the number of rows in a BusinessProcess that has rows on a specific user?
Something like:
#businessProcess.todos.includes(XXX).where(users.id=?,1).count
#businessProcess.todos.includes(:row => :users).where("users.id=?",1).count
According to your associations, I'd rather go with just joining the tables like:
class Todo < ActiveRecord::Base
has_one :row
has_many: users, through: :row
scope :by_user_id, ->(user_id) {
joins(:users).where("users.id = ?", user_id)
}
end
and then:
#business_process.todos.by_user_id(1).count
Maybe you also could think of moving the where condition into a scope of Row, but that is more a responsibility thingie.
You also could read about ARel as an alternative: The N+1 problem and ARel.

Eager Load indirect associations Rails

Associations are below:
#app/models/pet.rb
class Pet < ActiveRecord::Base
belongs_to :pet_store
end
#app/models/pet_store.rb
class PetStore < ActiveRecord::Base
has_many :pets, dependent: :destroy
has_many :employees, dependent: :destroy
end
#app/models/employee.rb
class Employee < ActiveRecord::Base
belongs_to :pet_store
end
I want to do something like so which would cause an N + 1 error:
#pets = Pet.where(species: "Dog").includes(:pet_store)
#pets.each do |pet|
pet.pet_store.employees.each do |employee|
puts employee.name
end
end
This causes an N+1 error because a query must be made for each employee. I would like to eager load the indirect associated employees. However, I cannot simply includes(:employees) because a pet has no direct association to employees. How can this be done?
You can with:
#pets = Pet.includes(:pet_store => :employees)
The Rails Guide on the Query Language is great. Here's the docs on eager-loading.

Activerecord where condition with join without table name prefix

I have 2 tables. I use table prefix x_.
User (table x_users)
Comment (table x_comments)
I want to find out total count after inner join.
This query works fine.
User.joins(:comments).where(x_comments: {something: 1}).count
How can I remove x_ from where condition to make this call generic?
Models
class User < ActiveRecord::Base
has_many :comments, dependent: :destroy
end
class Comment < ActiveRecord::Base
attr_accessible :something
belongs_to :user
end
As #BroiSatse already mentioned, You can use ActiveRecord::Base.table_name to set the table name explicitly in a model and to get the table name in a query for genericity.
You query would be:
User.joins(:comments).where(Comment.table_name: {something: 1}).count
Setting a table name explicitly:
class Comment < ActiveRecord::Base
self.table_name = "x_comments"
end
You can override the table_name method like this:
class Comment < ActiveRecord::Base
def self.table_name
"x_" + super
end
end
Comment.table_name # => "x_comments"
Consider writing your conditions as scopes and let ActiveRecord handle the table aliasing for you.
class User < ActiveRecord::Base
has_many :comments, dependent: :destroy
def self.for_comment_something(foo)
joins(:comments).
merge(Comment.for_something(foo))
end
end
class Comment < ActiveRecord::Base
attr_accessible :something
belongs_to :user
def self.for_something(foo)
where(something: foo)
end
end
Documentation for ActiveRecord::Relation#merge is here.
Put it all together like
User.for_comments_something(1).count

Resources