I'm having issue creating a nested polymorphic form. I am following the solution from this problem:
Rails: has_many through with polymorphic association - will this work?
The description was: A Person can have many Events and each Event can have one polymorphic Eventable record
Here are the relevant models:
class Event < ActiveRecord::Base
belongs_to :person
belongs_to :eventable, :polymorphic => true
end
class Meal < ActiveRecord::Base
has_one :event, :as => eventable
end
class Workout < ActiveRecord::Base
has_one :event, :as => eventable
end
class Person < ActiveRecord::Base
has_many :events
has_many :meals, :through => :events, :source => :eventable,
:source_type => "Meal"
has_many :workouts, :through => :events, :source => :eventable,
:source_type => "Workout"
end
My controller looks like this:
def
#person = Person.new
#person.meals.new
#person.workouts.new
new
My view looks like this:
<%= form_for #person do |person| %>
<%= person.fields_for :meals, #person.meals.build do |meals| %>
<%= meals.text_field :food %>
<% end %>
<% end %>
The error I am currently getting is:
unknown attribute: person_id
I would think that the polymorphic association is hindering the creation of the object because meals can't be create until person has been created? Did I set up the form correctly? Thanks!
You'll need to include a person_id attribute in the events table (as per the belongs_to association)
There are also some other issues you should resolve:
Controller
def new
#person = Person.build
end
def create
#person = Person.new(person_attributes)
#person.save
end
private
def person_attributes
params.require(:person).permit(:your, :attributes, events_attributes: [:eventable_type, :eventable_id, meal_attributes: [:params], workout_attributes: [:params]])
end
Models
#app/models/person.rb
Class Person < ActiveRecord::Base
has_many :events
has_many :meals, :through => :events, :source => :eventable,
:source_type => "Meal"
has_many :workouts, :through => :events, :source => :eventable,
:source_type => "Workout"
def self.build
person = Person.new
person.events.build.build_meal
person.events.build.build_workout
end
end
Views
#app/views/people/new.html.erb
<%= form_for #person do |person| %>
<%= person.fields_for :events do |e| %>
<%= e.fields_for :meal %>
<%= e.text_field :food %>
<% end %>
<% end %>
<% end %>
Related
I have three objects: Contact, Sector, and Contact_sector.
Contact contains an id and some other irrelevant (non-reference) columns
Sector contains an id and sector column with ~10 editable entries
Contact_sector has a contact_id reference and a sector_id reference. In my mind I imagine that every sector that applies to some contact can be found here, and if a sector is un-applied it is removed from here.
I want to have a collection of checkboxes in the contact _form formed from list of entries in :sectors, but updating the form with certain boxes ticked adds/removes entries from :contact_sectors.
Where am I going wrong?
UPDATED: Fixed strong_params to permit sectors, now I am unable to find the sectors by id ActiveRecord::RecordNotFound (Couldn't find Sector with ID=["1", ""] for Contact with ID=1)
Models:
class Contact < ActiveRecord::Base
has_many :contact_sectors
has_many :sectors, through: :contact_sectors
accepts_nested_attributes_for :contact_sectors, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :sectors, :reject_if => :all_blank, :allow_destroy => true
end
class Sector < ActiveRecord::Base
has_many :contact_sectors
has_many :contacts, through: :contact_sectors
def name_with_initial
"#{sector}"
end
end
class ContactSector < ActiveRecord::Base
belongs_to :contact
belongs_to :sector
end
View:
<%= f.fields_for(:sectors) do |s| %>
<%= s.collection_check_boxes :id, Sector.all,
:id, :name_with_initial,
{ prompt: true }, { class: 'form-control' } %>
<% end %>
Controller
def edit
#contact.sectors.build
end
def contact_params
#Not sure if I need something like this or not
params['contact']['sectors'] = params['contact']['sectors']['id'].split(',')
params.require(:contact).permit(:firstname, :lastname,
contact_sectors_attributes: [:id],
sectors_attributes: [:_destroy, {:id => []}])
end
Instead of creating the join model explicitly you can just declare the relationship as has_many through: and let ActiveRecord handle the join model:
class Contact < ActiveRecord::Base
has_many :contact_sectors
has_many :sectors, through: :contact_sectors
accepts_nested_attributes_for :sector,
reject_if: :all_blank, allow_destroy: true
end
class Sector < ActiveRecord::Base
has_many :contact_sectors
has_many :contacts, through: :contact_sectors
end
class ContactSector < ActiveRecord::Base
belongs_to :contact
belongs_to :sector
end
<%= form_for(#contact) do |f| %>
<%= f.fields_for(:sectors) do |s| %>
<%= s.collection_check_boxes :id, Sector.all,
:id, :name_with_initial,
{ prompt: true }, { class: 'form-control' } %>
<% end %>
<% end %>
models
class Contact < ActiveRecord::Base
has_many :sectors, through: :contact_sectors
has_many :contact_sectors
accepts_nested_attributes_for :sectors
end
class Sector < ActiveRecord::Base
has_many :contacts, :through => :contact_sectors
has_many :contact_sectors
end
class ContactSector < ActiveRecord::Base
belongs_to :contact
belongs_to :sector
end
view
<%= form_for(#contact) do |f| %>
<% Sector.all.each do |sector| %>
<%= check_box_tag "contact[sector_ids][]", sector.id, f.object.sectors.include?(sector) %>
<%= sector.sector %>
<% end %>
<% end %>
controller
def update
#To make sure it updates when no boxes are ticked
#contact.attributes = {'sector_ids' => []}.merge(params[:contact] || {})
end
def contact_params
params.require(:contact).permit(:firstname, :lastname, sector_ids: [])
end
Recommended reading:
http://millarian.com/rails/quick-tip-has_many-through-checkboxes/
Rails 4 Form: has_many through: checkboxes
I am trying to sort comments into events using a has_many :through association using checkboxes however the selected events are not saved. Here are my models:
class Comment < ActiveRecord::Base
has_many :categorizations
has_many :events, :through => :categorizations
end
class Event < ActiveRecord::Base
has_many :categorizations
has_many :comments, :through => :categorizations
end
class Categorization < ActiveRecord::Base
belongs_to :comment
belongs_to :event
end
My comments form looks like this:
<%= simple_form_for [#post, #comment] do |f| %>
<%= f.input :title %>
<%= f.association :events, :as => :check_boxes %>
<%= f.submit "Save" %>
After reading this answer, I added this to my event and comment controllers with no luck:
def comment_params
params.require(:comment).permit(:post_id, :title, :categorization_ids => [])
end
Try:
def comment_params
params.require(:comment).permit(:post_id, :title, :event_ids => [])
end
It's hard to know what's going on though without recreating it or seeing server logs, Hopefully this will solve it.
If I used this code to set up a tagging system for my app, how would I then render the posts with the tags I assigned to them. For instance if I assigned a sports tag to a couple posts, how would I then render all posts with the sports tag assigned to it
rails g model tag name:string
rails g model tagging article_id:integer tag_id:integer
rake db:migrate
class Tagging < ActiveRecord::Base
belongs_to :article
belongs_to :tag
end
class Tag < ActiveRecord::Base
has_many :taggings, :dependent => :destroy
has_many :articles, :through => :taggings
end
class Article < ActiveRecord::Base
has_many :comments, :dependent => :destroy
has_many :taggings, :dependent => :destroy
has_many :tags, :through => :taggings
validates_presence_of :name, :content
attr_writer :tag_names
after_save :assign_tags
def tag_names
#tag_names || tags.map(&:name).join(' ')
end
private
def assign_tags
if #tag_names
self.tags = #tag_names.split(/\s+/).map do |name|
Tag.find_or_create_by_name(name)
end
end
end
end
<p>
<%= f.label :tag_names %><br />
<%= f.text_field :tag_names %>
</p>
Maybe something like this:
# TagsController
def show
#articles = Tag.find_by_name('sports').articles
end
# tags/show.html.haml
- #articles.each do |article|
= render :partial => :article
Item gets the collection_fields from his collections.
For each collection_field of the collection item may have a field_value
models
class Item < ActiveRecord::Base
belongs_to :collection
has_many :field_values, :dependent => :destroy
has_many :collection_fields, :through => :collection
accepts_nested_attributes_for :field_values, :allow_destroy => true
end
class Collection < ActiveRecord::Base
has_many :items, :dependent => :destroy
has_many :collection_fields, :dependent => :destroy
end
class CollectionField < ActiveRecord::Base
belongs_to :collection
belongs_to :field
has_many :items, :through => :collection
has_many :field_values, :dependent => :destroy
end
class Field < ActiveRecord::Base
has_many :collection_fields
end
class FieldValue < ActiveRecord::Base
belongs_to :item
belongs_to :collection_field
end
controller
def new
#item = Item.new
#item.collection = Collection.find(params[:collection])
#item.collection.collection_fields.each do |cf|
#item.collection_fields << cf
end
def edit
#item = Item.find(params[:id])
view
<%= form_for(#item, :html => { :multipart => true }) do |f| %>
<% #item.collection_fields.each do |cf| %>
<% f.label cf.field.name %>
<%= f.fields_for :field_values, cf.field_values.find_or_create_by_item_id(#item.id) do |fv| %>
<%= fv.text_field :valore %>
This code is working fine with the edit method, but when I try to add a new item I get:
Couldn't find FieldValue with ID=213 for Item with ID=
How should I implement these form fields correctly?
I've finally worked out a solution. It's not so elegant, but it works
- #collection.collection_fields.each do |cf|
= f.label cf.field.name
- if #item.new_record?
= f.fields_for :field_values, #item.field_values.build() do |field_value|
= field_value.text_field :valore
= field_value.hidden_field :collection_field_id, :value => cf.id
- else
= f.fields_for :field_values, #item.field_values.find_or_create_by_collection_field_id(cf.id) do |field_value|
%td= field_value.text_field :valore
models/user.rb
class User < ActiveRecord::Base
has_many :clubs, :dependent => :destroy
has_many :announcements, :dependent => :destroy
end
models/announcement.rb
class Announcement < ActiveRecord::Base
belongs_to :user
belongs_to :club
end
models/club.rb
class Club < ActiveRecord::Base
belongs_to :user
has_many :announcements, :dependent => :destroy
end
controllers/announcements/announcements_controller.rb
def index
#announcements = Announcement.find(:all, :include => [:user, :club])
end
Problem:
When i type this code,
views/announcements/index.html.erb
<% #announcements.each do |announcement| %>
<%= announcement.user.username %>
<% end %>
I get this error:
NoMethodError in Announcements#index
undefined method `username' for nil:NilClass
when i change code to this, it works.
<% #announcements.each do |announcement| %>
<%= announcement.club.user.username %>
<% end %>
Why is the first code is not working? What is the difference between these codes. Thanks.
It looks like you've got an announcement which has no user set, but does have a club which in turn does have a user. Perhaps consider adding a validation to require that the :user_id column on announcement is present.