How to query the activerecord based on the enum status? - ruby-on-rails

I am trying implement a search/filter action on a model Production based on a column status. The column status is of integer type. Later for the purpose of readability I used enum datatype on status column as follows.
class Production < ApplicationRecord
enum status:{
Preproduction:1,
Postproduction: 2,
Completed:3
}
end
Then I started to work on a search/filter functionality to fetch the record based on the status given by the user.
productions_controller
def filter
if params[:filter]
#productions = Production.where('productions.status like ?', "%#{params[:filter]}%")
else
#productions = Production.all
end
end
view
<%= form_tag [:filter, :productions], :method => 'get' do %>
<p>
<%= text_field_tag :filter, params[:filter] %>
<%= submit_tag "Filter", :status => nil %>
</p>
<% end %>
Now I am able to query the record properly only if I enter the integer values like 1 2 or 3 in the text field. When I enter the status like Preproduction like I assigned, I am not getting the result. I am getting a blank page. How can I fix this ? How can I make it to accept the string and query successfully ?

You can do this...
#productions = Production.where('productions.status like ?', "%#{Production.statuses[params[:filter]]}%")
Enums have a pluralized class method, so enum status in Production has a hash
Production.statuses which looks like your status hash but with the symbols changed into strings.

Related

Scope with two parameters rails

I'm following this tutorial to implement a filtering feature in my Rails app. I want admins to be able to filter by age, identifier, and a date interval the users were created. It is the last bit that is causing me some headache.
In my model user.rb, I have defined the following scopes:
scope :created_between, -> (startdate, enddate) {where(created_at: startdate..enddate)}
scope :identified, -> { where.not(identifier: [nil, '']) }
scope :age, -> (age) { where("age > ?", age)}
In my controller users_controller.rb, I use a function to filter the params:
def search
filter = params.slice(:age, :created_between, :identified)
filter.each do |key, value|
if value.present?
#users = #users.public_send(key,value)
else
#users = #users.public_send(key)
end
end
end
I differentiate between a value present or not, since the :identified scope is implemented as a checkbox and therefore passes no value like
Lastly, I have created a form for all the possible filters like so, in my view.html.erb file:
<%= form_tag users_search_path, :method => :get, :enforce_utf8 => false do %>
<%= date_field :created_between, "from" %>
<%= date_field :created_between, "to" %>
<%= check_box_tag :identified, '', false %>
<%= text_field_tag :age, "age" %>
<% end %>
The filter for age and identified works. When I submit the form the query becomes /users/search?identified=&created_between[from]=&created_between[to]= when I only check the checkbox identified (the date_field is also passed although I did not submit any date). And /users/search?age=21&created_between[from]=&created_between[to]= when I only submit an age.
My problem is that whenever I try to submit two dates for the created_between scope I get an wrong number of arguments (1 given, expected 2) error. I'm not sure that I'm submitting the date fiels correctly.
How can I pass the two needed params to the scope? Or should I do it another way instead?
Sometimes just writing out the question seems to make things clearer. However, the solution was to alter my scope function for :created_between to:
scope :created_between, -> (date) {where(created_at: date[:from]..date[:to])}

Searching for parameter value in a postgres column

I'm attempting to implement an advanced search on my Rails 5 site. The user passes in a parameter "provider_type", and I would like to return all records that contain that value. The value is chosen from a dropdown list using simple-form. My new.html.erb looks like this:
<%= simple_form_for Search.new, :html => {:class => 'form-horizontal' } do |f| %>
<%= f.input :provider_type, collection: ['Mental Health', 'Medical'] %>
<%= f.button :submit %>
<% end %>
My Search model looks like this:
class Search < ApplicationRecord
def search_providers
providers = Provider.all
providers = providers.where("provider_type LIKE ?", ['Mental Health', 'Medical']) if provider_type.present?
providers
end
end
And my Searches controller:
def SearchesController < ApplicationController
def new
#types = Provider.uniq.pluck(:provider_type)
end
private
def search_params
params.require(:search).permit(:provider_type)
end
end
end
When I try to search for 'Mental Health' in the search form, I get this error: PG::UndefinedFunction: ERROR: operator does not exist: character varying[] ~~ unknown
EDIT
When I reword it as
providers.where(provider_type: provider_type) if provider_type.present?
This produces the error "PG::InvalidTextRepresentation: ERROR: malformed array literal: "%Mental Health%" DETAIL: Array value mut start with "{" or dimension information.
Probably you need not LIKE operator but IN. IN (or ANY) checks if fields match to one of element of array:
providers.where(provider_type: provider_type) if provider_type.present?
Rails 3 / Ruby: ActiveRecord Find method IN condition Array to Parameters single quote issue

Search/ filter method for all attributes in my index table

I'm trying to write a row for my index table that filters my objects regarding a specific value of a specific column. What I have until now is this:
pimps_controller.rb:
def index
#pimps = Pimp.search(params[:search])
end
pimp.rb:
def self.search( search)
if search
where('title LIKE ?', "%#{search}%")
else
scoped
end
end
A part of view:
<%= text_field_tag :search, params[:search] %>
That filters after the objects title only so I tried to alter it to make it functional for different search fields that can filter after different attributes. I want to pass a second parameter value if someone fires the search function to make sure it triggers for the right attributes. That's what I've tried:
pimps_controller.rb
#pimps = Pimp.search(params[:search_column],params[:search])
pimp.rb:
def self.search(search_column, search)
if search
col = "%#{search_column}"
s = "%#{search}%"
where(col 'LIKE ?', s)
else
scoped
end
end
The view:
<%= text_field_tag :search, params[:search], params[:search_column => title] %>
But it's not working. I get an error message for passing the both parameters in one search field I guess. How would you do it?
Here's a simple tutorial on how to do it:
https://we.riseup.net/rails/simple-search-tutorial
In the model, you will have to add the fields with or condition to the query.
def self.search(search)
search_condition = "%" + search + "%"
find(:all, :conditions => ['title LIKE ? OR description LIKE ?', search_condition, search_condition])
end
If you want to define the field to search in the params you can use string interpolation with simple quotes:
%q(text contains "#{search.query}")
You need 2 text fields, one for the column, one for the value:
# view
<%= text_field_tag :search_value, params[:search_value] %>
<%= text_field_tag :search_column, params[:search_column] %>
# controller
#pimps = Pimp.search(params[:search_column], params[:search_value])
# Pimp model
def self.search(search_column, search_value)
if search_value.present? && search_column.present?
column = self.column_names.include?(search_column.to_s) ? search_column : 'title'
value = "%#{search_value}%"
where("#{self.table_name}.#{column} LIKE ?", value)
else
scoped
end
end
The problem about this method is that if you don't type the exact name of the column, it will search the value in the column title. I think you should use a select_tag, listing all searchable columns of the model:
# view
<%= select_tag :search_column, options_for_select(Pimp.column_names.map { |col| [col, col] }, params[:search_column]) %>
This view code will display a select tag with the available columns of the Pimp model. You can easily limit the searchable columns by defining a class method on Pimp:
# Pimp model
def searchable_columns
self.column_names - ['id', 'created_at', 'updated_at']
end
# view
<%= select_tag :search_column, options_for_select(Pimp.searchable_columns.map { |col| [col, col] }, params[:search_column]) %>

Ransack search not working if there is 'space' in search term

I am using ransack for search in my rails 3.2 application using postgres as database.
I have a Invoice model and every invoice belongs_to a buyer. Below is my search form in index page.
views/invoices/index.html.erb
<%= search_form_for #search do |f| %>
<%= f.text_field :buyer_name_cont %>
<%= f.submit "Search"%>
<% end %>
And here is my controller code.
controllers/invoices_controller.rb
def index
#search = Invoice.search(params[:q])
#invoices=#search.result(:distinct => true).paginate(:page => params[:page], :per_page => GlobalConstants::PER_PAGE )
respond_to do |format|
format.html # index.html.erb
format.json { render json: #invoices }
end
end
Let's say a invoice is there of a buyer having name "Bat Man".
If I search "Bat", I get the invoice in results.
Again if I search "Man", I get the invoice in results.
But if I search "Bat Man", I don't get the invoice in results.
I know it might be something trivial but I am not able to resolve.
Update
When I tried the sql query formed directly in database using pgAdmin, I realized that in database there were multiple spaces in the buyer name, something like "Bat.space.space.space.Man".
Can something be done so that "Bat.space.Man" search also finds "Bat.space.space.space.Man" in results?
You could sanitize your data. For instance with regexp_replace(). Run in the database once:
UPDATE invoice
SET buyer = regexp_replace(buyer, '\s\s+', ' ', 'g')
WHERE buyer <> regexp_replace(buyer, '\s\s+', ' ', 'g');
And sanitize new inserts & updates likewise.
\s .. class shorthand for "white space" (including tab or weird spaces).
The 4th parameter 'g' is for "globally", needed to replace all instances, not just the first.
Ransack not support cont search for multi terms, I solved the requirement my customized way. the details as following:
Add scope to your model:
scope :like_search, ->(column, value) {
keywords = value.to_s.split.map{ |k| "%#{k}%" }
where(Array.new(keywords.size, "#{column} ILIKE ?").join(' AND '), *keywords)
}
in your view. instead of using f.text_field :buyer_name_cont provided by ransack, use normal field helper text_field_tag :buyer_name, params[:buyer_name]
then restrict your ransack in scope:
scope = Invoice.like_search(:name , params[:buyer_name])
#q = scope.ransack(params[:q])

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.

Resources