I have an f.select input on my Rails app that comes from this helper method.
def option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method)
collection.map do |group|
option_tags = options_from_collection_for_select(
group.send(group_method), option_key_method, option_value_method)
content_tag(:optgroup, option_tags, :label => group.send(group_label_method))
end.join.html_safe
end
The select in the view is shown below.
<%= f.select(:type_id, option_groups_from_collection_for_select(#categories, :types, :category, :id, :name)) %>
When saving the Post, the correct type_id is getting saved, but when I go and edit the post, the select doesn't show the currently selected item like it's supposed to. I'm assuming something is wrong in my code.
Here is my category model
has_many :posts
has_many :types, :order => "name"
and here is my type model
belongs_to :category
You have to provide a 5th argument which is the selected key. Try the code below:
<%= f.select(:type_id, option_groups_from_collection_for_select(#categories, :types, :category, :id, :name, f.object.type_id)) %>
f.object.type_id returns the type_id attribute of the object passed in the form if it has one. Otherwise, it would be nil and will not have anything selected.
I had the same problem and I found that I had simply misspelled the object's name in my controller file. It did not save because the matching object could not be found.
Related
I have a one-to-many association in my code, like this:
class Second < ActiveRecord::Base
has_many :firsts
end
class First < ActiveRecord::Base
belongs_to :second
accepts_nested_attributes_for :second
end
In my erb for First, I have:
<%= f.input :one_field, :label => false %>
<%= f.semantic_fields_for :second do |cp_f| %>
<%= cp_f.input :another_field, :as => :string, :label => "another field" %>
<%= end %>
The form correctly populates the data in the nested table.
I need to put some validation in the controller, and I'd like to point the user to the field where the error occurred. If I write an error like this:
errors.add :one_field, "This is wrong"
This works no problem and puts the error on the page right by the field. But I'd like to do the same thing for the nested field, like maybe:
errors.add :second.another_field, "Another wrong one"
But I get an error:
undefined method `another_field' for :second:Symbol
Is there a way to put an error on the nested field?
The problem was accessing the errors member of First. What I needed was:
second.errors.add :another_field, "Another wrong one"
I am using ActiveAdmin in my rails application.My setup is the following:
app/models/translation_type.rb
class TranslationType < ActiveRecord::Base
has_many :translation_details
accepts_nested_attributes_for :translation_details, allow_destroy: true
end
app/models/translation_detail.rb
class TranslationDetail < ActiveRecord::Base
belongs_to :translation_type
attr_accessor :id, :language_from, :language_to, :price
end
app/admin/translation_type.rb
ActiveAdmin.register TranslationType do
permit_params :translation_type, translation_details_attributes: [:id, :language_from, :language_to, :price, :_destroy]
index do
column :translation_type
column :translation_details
column :created_at
column :updated_at
actions
end
filter :translation_type
filter :created_at
form do |f|
f.inputs "Translation Type Details" do
f.input :translation_type
f.input :created_at
f.input :updated_at
end
f.inputs do
f.has_many :translation_details, allow_destroy: true do |tran_det|
tran_det.input :language_from, :collection => ['Gr', 'En', 'Fr', 'De']
tran_det.input :language_to, :collection => ['Gr', 'En', 'Fr', 'De']
tran_det.input :price
end
end
f.actions
end
controller do
before_filter { #page_title = :translation_type }
end
I don't need a section for translation_detail so I have omited app/admin/translation_detail.rb
I want to have nested forms of translation_detail in my translation_type form. Using the code above creates the nested form but after submit the attributes of translation_detail are not saved. Furthermore when I try to update or delete a translation_detail this message is shown
Couldn't find TranslationDetail with ID=* for TranslationType with ID=*
How can this be fixed?
It seem that this line
attr_accessor :id, :language_from, :language_to, :price
in app/models/translation_detail.rb, was causing the error, but I am not really sure why.
I ran into the same problem when saving a field that previously wasn't being saved and did not exist in the DB. I had to remove the attr_accessor :fieldName from the model file to make it permanent.
As this answers states: attr_accessor on Rails is only ever used if you don't want the attributes to be saved to the database, only stored temporarily as part of an instance of that Model.
What probably was happening in the question's example is that it could not save the TranslationDetail because everything except for its relationship to TranslationType was being discarded before saving.
I want to rewrite manual logic for updating a table to use nested attributes while using a third table to select multiple id values. This is my first time attempting this.
I have a MediaLibrary model. I have a PageTitle model which contains names. I have a MediaPage model which contains the id fields for MediaLibrary and PageTitle. I want to populate MediaPage using nested attributes. I want to create a collection_select for PageTitle which has the values that populate page_title_id MediaPage.
Here are my model definitions.
media_library.rb
has_many :media_pages, dependent: :destroy
accepts_nested_attributes_for :media_pages, allow_destroy: true
media_page.rb
belongs_to :media_library
validates :media_library_id, presence: true
validates :page_title_id, presence: true
page_title.rb
has_many :media_pages
My original view code when I was manually collecting the values is below.
<%= fields_for :media_pages do |media_page| %>
<%= media_page.label :media_page, "Page Titles" %><%= media_page.collection_select(:page_title_id, PageTitle.order("name"), :id, :name, {}, {multiple: true}) %>
<% end %>
Here is my current view:
<%= f.label :media_pages, "Page Titles" %>
<%= f.fields_for :media_pages do |media_page| %>
<%= media_page.collection_select(:page_title_id, PageTitle.order("name"), :id, :name, {}, {multiple: true}) %>
<% end %>
If I have more than one MediaPage row the view will show a collection_select list for each row. For example if I have two MediaPage rows I will see one collection_select list with the PageTitle name for the first record highlighted and a second collection_select list with the PageTitle name for the second record highlighted. As you can see the only things I changed from my first fields_for statement and the second one is (1) moving the label outside of the fields_for and (2) adding f.
I attempted something similar in Rails 3 Rails 3.2.13 - Delete Entry From Nested Attributes. When I attempted this in Rails 3 the collection_select only displayed one list with multiple rows highlighted. Since there was an issue with a blank array entry I decided to continue doing the manual process of updating the intermediate table.
What I want is to have one collection_select list displayed on my view where multiple PageTitle ids can be selected and populate the MediaPage table using nested attributes. If rows exist I want the corresponding PageTitle rows to be highlighted.
I have been trying to solve this for a long while. Through my research I have not found any examples where nested attributes were populated using a separate table.
Any help would be appreciated.
After doing more checking I was able to find a solution. I implemented something similar to what I read on Rails has_many :through and collection_select with multiple.
media_library.rb
has_many :page_titles, through: :media_pages
has_many :media_pages, dependent: :destroy
accepts_nested_attributes_for :media_pages, allow_destroy: true
media_page.rb
belongs_to :media_library
belongs_to :page_title
page_title.rb
has_many :media_pages, dependent: :destroy
I replaced the fields_for with the following collection_select statement.
<%= collection_select(:media_library, :page_title_ids, PageTitle.order("name"), :id, :name, {:selected => #media_library.page_title_ids, include_hidden: false}, {:multiple => true}) %>
Now I have the rows in the drop down list selected with the page_title_ids from MediaPage. I am manually deleting and adding the MediaPage rows from params[:media_library][:page_title_ids].
I am trying to create multiple checkbox tags for second level embedded documents. However, now it just creates new objects.
I have models
class Paprasa
include Mongoid::Document
attr_accessible :id, :bkompetencijas, :bkompetencijas_attributes
accepts_nested_attributes_for :etapa
accepts_nested_attributes_for :bkompetencijas, :allow_destroy => true
embeds_many :bkompetencijas , class_name: 'Kompetencija', inverse_of: :paprasabk, :cascade_callbacks => true
end
#-----------------------------------------------
class Kompetencija
include Mongoid::Document
attr_accessible :id, :paprasabk, :paprasabk_attributes, :siekinys, :siekinys_attributes
accepts_nested_attributes_for :paprasa, :allow_destroy => true
accepts_nested_attributes_for :siekinys, :allow_destroy => true
embedded_in :paprasabk, class_name: 'Paprasa', inverse_of: :bkompetencijas
embeds_many :siekinys, class_name: 'Siekiny' , inverse_of: :kompetencija, :cascade_callbacks => true
end
#-----------------------------------------------
class Siekiny
include Mongoid::Document
field :matricos_ids, :type => Array, :default => []
attr_accessible :id, :kompetencija, :kompetencija_attributes, :matricos_ids
accepts_nested_attributes_for :kompetencija
embedded_in :kompetencija , class_name: 'Kompetencija', inverse_of: :siekinys
end
a form
<%= form_for [#paprasa],:html => { :multipart => true} do |f| %>
<%= f.fields_for :dkompetencijas do |form_inner| %>
<%= form_inner.fields_for :siekinys do |form_inner_inner| %>
<% #some_other_object.each_with_index do |d,indexx| %>
<%= check_box_tag "#{field_name_for_js(form_inner_inner, "matricos_ids")}[]", d.id, form_inner_inner.object.matricos_ids.include?(d.id), :id => "#{field_id_for_js(form_inner_inner, "matricos_id_") << d.id.to_s}" ).to_s %>
<% end %>
<% end %>
<% end %>
<% end %>
where
def field_id_for_js(builder, attribute)
"#{builder.object_name}[#{attribute.to_s.sub(/\?$/,"")}]".gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
end
def field_name_for_js(builder, attribute)
"#{builder.object_name}[#{attribute.to_s.sub(/\?$/,"")}]"
end
it gives me ouput like this
<input id="paprasa_dkompetencijas_attributes_0_siekinys_attributes_0_matricos_id_506567916226f718e50000cb" name="paprasa[dkompetencijas_attributes][0][siekinys_attributes][0][matricos_ids][]" type="checkbox" value="506567916226f718e50000cb" />
<input id="paprasa_dkompetencijas_attributes_0_siekinys_attributes_1_matricos_id_506567916226f718e50000cb" name="paprasa[dkompetencijas_attributes][0][siekinys_attributes][1][matricos_ids][]" type="checkbox" value="506567916226f718e50000cb" />
and parameters
{"paprasa"=>{"dkompetencijas_attributes"=>{"0"=>{"siekinys_attributes"=>{"0"=>{"id"=>"505adfd26226f7555a000191", "matricos_ids"=>["506567916226f718e50000cb"]}, "1"=>{"id"=>"507558176226f75fa5000033"}, "2"=>{"id"=>"512f19626226f765c7000071"}, "3"=>{"id"=>"512f36456226f765c70000c8"}}, "id"=>"505adfd26226f7555a000190"}}}, "user_id"=>"5058514b6226f73ae4000064", "etapa_id"=>"505851516226f73ae4000065"}
this just creates new "siekiny" objects instead of updating existing ones. What I am doing wrong?
I actually ran into this problem recently. Accepts_nested_attributes_for cannot(yet) decipher with a new parent object form whether to create new child objects or find and update existing ones. If you don't want your child objects being duplicated when you create a new parent object then you will have to create a custom setter method to handle the find_or_create for you.
Checkout THIS question which solves the problem by overriding autosave associations.
Or, checkout THIS question which solves the problem by using reject_if and a custom method.
Another option to solve this problem is to break down the hash in the controller and do all object creating there instead of in the model.
However, this did not work for me. For some actions it worked several times and then broke again. I even tried from #some_other_object side (this object is referenced from paprasa and it just kept creating new objects. The only way I got this was putting some "hackish" things in controller, which I really don't like.
I took parent object, iterated through child using index on params to look for specified array field and assigned to child attribute, then the .save method. Now everything works, but i do not Like it...
I've setup some models in the table inheritance fashion and everything seems to be all fine and dandy. But, when I use a collection select field to select values from one of the models it saves it but it saves the ID of the data and not the actual value of the data. So when I try to display the value on the show view it just shows the corresponding ID and not the actual value.
Here is my setup. I'm using formtastic as a side note.
View
<%= show_field "County", #company.county %>
Form
<%= f.input :county, :label => 'County', :as => :select, :collection => County.find(:all) %>
Base Model
class Tag < ActiveRecord::Base
before_create :set_type
before_update :set_type
attr_accessible :type, :name, :category
belongs_to :company
def set_type
self.type = self.category
end
end
Inherited Model
class County < Tag
end
The tag/county is supposed to store the id not the value. Within your view you can then reference the name field of the county, or whichever field you wish to show
<%= show_field "County", #company.county.name %>