I have a User, and a Country models, and in User model, it has has_many :countries relationship. The country has 3 attributes -> :id, :name, :is_main
Let's say the countries table is currently populated as follow:
id name is_main
----------------------------------
1 USA true
2 Germany nil
3 France nil
4 England nil
Let's say a user is created that has countries USA, and Germany. So in this case the user.countries.pluck(:id) would return [1,2].
What I would like to achieve is that when editing the user's countries, a dropdown will appear and I am only allowed to add (or remove) countries where 'is_main' attribute is nil. In other words, in the dropdown, the USA country should either be disabled or be hidden completely to be selected. At the same time, the USA should remain in the user.countries after form submission plus any new countries that have been added from update action.
In short:
Original => user.countries.pluck(:id) => [1,2]
In edit form, if I add France to the user, the end result should be user.countries.pluck(:id) => [1,2,3]
I have tried the following:
f.collection_select(:countries, Country.where(is_main: nil), :id, :name, {}, {:multiple => true})
In doing this, the dropdown will display all the countries for me to add except for USA, which is what I desired. But the problem is when I click submit, the params[:country_ids] will be [2, 3]. As a result, after update action, the user.countries.pluck(:id) would become [2,3] instead of desired [1,2,3], effectively removing USA's id.
Is there a way to work around this? I have tried also adding :disabled option in collection_select that disables USA option, but the params[:country_ids] would still be [nil, 2, 3]. Appreciate if anyone could advise me on this.
I've come across this issue on my application. In rails 4+, set include_hidden attribute to false in multi-select selet_tags. Here's how i did it.
= f.input :countries, as: :select, collection: Country.where(is_main: nil).map{|country| [country.id, country.name]}, input_html: { multiple: true, data: { placeholder: "Countries"} }, include_hidden: false
Hope this helps you solve your issue
Actually using your example for multiple selection, your params after submit should have params[:country_ids] = [2,3]. If you are not giving the option to select USA, then it just won't be included.
You can disable options doing this:
// Your select id should be 'user_country_ids', if not change it
// This assumes that your first item will always be USA
$('#user_country_ids option:eq(0)').prop('disabled', true);
Anyway, this JS can be bypassed, so you will need to handle this on your backend too adding a validation for this in your User model:
class User
has_many :countries
validate :main_countries_always_associateds
private
def main_countries_always_associateds
errors.add(:countries, "must include main ones") if Country.where(is_main: true).any?{ |c| !self.countries.include? c }
end
end
EDIT:
If you want to always have main countries inside each user countries, then you can use before_validation callback to override this selection. I recommend to also include the disabled options on the multiple select, so the user is aware of this, the validation may become not necessary.
class User
has_many :countries
before_validation :associate_main_countries
# You should use something like this if you want your dropdown to always show marked main countries
def self.new_custom_user
User.new(countries: Country.where(is_main: true))
end
private
def associate_main_countries
Country.where(is_main: true).each do |c|
self.countries << c if !self.countries.include?(c)
end
end
end
Related
I created a new datetime row for a page, it works well. The column displays all the specific dates, but when I want to show specific rows using a date filter, it doesn't display a thing.
In the model:
before_validation :payment_date
def payment_date
self.payment_date = sales_order.payment_transactions.last.date
end
The date filter from the controller file:
filter :payment_date, :as => :date_range, :collection => proc { Complaint.all.map{|c| c.sales_order.try(:payment_transactions).try(:last).try(:date)} }
I suppose I did a couple of mistakes and i'm curious where the bad coding is.
In AA filtering is based on Ransack, so your filter would look as follows (assuming sales_order is an association on your model):
filter :sales_order_payment_transactions_date, label: 'Some label', as: :date_range
Here you basically go along the association chain - it is acceptable with ransack.
As part of an application I'm building, I need to store the days on which some action needs to happen.
Currently I have this stored in a 7-digit bit-string, with each digit corresponding to one day of the week. A value of 1 for any day means the action should happen that day, a value of 0 means it should not.
Ex. String 0101110 means the action should run on Tuesday, Thursday, Friday and Saturday every week.
In my form, I have seven checkboxes set up, one for each name.
How would I go about merging these seven Boolean results into the one bitstring, such that I can save it in the database?
Would you do this in the view, or the model?
I've done some thinking myself, and one option (in the view) could be to set up a hidden field in the form that is the result of joining/concatenating the seven Booleans from the checkboxes. Then the checkboxes' values could be discarded on submit. I'm just not quite sure how to do this in rails.
Another option could be to handle the merging in the model. Again - not quite sure what to do.
Any help appreciated, thanks in advance.
Define the checkbox values for the weekdays as virtual attributes on your model, and concatenate them on a before_save hook. In your model:
attr_writer :mon, :tue, :wed, :thu, :fri, :sat, :sun
before_save :set_weekdays
private
def set_weekdays
self.weekdays = bitstr(#mon) + bitstr(#tue) + bitstr(#wed) # etc
end
def bitstr(bool)
bool ? "1" : "0"
end
Make sure to permit mon and co as strong parameters for the model in Rails 4. You might also want to define getters for these attributes that would get filled from the bitstring, so that the form could render with the correct checkboxes pre-selected on edits.
I am assuming the week column is week
You can make the week column of type: text and add this to your model
serialize :week : this helps to save an array in the column
next, create a class Week, where week.rb:
class Week
include ActiveModel::Model
attr_accessor :id, :name
DAYS = [
{id: 1, "day" => "monday"},
{id: 2, "day" => "tuesday"},
...
]
def self.days
DAYS.map {|d| self.new(
id: d[:id],
name: d[:day]
)}
end
end
and then, you can call the checkboxes from the view as follow:
<%= f.collection_check_boxes(:week, Week.days, :id, :name, include_hidden: false) do |b| %>
<%= b.check_box(class: "check_box") %> <%= b.object.name %>
<% end %>
lastly, dont forget to make the week atribute accesible from the controller. In rails 4, this will be:
params.require(:user).permit(week: [])
Hope this helps you?
In my application, users describe buildings. A user should be able to specify in which neighborhood a building exists using a grouped select. The models look like:
class Building
include Mongoid::Document
belongs_to :neighborhood
end
class Neighborhood
include Mongoid::Document
field :name, type: String, default: nil
field :borough, type: String, default: nil
field :city, type: String, default: nil
end
Using simple_form, I'm trying to generate a grouped select represent a list of neighborhoods the building might belong to.
= building_form.association :neighborhood, as: :grouped_select, collection: Neighborhood.where(city: city), group_method: :borough
Which ideally creates something like:
Borough #1
Downtown
Uptown
Borough #2
Suburbs
...
However, I get this error:
undefined method `map' for "Borough #1":String
It appears it is calling Neighborhood.borough.map, and because a String doesn't have a map function, it errors out. How do I fix this?
I've struggled with this for sometime, and unfortunately the intuitive 'Rails' magic I was hoping to get from association doesn't seem to exist. It's using the underlying Rails grouped_collection_select, which doesn't seem to handle objects/models very well.
Instead, it appears to handle Arrays much better. According to this documentation, the collection input should be in the form of:
[
['group_name',
[
['item-name','item-value'],
['item2-name','item2-value'],
...(more items)...
]
],
['group2_name',
[
['item3-name','item3-value'],
['item4-name','item4-value'],
...(more items)...
]
],
...(more groups)...
]
MongoDB models don't lend themselves to this format naturally, so I wrote a helper method on my Neighborhood class:
def self.grouped_by_borough(city)
groups = []
Neighborhood.where(city: city).distinct(:borough).each_with_index do |borough, index|
groups << [borough, Neighborhood.where(city: city, borough: borough)]
end
return groups
end
Then my association looks like:
= building_form.association :neighborhood, as: :grouped_select, collection: Neighborhood.grouped_by_borough(city), group_method: :last, option_key_method: :name, option_value_method: :id
This also automatically selects any previously selected neighborhood, which is convenient for 'edit' forms.
If any Rails forms/Mongoid gurus have a cleaner way of handling this, I'd love to hear about it.
[I'm new to rails, and I hope it's not a silly question, seen a similar question but it's for PHP and doesn't help in my case]
To explain my problem, I'm using a analogy to users here. Lets say I have users table in my app, I have added a field called user_type to users table. Now I want to specify which type of user is.
lets say I have 5 types of users eg. moderator, administrator, consumer etc.
I don't want to make user_type field to be string type to store user type. Instead I want to make user_type to store integer and then map these integer values to respective string values.
Advantage to this approach is that I can change what a user type is called. Suppose that I no longer wish to call consumer a consumer and instead wish to call it something else.
I believe storing integer in db is better and gives some flexibility.
I know I can create select menu using formtastic(I'm using active_admin as admin panel, formtastic is used for forms)
<%= f.input :user_type, :as => :select, :collection => {
0 => "Admin",
1 => "Moderator",
2 => "Consumer",
} %>
and then store values in db, and then select these users from db.
I want to know Is there a better way or approach to do it in rails or there is some gem available to do this or some other approach you prefer and why you recommend it.
I'm using postgresql as database.
Thanks!!
I personally like the active_enum gem combined to simple_form because it's really simple to implement and they work fine together.
In your case, you would have to define an enum class like this :
class Type < ActiveEnum::Base
value 1 => 'Admin'
value 2 => 'Moderator'
value 3 => 'Consumer'
end
Then in your User model, you simply add this :
enumerate :user_type, :with => Type
And what is really great with simple_form is that you simply have to call :
<%= f.input :user_type =>
to get a select with all your values.
Try this
# user.rb
USER_TYPES = { moderator: 1, superuser: 2, admin: 3, client: 4 }
# views
select :user, :user_type, User::USER_TYPES
This saves the integer values to the database. If you want to get the the string equivalent, use User::USER_TYPES.key(#user.user_type)
EDIT: forgot to add scopes
scope :moderators, where(user_type: USER_TYPES[:moderator])
scope :superusers, where(user_type: USER_TYPES[:superuser])
...
or
USER_TYPES.each do |user_type, value|
scope :"#{user_type}s", where(user_type: USER_TYPES[user_type])
end
I'm trying to use elasticsearch via tire gem for a multi-tenant app. The app has many accounts with each account having many users.
Now I would like to index the User based on account id.
User Model:
include Tire::Model::Search
mapping do
indexes :name, :boost => 10
indexes :account_id
indexes :company_name
indexes :description
end
def to_indexed_json
to_json( :only => [:name, :account_id, :description, :company_name],
)
end
Search Query:
User.tire.search do
query do
filtered do
query { string 'vamsikrishna' }
filter :term, :account_id => 1
end
end
end
The filter works fine and the results are displayed only for the filtered account id (1). But, when I search for a query string 1:
User.tire.search do
query do
filtered do
query { string '1' }
filter :term, :account_id => 1
end
end
end
Lets say there is an user named 1. In that case, the results are getting displayed for all the users with account id 1 and the user too. This is because I added account_id in the to_indexed_json. Now, how can I display only the user with name 1? (All users should not be available in the hits. Only the user with name 1 should be displayed)
When there are no users with 1 as name or company name or description, I just don't want any results to be displayed. But, in my case as I explained I would get all the users in the account id 1.
You are searching on the _all special field, which contains a copy of the content of all the fields that you indexed. You should search on a specific field to have the right results like this: field_name:1.
If you want you can search on multiple fields using the query string:
{
"query_string" : {
"fields" : ["field1", "field2", "field3"],
"query" : "1"
}
}