How do I sort an activerecord result set on a i18n translated column? - ruby-on-rails

I have the following line in a view:
<%= f.select(:province_id, options_from_collection_for_select(Province.find(:all, :conditions => { :country_id => #property.country_id }, :order => "provinces.name ASC"), :id, :name) %>
In the province model I have the following:
def name
I18n.t(super)
end
Problem is that the :name field is translated (through the province model) and that the ordering is done by activerecord on the english name. The non-english result set can be wrongly sorted this way. We have a province in Belgium called 'Oost-Vlaanderen'. In english that is 'East-Flanders". Not good for sorting:)
I need something like this, but it does not work:
<%= f.select(:province_id, options_from_collection_for_select(Province.find(:all, :conditions => { :country_id => #property.country_id }, :order => "provinces.I18n.t(name) ASC"), :id, :name) %>
What would be the best approach to solve this?
As you may have noticed, my coding knowledge is very limited, sorry for that.

You need to sort the entries after they have been loaded from the database. This should probably do the trick:
Provinces.find(:all, :conditions => {:country_id => #property.country_id}).sort_by{|p| p.name}

Related

How do I search for multiple records in a search form?

I am trying to allow the user to be able to choose multiple records in a field on the search form.
Something like this:
<%= f.input_field :neighborhood_id, collection: Neighborhood.order(:name), :url => autocomplete_neighborhood_name_searches_path, :as => :autocomplete, 'data-delimiter' => ',', :multiple => true, :class => "span8" %>
It sends it to my search model like this: #search = Search.create!(params[:search])
This is what the Search.rb model does with it:
key = "%#{keywords}%"
listings = Listing.order(:headline)
listings = listings.includes(:neighborhood).where("listings.headline like ? or neighborhoods.name like ?", key, key) if keywords.present?
listings = listings.where(neighborhood_id: neighborhood_id) if neighborhood_id.present?
listings
The issue is that this is just accepting 1 neighborhood_id, so I am getting this error when I choose multiple objects:
undefined method `to_i' for ["Alley Park, Madison"]:Array
Where Alley Park and Madison are the names of 2 neighborhoods, not the IDs.
So how do I get this working?
Thanks.
Edit 1
The issue seems to not be in the lookup of the params[:search] per se, but rather in the conversion of the form input to an array of entries. I tried changing the search method to be something like:
listings = listings.includes(:neighborhood).where("neighborhoods.name like ?", neighborhood_id) if neighborhood_id.present?
Don't get hung up on the fact that I am looking up neighborhood.name and passing in neighborhood_id. I just did that because I know that the params for the field neighborhood_id were actually the names of the neighborhood. If this had worked, I would have refactored some stuff, but it didn't. So don't get hung up on that.
But that still returns the error undefined method 'to_i'....
Also, I still get that error even if I just pass in 1 option.
listings = listings.where("neighborhood_id in (?) ", neighborhood_id)
You can get the id instead of neighborhood names from the input field like this:
<%= f.input_field :neighborhood_id, collection: Neighborhood.order(:name), :url => autocomplete_neighborhood_name_searches_path, :as => :autocomplete, 'data-delimiter' => ',', :multiple => true, :class => "span8", :input_html => { :id => "neighborhood_id" } %>

Collection select with many-to-many in same model in rails gives error

I have a weird rails database/form problem.
I got 1 table with Courses, and 1 table Prerequisites, which has 2 columns that both hold Course_ids (columns are called "course_a_id" and "course_b_id").
In the Course model I have this:
:has_and_belongs_to_many(:prerequisites,
:join_table => "prerequisites",
:foreign_key => "course_a_id",
:association_foreign_key => "course_b_id",
:class_name => "Course")
From this SO answer
This works if I put the prerequisites in by Console, something like this:
Course.find(3).prerequisites = [Course.find(1), Course.find(2)]
This form field however does not let me put prereqs in the DB:
<%= f.label :prerequisite, "Prerequisites" %>
<%= f.collection_select(:prerequisites, Course.all, :id, :name,
{:multiple => true}, :multiple => "multiple") %>
Gives this error after posting with one option selected with value 1:
"Course(#-631146998) expected, got String(#77208170)"
and in the parameter dump:
"prerequisites"=>["",
"1"]}
I have no clue how that "" ended up in the params and can't figure out another way to create the form field. I think my model is set up correctly.

multiparameter error with datetime_select

I have the following code in my form.
<%= f.datetime_select(:date_time, :prompt => {:day => 'Day', :month => 'Month', :year => 'Year'}, :start_year => Date.today.year, :end_year => Date.today.year + 2, :minute_step => 15, :include_blank => false) %> if either one is blank.
When one of the fields is left blank, I get:
1 error(s) on assignment of multiparameter attributes
The params that are being passed are:
{"utf8"=>"✓",
"authenticity_token"=>"kQpfsj5RxnDtxkvBdwPEFnX1fY6euKnMQeDRAkvJvIE=",
"event"=>{"description"=>"",
"venue"=>"",
"street"=>"",
"city"=>"",
"country_id"=>"",
"date_time(1i)"=>"",
"date_time(2i)"=>"",
"date_time(3i)"=>"",
"date_time(4i)"=>"00",
"date_time(5i)"=>"00",
"ticket_url"=>""},
"x"=>"94",
"y"=>"12"}
Anyone know why this is occurring?
There seems to be a "dirty" fix for this at this link, but perhaps there is a better solution in Rails 3?
Christian. This is a bug in Rails that checks the database to infer the type needed for the multiparameter attributes. My guess is that your "date_time" attribute is not associated with a time column in your database.
I recently tackled this problem where I wanted a non-database attribute to accepted multiparameter attributes, this was the best solution I could come up with:
I found myself wanting to set an attr_accessor to handle passing a date to my model in a form_for tag with the f.datetime_select helper. So this is what I had:
Model:
attr_accessor :my_time
View:
<%= f.datetime_select :my_time %>
Unfortunately when I submit my form I get this:
1 error(s) on assignment of multiparameter attributes
Well it turns out that this is actually a Rails bug a ticket for which has been submitted. In the meantime how do we make this work? The only solution I could find that was remotely attractive was to make use of composed_of as a replacement for attr_accessor. so...
Model:
composed_of :my_time,
:class_name => 'Time',
:mapping => %w(Time to_s),
:constructor => Proc.new{ |item| item },
:converter => Proc.new{ |item| item }
I know almost nothing about the composed_of method so you should probably do your own reading on it, but what I do know is that it creates both a reader and writer for the given instance variable, and more importantly, the setter accepts multiparameter attributes. How I chose the options:
class_name: the name of our expected class. In this case, Time
mapping: the first argument is the class and the second argument seems to work with any method that an instance of the class responds to. I chose to_s
constructor: Not really sure how this is supposed to work. Seems to be called when #my_time is nil.
converter: Not really sure how this is supposed to work. Seems to be called when from my_time=, but doesn't seem to be applied with mass assignment.
One problem I ran into with this solution was that times were getting set in UTC instead of the environment's time zone. So unfortunately we cannot use my_time directly, but instead need to convert it to the proper time zone:
Time.zone.parse(my_time.to_s(:number))
What Does ActiveRecord::MultiparameterAssignmentErrors Mean?
def initialize(attributes={})
date_hack(attributes, "deliver_date")
super(attributes)
end
def date_hack(attributes, property)
keys, values = [], []
attributes.each_key {|k| keys << k if k =~ /#{property}/ }.sort
keys.each { |k| values << attributes[k]; attributes.delete(k); }
attributes[property] = values.join("-")
end
I had the same problem using a date dropdown that wasn't backed by a database attribute. I wrote a little Rack middleware to cope with the problem:
class DateParamsParser
def initialize(app)
#app = app
end
def call(env)
if %w{POST PUT}.include? env['REQUEST_METHOD']
params = Rack::Utils.parse_query(env["rack.input"].read, "&")
# selects only relevant params like 'date1(1i)'
filtered_params = params.select{ |key, value| key =~ /\(\di\)/ }
# delete date params
filtered_params.each { |key, value| params.delete(key) }
# returns something like {'date1' => [2012, 5, 14], 'date2' => [2002, 3, 28]}
date_array_params = filtered_params.sort.reduce({}) do |array_params, keyvalue|
date_key = keyvalue.first.match(/(.+)\(/)[1] + ']'
array_params[date_key] ||= []
array_params[date_key] << keyvalue.last
array_params
end
# Creates params with date strings like {'date1' => '2012-5-14', 'date2' => '2002-3-28'}
date_params = Hash[date_array_params.map{ |key, date_array| [key, date_array.join('-')] }]
params.merge! date_params
env["rack.input"] = StringIO.new(Rack::Utils.build_query(params))
env["rack.input"].rewind
end
#app.call(env)
end
end
And in application.rb I put
config.middleware.insert_before ActionDispatch::ParamsParser, "DateParamsParser"
Note that I only build a date string here. So if you also require time you'll need to build the date_params differently.
I faced the same problem with the model below
class Reservation < ActiveRecord::Base
attr_accessor :sid, :check_in, :credit_card_number, :expiration_date
attr_accessible :expiration_date
end
The corresponding form with the field for the expiration date:
<div class="field">
<%= f.label :expiration_date %>
<%= f.date_select(:expiration_date, start_year: Time.now.year + 3, :end_year => Time.now.year - 3, discard_day: true) %>
</div>
as mentioned by #gabeodess the problem is checking the database to infer the type accordingly the solution I did for it was adding the following code to the model to put the type of the needed attribute in this case :expiration_date so the model is modified to be the following
class Reservation < ActiveRecord::Base
attr_accessor :sid, :check_in, :credit_card_number, :expiration_date
attr_accessible :expiration_date
columns_hash["expiration_date"] = ActiveRecord::ConnectionAdapters::Column.new("expiration_date", nil, "date")
end
Hope this is useful
Remove :include_blank => false from your code.
<%= f.datetime_select(:date_time, :prompt => {:day => 'Day', :month => 'Month', :year => 'Year'}, :start_year => Date.today.year, :end_year => Date.today.year + 2, :minute_step => 15 %>
Thanks....
I was facing the same problem.
I just added attr_accessible for that attribute and it works fine.
Hope it helps.

How can I retrieve information from an active record with collection select?

I have 3 models (Users - Membership - Community)
The users can become members to many communities. For this I made a Membership that contain the user_id, community_id.
After connected, the user have to choose a community. The model User as a community_id that contain that unique community.
When editing, he would be able to change this community.
If I do this :
<%= f.collection_select :community_id, Community.find(:all), :id, :name, { :allow_blank => 'Select a community' }, :style => "width: 200px;" %>
All the communities happier, also that who he is not member.
I tried this :
<%= f.collection_select :community_id, Membership.find(:all), :community_id, :id, { :allow_blank => 'Select a community' }, :style => "width: 200px;" %>
But I show only the number (:id) of the Membership…
How can I join this id with the name of the community ?
Not sure if this will work but try it out:
member.rb # add a method to the member model that returns the
def community_name
community.name
end
#view
<%= f.collection_select :community_id, Membership.find(:all, :include => :community), :community_id, :community_name, { :allow_blank => 'Select a community' } %>
The :include option prefetches all communities in the membership collection in one query.
I think you were closer with your first attempt but instead of finding all communities you need to just find communities that the user is a member of. So instead of Community.find(:all) you would use:
Community.find(:all,
:includes => :memberships,
:conditions => ['memberships.user_id = ?', #user.id])
This assumes that you have an #user variable set up for your view. You need this to restrict the find to just communities that your user is a member of.
It also assumes that there's an association on Community: has_many :memberships. I've guessed you've got that already from the question.

Elegant "select distinct" for collection_select

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.

Resources