Given the following two models:
class Wheel
belongs_to :car
def self.flat
where(flat: true)
end
and
class Car
has_many :wheels
def flats
self.wheels.flat
end
def has_flats?
flats.count > 0
end
I need a query for all cars with flat tires. I'm wondering why this isn't working in the cars model?:
def self.with_flats
where(:has_flats?)
end
or
def self.with_flats
where(:has_flats? == true)
end
This isn't returning the correct records. Any ideas?
Define a scope in Car model:
class Car
has_many :wheels
scope :having_flat_wheels, joins(:wheels).where("wheels.flat=?", true).uniq
......
end
Then to get all cars with flat tires:
Car.having_flat_wheels
Related
I am using Rails as a JSON API. My database has the following structure: the City model has_many Users, which in turn has_many Businesses.
When I send a GET request to businesses#index, I want Rails to return all the businesses in a given city, not just for a given user. What's the best way to do this?
I've already tried the given code below as a first pass, which is returning an internal server error (500).
def index
#city = City.find(params[:city_id])
#users = #city.users
#businesses = #users.businesses
render json: #businesses
end
Can you try this?
Because of the #user is an array. so the error will occuring.
# city.rb
class City < ApplicationRecord
has_many :users
end
#user.rb
class User < ApplicationRecord
belongs_to :invoice
has_many :businesses
end
#business.rb
class Business < ApplicationRecord
belongs_to :user
end
# your controller
def get_business
#city = City.find(params[:id])
#business = #city.users.includes(:business)
render json: #business
end
Add a new relationship to your City model:
has_many businesses, through: users
Then when you have a specific city you can get all businesses:
#city.businesses
You might also want to try starting with the Business model to make the query.
Business.joins(user: :city).where(cities: { id: params[:city_id] })
joins uses the association names
where uses the table names
I have 2 models in my app: Person and Review each person has many reviews and each review belongs to a person. I have an attribute called grade inside of my review model and I'd like to display the average grade of each person so I wrote this scope:
scope :average_grade, -> { self.first.review.average(:grade) }
Is there a better way for doing that? Besides the 2 queries that this method needs, I also have to run another 2 queries to get the proper Person object in my controller:
def show
#average = Person.includes(:review).where(id: params[:id]).average_grade
#person = Person.includes(:review).find(params[:id])
end
How could I avoid all of those queries?
Your scope is an instance method rather than scope, since it does not return ActiveRecord::Relation object.
I suggest you to do following:
# person.rb:
def average_grade
review.average(:grade)
end
# controller:
def show
#person = Person.find(params[:id])
#average = #person.average_grade
end
# person.rb
class Person < ActiveRecord::Base
has_many :reviews
end
# review.rb
class Review < ActiveRecord::Base
belongs_to :person
scope :by_person, ->(person) { where(person_id: person) }
end
# persons_controller
class PersonsController < ApplicationController
helper_method :person
private
def person
return #person if defined? #person
#person = Person.find(params[:id])
end
end
# show.html.haml
- present(person) do |person_presenter|
%p= person_presenter.average_grade
# person_presenter.rb
class PersonPresenter < BasePresenter
present :person
def average_grade
Review.by_person(person).average(:grade)
end
end
More on presenters you could find here Railscasts PRO #287 Presenters from Scratch
In Rails is there a way that I can make a method call itself based on a change in the database? For instance, lets say I have two classes: Products and Orders.
Orders have three possible enum values:
class Order < ActiveRecord::Base
enum status: [:pending, :processing,:shipped]
belongs_to :products
end
I would like to batch process Orders so when a product has 50 orders, I want it to set all Orders associated with it to processed. Orders default to :pending. To change an order to :processing I would call order.processing!. I could write a method into the Products model like:
def process_orders
if self.orders.count=50
self.orders.each do |order|
order.processing!
end
end
The problem with this is that I would have to call the process_orders method for it to execute, is there anyway I could make it automatically execute once a product has 50 orders?
This is sounds like a good opportunity to use an Active Record Callback.
class Order < ActiveRecord::Base
belongs_to :product
after_save do
product.process_orders if product.pending_threshold_met?
end
end
class Product < ActiveRecord::Base
has_many :orders
def pending_threshold_met?
orders.where(status: :pending).count >= 50
end
end
I think you can use update_all to update the status column of all of your orders at once rather looping through them one by one:
self.orders.update_all(status: :processing)
and wrap that inside a callback.
Something like this:
class Order < ActiveRecord::Base
after_save do
product.process_orders if product.has_fifty_pending_orders?
end
# rest of your model code
end
class Product < ActiveRecord::Base
# rest of your model code
def process_orders
self.orders.update_all(status: :processing)
end
def has_fifty_pending_orders?
self.orders.where(status: :pending).count >= 50
end
end
I have the following models:
class User < ActiveRecord::Base
has_many :survey_takings
end
class SurveyTaking < ActiveRecord::Base
belongs_to :survey
def self.surveys_taken # must return surveys, not survey_takings
where(:state => 'completed').map(&:survey)
end
def self.last_survey_taken
surveys_taken.maximum(:position) # that's Survey#position
end
end
The goal is to be able to call #user.survey_takings.last_survey_taken from a controller. (That's contrived, but go with it; the general goal is to be able to call class methods on #user.survey_takings that can use relations on the associated surveys.)
In its current form, this code won't work; surveys_taken collapses the ActiveRelation into an array when I call .map(&:survey). Is there some way to instead return a relation for all the joined surveys? I can't just do this:
def self.surveys_taken
Survey.join(:survey_takings).where("survey_takings.state = 'completed'")
end
because #user.survey_takings.surveys_taken would join all the completed survey_takings, not just the completed survey_takings for #user.
I guess what I want is the equivalent of
class User < ActiveRecord::Base
has_many :survey_takings
has_many :surveys_taken, :through => :survey_takings, :source => :surveys
end
but I can't access that surveys_taken association from SurveyTaking.last_survey_taken.
If I'm understanding correctly you want to find completed surveys by a certain user? If so you can do:
Survey.join(:survey_takings).where("survey_takings.state = 'completed'", :user => #user)
Also it looks like instead of:
def self.surveys_taken
where(:state => 'completed').map(&:survey)
end
You may want to use scopes:
scope :surveys_taken, where(:state => 'completed')
I think what I'm looking for is this:
class SurveyTaking < ActiveRecord::Base
def self.surveys_taken
Survey.joins(:survey_takings).where("survey_takings.state = 'completed'").merge(self.scoped)
end
end
This way, SurveyTaking.surveys_taken returns surveys taken by anyone, but #user.survey_takings.surveys_taken returns surveys taken by #user. The key is merge(self.scoped).
Waiting for further comments before I accept..
I'm new to ruby on rails and here is the problem:
I'm trying to get the client name inside a loop about the car information. The car data holds an column called "belongs_to" which is the ID of the client.
The important line: <td><%= #client.find(car.belongs_to) %></td>
The controller:
def index
#cars = Car.all
#client = Client.all
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #cars }
end
end
How would you guys do this?
You would use Models to achieve this:
class Car < ActiveRecord::Base
belongs_to :client
end
class Client < ActiveRecord::Base
has_many :cars
end
and in your View:
#car.client
EDIT
or the other way round:
#client.cars
# to iterate over them:
#client.cars.each do |car|
# do something with car
end
belongs_to is not a very good column name. There is an association type called "belongs_to" where you can establish a relationship between models, where one of them "belongs_to" another model, such as:
class Car < ActiveRecord::Base
belongs_to :client # there is a column on the cars table called "client_id"
end
and
class Client < ActiveRecord::Base
has_many :cars
end
Then you can do:
client = car.client
Take a look at Ruby on Rails Guides - A Guide to Active Record Associations
Try
Client.find(car.belongs_to)
What you really want to do, though is have your Car model have something like this
belongs_to :client
And your Client model have
has_many :cars
Then you can simply call #car.client to get what you want.
I guess you could do something like:
#cars.each do |car|
car.client
end
and in car model, I guess you alredy have:
belongs_to :client