Using .where condition for results greater than an integer - mysql

Trying to do something pretty straightforward in a Rails controller. Right now, I am showing a user all results matching their user_id from a Scoreboard table.
I now want to adjust that to show results from their user_id but also only scores greater than the integer 0. So I changed my controller to:
def index
#scoreboards = Scoreboard.where(user_id: current_user.id, "score >= 0").order(score: :desc)
end
This receives a syntax error, so my comparison in the .where is probably wrong. How should I add this second condition?

I try to avoid putting these kind of database operations directly in the controller, because the model is the more appropriate place. I'd write three scopes in the model:
class Scoreboard < ActiveRecord::Base
#...
scope :for_user_id, ->(user_id) { where(user_id: user_id) }
scope :with_scores, -> { where('score > 0') }
scope :by_descending_score, -> { order(score: :desc) }
#...
end
...then in the controller, you'd merely write this:
Scoreboard.for_user_id(current_user.id).with_scores.by_descending_score
This keeps your controller thinner (and more readable) while potentially supporting re-use of these lookups in the most atomic fashion and keeping the database logic wholly contained in the model.

I would try this:
#scoreboards = Scoreboard.where(user_id: current_user.id)
.where("category >= 0").order(score: :desc)

Related

Write ActiveRecord Query

I am having Orders with status(Pending, and Completed), Pickup_date, dropoff_date.
And i want to order conditionally on the basis of status.
If status is Pending, it should be order by pickup_time.
If status is Completed, it should be order by dropoff_time.
We can do it in single query in scope as :
scope :order_by_time, -> { order("CASE WHEN (orders.state IN ('pending', 'picked_up')) THEN 'orders.pick_up_time' WHEN (orders.state IN ('ready_for_delivery', 'delivered')) THEN 'orders.delivery_time' END") }
I would highly recommend not to mix scope responsibilities. If you do it all in one scope called order_by_time, the naming is confusing, if I see Order.order_by_time I will assume that it only orders the result, and I will be truly surprised when I learn the reality after checking the implementation...
I would recommend some isolation, which provides more flexibility for later use:
scope :pending, -> { where(status: :pending) }
scope :completed, -> { where(status: :completed) }
scope :order_by_pickup_time, -> { order(pickup_time: :desc) }
scope :order_by_dropof_time, -> { order(dropof_time: :desc) }
Then you could use them:
Order.pending.order_by_pickup_time
Order.completed.order_by_dropof_time
In the model
scope :pending, -> { where(status: "pending").order(pickup_time: :desc) }
scope :completed, -> { where(status: "completed").order(dropof_time: :desc) }
Then use Order.pending it will order by pick up time it with all pending orders
Then use Order.completed it will order it with all conmpleted orders by dropoff time
Query using conditionals
I suggest to use conditionals query
class Orders < ApplicationRecord
scope :order_by_pickup_time, ->(status) { where("created_at < ?", pickup_time) if status == "pending" }
end
Like the other examples, this will behave similarly to a class method.
class Orders < ApplicationRecord
def self.order_by_pickup_time(status)
where("created_at < ?", pickup_time) if status == "pending"
end
end
However, there is one important caveat: A scope will always return an ActiveRecord::Relation object, even if the conditional evaluates to false, whereas a class method, will return nil. This can cause NoMethodError when chaining class methods with conditionals, if any of the conditionals return false.

Ruby on Rails: Filter list of records in model before returning it to controller?

Lets assume I have the following model:
class Computer < ActiveRecord::Base
end
and I retrieve all computers in my controllers like this:
#computers = Computer.all
Now I add a feature to deactivate certain computers and filter the deactivated computer like this:
class Computer < ActiveRecord::Base
scope :not_deactivated, -> { where('deactivated IS NULL OR deactivated = ?', false) }
end
and in my controllers:
#computers = Computer.all.not_deactivated
The issue with this method is that I have to add not_deactivated in all my controllers.
Is it possible to do this filter in the model so I don't have to touch the controllers?
Easy and common thing to do in controller:
before_action :not_deactivated # , only [:index...]
private
def not_deactivated
#computers = Computer.where(your code)
end
Since the controller handles the view you must initiate the object somehow anyway. By filtering like that you can achieve what you are trying to do now with the model filter.
you can do it by declaring default_scope to run this every time you make a call to your Computer model
class Computer < ActiveRecord::Base
default_scope { where('deactivated IS NULL OR deactivated = ?', false) }
end
So, when you run Computer.all the result you get is Computer.where('deactivated IS NULL OR deactivated = ?', false)

How do you Skip an Object in an ActiveModel Serializer Array?

I have searched through all the active model serializer (v 0.9.0) documentation and SO questions I can find, but can't figure this out.
I have objects which can be marked as "published" or "draft". When they aren't published, only the user who created the object should be able to see it. I can obviously set permissions for "show" in my controller, but I also want to remove these objects from the json my "index" action returns unless it is the correct user. Is there a way to remove this object from the json returned by the serializer completely?
In my activemodel serializer, I am able to user filter(keys) and overloaded attributes to remove the data, as shown using my code below, but I can't just delete the entire object (I'm left having to return an empty {} in my json, trying to return nil breaks the serializer).
I'm probably missing something simple. Any help would be much appreciated!
class CompleteExampleSerializer < ExampleSerializer
attributes :id, :title
has_many :children
def attributes
data = super
(object.published? || object.user == scope || scope.admin?) ? data : {}
end
def filter(keys)
keys = super
(object.published? || object.user == scope || scope.admin?) ? keys : {}
end
end
That looks correct, try returning an array instead of a hash when you dont want any keys. Also, I don't think calling super is necessary b/c the filter takes in the keys.
Also, I don't think defining an attributes method is necessary.
I have chapters that can either be published or unpublished. They're owned by a story so I ended doing something like below.
has_many :unpublished_chapters, -> { where published: false }, :class_name => "Chapter", dependent: :destroy
has_many :published_chapters, -> { where published: true }, :class_name => "Chapter", dependent: :destroy
Inside of my serializer, I choose to include unpublished_chapters only if the current_user is the owner of those chapters. In ams 0.8.0 the syntax is like so.
def include_associations!
include! :published_chapters if ::Authorization::Story.include_published_chapters?(current_user,object,#options)
include! :unpublished_chapters if ::Authorization::Story.include_unpublished_chapters?(current_user,object,#options)
end
In my case, it's not so bad to differentiate the two and it saves me the trouble of dealing with it on the client. Our situations are similar but say you want to get all of the chapters by visiting the chapters index route. This doesn't make much sense in my app but you could go to that controller and render a query on that table.

Rails 4: ActiveRecord or MySQL query where no related models have attribute

Having a tough time with this one. I have a Job model, and a JobStatus model. A job has many statuses, each with different names (slugs in this case). I need an 'active' method I can call to find all jobs where none of the associated statuses has a slug of 'dropped-off'.
class Job < ActiveRecord::Base
belongs_to :agent
has_many :statuses, :class_name => "JobStatus"
validates :agent_id,
:pickup_lat,
:pickup_lng,
:dropoff_lat,
:dropoff_lng,
:description,
presence: true
class << self
def by_agent agent_id
where(agent_id: agent_id)
end
def active
#
# this should select all items where no related job status
# has the slug 'dropped-off'
#
end
end
end
Job Status:
class JobStatus < ActiveRecord::Base
belongs_to :job
validates :job_id,
:slug,
presence: true
end
The closest I've gotten so far is:
def active
joins(:statuses).where.not('job_statuses.slug = ?', 'dropped-off')
end
But it's still selecting the Job that has a dropped-off status because there are previous statuses that are not 'dropped-off'. If i knew the raw sql, I could probably work it into activerecord speak but I can't quite wrap my head around it.
Also not married to using activerecord, if the solution is raw SQL that's fine too.
Job.where.not(id: JobStatus.where(slug: 'dropped-off').select(:job_id))
will generate a nested subquery for you.
Not the cleanest method, but you could use two queries.
# Getting the ID of all the Jobs which have 'dropped-off' JobStatuses
dropped_off_ids = JobStatus.where(slug: 'dropped-off').pluck(:job_id)
# Using the previous array to filter the Jobs
Job.where.not(id: dropped_off_ids)
Try this:
def active
Job.joins(:statuses).where.not('job_statuses.slug' => 'dropped-off')
end
or this:
def active
Job.joins(:statuses).where('job_statuses.slug != ?', 'dropped-off')
end
I think you may want to reevaluate your data model somewhat. If the problem is that you're turning up old statuses when asking about Job, you likely need to have column identifying the current status for any job, i.e. job.statuses.where(current_status: true)
Then you can very easily grab only the rows which represent the current status for all jobs and are not "dropped-off".
Alternatively, if I'm misunderstanding your use case and you're just looking for any job that has ever had that status, you can just go backwards and search for the status slugs first, i.e.
JobStatus.where.not(slug: "dropped-off").map(&:job)

Return records from 2 tables using mysql and rails 3

need help with this please:
#follows = Follow.where("admin_user_id = ?", session[:id])
This returns records from the Follow table, that has the following coloumns, :admin_user_id, :assignment_id. I would now like to do this:
#assignments = Assignment.where("id = ?", #follows.assignment_id)
The Asssignment table has the following columns, :id, :name.I have tried the "#follows.assignment_id" to substitute each id which i can then use in the view like,
<% #assignment.each do |assignment| %>
<%= assignment.name %>
<% end %>
Thanks in advance for your help
Part of the problem here is your first query: Follow.where(...) returns a scope, it will not perform the query until you try to access the #follows object. This is probably not a big deal, but you should probably make it this (unless you are going to dynamically add more conditions):
#follows = Follow.where("admin_user_id = ?", session[:id]).all
This ensures that #follows is an array of Follow objects. Because it's an array (and not a single Follow) there is no assignment_id on it. Change your second query to this:
#assignments = Assignment.where(:id => #follows.map{|f| f.assignment_id}).all
Basically, .map returns an array of the return values of the block instead of the original object it was called on, meaning instead of an array of follows, it returns an array of the assignment ids of each object. When you pass this array as the conditions, it generates a SQL query something like:
select * from assignments where id IN (1, 2, 3)
if your follows have assignment ids 1, 2, and 3.
In your model:
class Follow < ActiveRecord::Base
belongs_to :assignment
end
For Assignments
class Assignments < ActiveRecord::Base
has_many :follows
end
Then you can call
#assignments = []
#follows.collect {|f| #assignments << f.assignment}
Also:
This is the error i get:undefined method `assignment_id' for #<ActiveRecord::Relation:0xb6d8104c>
Make sure your sql table has a column called assignment_id with an integer value