Elegant "select distinct" for collection_select - ruby-on-rails

I am fairly new to RoR and hacking on a small application. In order to display existing values of some model column in a form, I currently do:
<% form_for([#testbox, #testitem]) do |f| %>
<%= f.label :sortkey %><br />
<%= f.collection_select :sortkey, Testitem.groups , :sortkey, :sortkey, {:include_blank => true} %>
<% end %>
In the model Testitem I have:
def Testitem.groups
return find_by_sql("SELECT DISTINCT sortkey from testitems; ")
end
I am sure there is a more elegant solution to this? I have tried find(:all).sortkey.unique but that throws undefined method 'sortkey' for #<Array:0x59d1b60>

This accomplishes the same thing but I'm not sure it's any more elegant:
Testitem.find(:all, :select => "DISTINCT sortkey")

It's probably much faster to do it Patrick Robertson's way. You can also do it with the group option:
Testitem.find(:all, :group => "sortkey")
using the :select option will put the DISTICT sortkey into the SQL query, while using :group adds GROUP BY sortkey. I don't know which one is faster.
You could also do it by just returning the strings of sortkey as well:
Testitem.find(:all).map(&:sortkey).uniq
This is probably slower because it will retrieve all the items and do the filtering on the Ruby side instead of the SQL side.

Related

How to exclude a certain selection from a collection_select list?

Simply I have a collection_select which allows a user to select any users that share the same business.id. It uses the list #colleagues created in Ruby:
#events_controller#new
#colleagues = User.where(business_id: current_user.business_id)
#events/new.html.erb
<%= f.collection_select :id,
#colleagues,
:id,
:full_name %>
But at the moment, this list includes the current_user, which I do not want to happen (the user should not be able to select them self). Is there a way I can exclude the current_user from this list from querying the database? I am using SQLite.
Thank you, and any further explanation would be much appreciated as I am new to Ruby and SQL.
#colleagues = User.where(business_id: current_user.business_id).where.not(id: current_user.id)
You might pass to collection_select something like
#colleagues.reject {|user| user.id == current_user.id }
This code works for me in rails 6.0.0
form.collection_select :parent_id, Collection.all.where.not(id: collection.id), :id, :title

rails drop down search/filter with ransack

I have column with 3 set possibilities, say (fruit, veg, meat). I want to filter my rows with a drop down, so it only shows rows with fruits or veg etc. I have been looking at the ransack gem but all the examples make you type something into a text field. I plan to have several drop downs as will to filter other columns. Is ransack even the best way to do this?
<%= f.select :poollength_eq, options_for_poollength, {:include_blank => "Select Pool Length"}, {:style => "width:175px", class: "form-control"} %>
I defined options_for_poollength in the application_helper.rb as
def options_for_poollength
[['Long Course Meters','LCM'], ['Short Course Yards','SCY'], ['Short Course Meter','SCM']]
end

Uniq value in option_from_collection_for_select

How i can print only unique value in options_from_collection_for_select() helper ?
Because i don't want to have 10 times the same value...
<%= select_tag :type, options_from_collection_for_select(
Course.where(category_id: #category.id),
:learning_type,
:learning_type,
#type)
%>
I'm not sure what you are trying to do here. Let's say you have Course model, and you want to show the uniq learning_type of the course. You can get the results as an array with something like:
Course.where(category_id: #category.id).pluck(:learning_type).uniq
or
Course.where(category_id: #category.id).pluck('DISTINCT learning_type')
It's always better to use Fat model and use scopes in your Course model thought.
Now you can easily use the array with that form helper, something like:
<%= select_tag :type, options_for_select(Course.where(category_id: #category.id).pluck('DISTINCT learning_type')) %>

How to display Rails select field values rather than stored integers in other views

I'm using a select field in a Rails app that is NOT tied to a related model, but stores integer values for a static series of options , i.e.,
<%= select (:this_model, :this_field, [['Option1',1],['Option2',2],['Option3',3],['Option4',4]] ) %>
In a show/ index view, if I want to display the option text (i.e. Option1, Option2, etc) rather than the integer value stored in the database, how do I achieve this?
Thanks for helping a noob learn the ropes!
EDIT
Based on Thorsten's suggestion below, I implemented the following. But it is returning nil, and I can't figure out why.
Invoice model:
##payment_status_data = { 1 => "Pending Invoice" , 2 => "Invoiced" , 3 => "Deposit Received", 4 => "Paid in Full"}
def text_for_payment_status
##payment_status_data[payment_status]
end
Invoice show view:
Payment Status: <%= #invoice.text_for_payment_status %>
In the console:
irb > i=Invoice.find(4)
=> [#<Invoice id: 4, payment_status: 1 >]
irb > i.text_for_payment_status
=> nil
I've tried defining the hash with and without quotes around the keys. What am I missing?
something like this would work:
<%= form_for #my_model_object do |form| %>
<%= form.label :column_name "Some Description" %>
<%= form.select :field_that_stores_id, options_for_select({"text1" => "key1", "text 2" => "key2"}) %>
<% end %>
Update
If you later want to display the text you can get it from a simple hash like this:
{"key1" => "text 1", "key2" => "text2"}[#my_object.field_that_stores_id]
But you better store this hash somewhere in a central place like the model.
class MyModel < ActiveRecord
##my_select_something_data = {"key1" => "text 1", "key2" => "text2"}
def text_for_something_selectable
##my_select_something_data[field_that_stores_id]
end
end
Then you can use it in your views like
#my_object.text_for_something_selectable
There are many possible variations of this. But this should work and you would have all information in a central place.
Update
Ok, I used something similar for our website. We need to store return_headers for rma. Those need to store a return reason as a code. Those codes are defined in an external MS SQL Server Database (with which the website exchanges lots of data, like orders, products, and much more). In the external db table are much more return reasons stored than I actually need, so I just took out a few of them. Still must make sure, the codes are correct.
So here goes he model:
class ReturnHeader < AciveRecord::Base
##return_reason_keys = {"010" => "Wrong Produc",
"DAM" => "Damaged",
"AMT" => "Wrong Amount"}
def self.return_reason_select
##return_reason_keys.invert
end
def return_reason
##return_reason_keys[nav_return_reason_code]
end
end
Model contains more code of course, but that's the part that matters. Relevant here is, that keys in the hash are strings, not symbols.
In the views i use it like this:
In the form for edit:
<%= form_for #return_header do |form| %>
<%= form.label :nav_return_reason_code "Return Reason" %>
<%= form.select :nav_return_reason_code, options_for_select(ReturnHeader.return_reason_select, #return_header.nav_return_reason_code) %>
<% end %>
(Maybe no the most elegant way to do it, but works. Don't know, why options_for_select expects a hash to be "text" => "key", but that's the reason, why above class level method returns the hash inverted.)
In my index action the return reason is listed in one of the columns. There I can get the value simply by
#return_headers.each do |rh|
rh.return_reason
end
If you have trouble to get it run, check that keys a correct type and value. Maybe add some debug info with logger.info in the methods to see what actual data is used there.

In ruby on rails, is it possible to do a sum query with group by using the find_each batching?

I'm loading data from my database and I'm doing a sum calculation with a group by.
ElectricityReading.sum(:electricity_value, :group => "electricity_timestamp", :having => ["electricity_timestamp = '2010-02-14 23:30:00'"])
My data sets are extremely large, 100k upwards so I was wondering if its possible to use the find_each to batch this to help with memory overhead.
I can write the batching manually use limit and offset I guess but I'd like to avoid that if the code already exists.
From http://railsforum.com/viewtopic.php?pid=88198#p88198
#categories = Categories.find(:all, :joins => :animals,
:select => "categories.*, SUM(animals.weight) as weight_sum",
:group => "categories.id")
# ATTENTION: weight_sum is now a temporary attribute of the categories returned!
# and the animals are NOT eager-loaded`
<% #categories.each do |c| %>
Category: <%= c.name %><br />
Sum of Weight in this category: <%= c.weight_sum %><br />
<% end %>
It isn't ActiveRecord.sum, but it should do the trick.

Resources