Rails 5 Nested form, parent doesn't saved - ruby-on-rails

I have hard time with this one. I have an album Model and a Track Model, Track belongs to album and album has many tracks. When I try to create an album with a track(the nested form below) it fails to save and renser the 'new' form with this message:
1 error prohibited this album from being saved: Tracks album must
exist
Albums Controller
class Admin::AlbumsController < AdminController
def new
#album = Album.new
#album.tracks.build
end
def create
#album = Album.new(album_params)
if #album.save
redirect_to admin_album_path(#album)
else
render 'new'
end
end
private
def album_params
params.require(:album).permit(:title, :kind, :release, tracks_attributes: [:id, :title, :time, :order])
end
end
Album Model
class Album < ApplicationRecord
has_many :tracks
accepts_nested_attributes_for :tracks
end
Track Model
class Track < ApplicationRecord
belongs_to :album
end
Form
<%= form_for [:admin, #album] do |f| %>
<% if #album.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#album.errors.count, "error") %> prohibited this album from being saved:
</h2>
<ul>
<% #album.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<h5>Album</h5>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :kind %><br>
<%= f.text_field :kind %>
</p>
<p>
<%= f.label :release %><br>
<%= f.text_field :release %>
</p>
<br><br><br>
<h5>Track</h5>
<%= f.fields_for :tracks do |tracks_form| %>
<p>
<%= tracks_form.label :title %>
<%= tracks_form.text_field :title %>
</p>
<p>
<%= tracks_form.label :time %>
<%= tracks_form.text_field :time %>
</p>
<p>
<%= tracks_form.label :order %>
<%= tracks_form.text_field :order %>
</p>
<% end %>
<%= f.submit class: "waves-effect waves-light btn" %>
<% end %>
I think album doesn’t saved so track can’t get the album id.
Could you help me to figure out what really happens?

When Rails attempts to save the track, the album has not yet been committed into the database. In order for this to to work you need to have the
:inverse_of
Try this
class Album < ApplicationRecord
has_many :tracks, inverse_of: :album
accepts_nested_attributes_for :tracks
end
class Track < ApplicationRecord
belongs_to :album, inverse_of: :tracks
validates_presence_of :album
end

Related

Rails 5 - has_many through: and nested fields_for in forms

I am new to RoR (using rails 5) and have problems with a has_many through: association.
I want to create Categories with different labels for different Languages.
Here is my model:
class Language < ApplicationRecord
has_many :category_infos
has_many :categories, through: :category_infos
end
class Category < ApplicationRecord
has_many :category_infos
has_many :languages, through: :category_infos
accepts_nested_attributes_for :category_infos
end
class CategoryInfo < ApplicationRecord
belongs_to :language
belongs_to :category
accepts_nested_attributes_for :language
end
The controller:
class CategoriesController < ApplicationController
def new
#category = Category.new
#languages = Language.all
#languages.each do |language|
#category.category_infos.new(language:language)
end
end
def create
#category = Category.new(category_params)
if #category.save
redirect_to #category
else
render 'new'
end
end
private
def category_params
params.require(:category).permit(:name, category_infos_attributes:[:label, language_attributes: [:id, :language]])
end
end
The form:
<%= form_with model: #category, local: true do |form| %>
<% if #category.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#category.errors.count, "error") %>:
</h2>
<ul>
<% #category.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<br/>
<% end %>
<p>
<%= form.label :name %>
<%= form.text_field :name %>
</p>
<p>
Labels:
</p>
<table>
<% #category.category_infos.each do |category_info| %>
<tr>
<td>
<%= category_info.language.name %>
</td>
<td>
<%= form.fields_for :category_infos, category_info do |category_info_form| %>
<%= category_info_form.fields_for :language, category_info.language do |language_form| %>
<%= language_form.hidden_field :id, value: category_info.language.id %>
<%= language_form.hidden_field :name, value: category_info.language.name %>
<% end %>
<%= category_info_form.text_field :label %>
<% end %>
</td>
</tr>
<% end %>
</table>
<p>
<%= form.submit %>
</p>
<% end %>
When I create a new category, I get this error :
Couldn't find Language with ID=1 for CategoryInfo with ID=
on Line :
#category = Category.new(category_params)
However I already have registered several languages in database (1 = English, 2 = French etc...)
How do I need to write the form so that I can create a Category and its CategoryInfos in English, French etc... at the same time?
Thanks in advance for your answers
You're making a classic newbie misstake and using fields_for when you just want to create an association by passing an id.
<%= form_with model: #category, local: true do |f| %>
# ...
<%= f.fields_for :category_infos do |cif| %>
<%= cif.collection_select(:language_id, Language.all, :name, :id) %>
<%= cif.text_field :label %>
<% end %>
<% end %>
While you could also pass the attributes to let a users create languages at the same time its very much an anti-pattern as it adds a crazy amount of responsibilities to a single controller. It will also create an authorization problem if the user is allowed to create categories but not languages.
Use ajax to send requests to a seperate languages controller instead if you need the feature.

Rails 4 fields_for not displaying field

I have two models that I would like to create with one form. I tried following this railscasts tutorial, but I just can't get the nested fields to display on the form. How can I make these nested fields appear?
Models
class Poll < ActiveRecord::Base
has_many :poll_answers, :dependent => :destroy
accepts_nested_attributes_for :poll_answers, allow_destroy: true
end
class PollAnswer < ActiveRecord::Base
belongs_to :poll
end
Controller
class PollsController < ApplicationController
def new
#poll = Poll.new
2.times { #poll.poll_answers.build }
end
private
def poll_params
params.require(:poll).permit([
:question,
poll_answers_attributes: [:answer]
])
end
end
View
<%= form_for(#poll) do |f| %>
<div class="field">
<%= f.label :question %><br>
<%= f.text_field :question %>
</div>
<% f.fields_for :poll_answers do |pa| %>
<p>Hello
<%= pa.text_field :answer %>
</p>
<% end %>
<%= debug #poll.poll_answers %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
You missed an =
<%= f.fields_for :poll_answers do |pa| %>
<p>Hello
<%= pa.text_field :answer %>
</p>
<% end %>

rails 4 nested form fields_for are not displayed

I just started learning Rails 4.2. The problem is that one field in the form is not being displayed.
I have restaurant, category and a dish. While creating a dish, the category and restaurant will also be inputted via /dishes/new.
Expected behaviour: Dish, Category and Restaurant fields are displayed.
Actual behaviour: Only Dish and Category fields are displayed.
Here are my models
models/restaurant.rb
class Restaurant < ActiveRecord::Base
has_many :categories
has_many :dishes, :through => :categories
end
models/category.rb
class Category < ActiveRecord::Base
belongs_to :restaurant
has_many :dishes
end
models/dish.rb
class Dish < ActiveRecord::Base
belongs_to :category
validates :name, :price, :category, :restaurant, :presence => true
accepts_nested_attributes_for :restaurant, :category
end
dish controller
def new
# I think this is where
# I am making a mistake
#dish = Dish.new
category = #dish.build_category
restaurant = category.build_restaurant
end
def create
#dish = Dish.new(dish_params)
respond_to do |format|
if #dish.save
.... # default stuff #
end
end
end
# strong params
def dish_params
params.require(:dish).permit(:name, :description, :price, restaurant_attributes: [:name], category_attributes: [:name])
end
Dishes views/dishes/_form.html.erb
<%= form_for(#dish) do |f| %>
<% if #dish.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#dish.errors.count, "error") %> prohibited this dish from being saved:</h2>
<ul>
<% #dish.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :nameWoW %><br>
<%= f.text_area :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="field">
<%= f.label :price %><br>
<%= f.number_field :price %>
</div>
*** The restaurant name field is not being displayed **
<%= f.fields_for :restaurant do |restaurant| %>
<div class="field">
<%= restaurant.label :Restname %><br>
<%= restaurant.text_area :name %>
</div>
<% end %>
<%= f.fields_for :category do |category| %>
<div class="field">
<%= category.label :Catname %><br>
<%= category.text_area :name %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I have followed steps from rails guide, browsed questions on stackoverflow and read some blog posts as well but havent been able to figure out whats wrong. Some micro level mistake is blocking me :( . Anyone knows whats wrong ?
Thanks in advance.
UPDATE:
Hey I found a solution.
def new
#dish = Dish.new
#dish.build_category
#dish.category.build_restaurant
end
This works well.But this is just a part of the actual solution. I had to do lot of /dish/create controller modification as well. I think the entire solution will have to be put in blog post. Otherwise it wont make any sense. I will soon be posting and updating it here.
You can add this in your dish.rb
class Dish
delegate :restaurant, to: :category
end
Or you can do
<%= f.fields_for :restaurant, #dish.category.restaurant do |restaurant| %>
<div class="field">
<%= restaurant.label :Restname %><br>
<%= restaurant.text_area :name %>
</div>
<% end %>
I think you are missing:
class Dish
belongs_to :restaurant, through: :category
end
You have it on the other side (many) but not there. You could test this by trying to output #dish.restaurant on your form (should be empty but not nil).
def new
# I think this is where
# I am making a mistake
#dish = Dish.new
category = #dish.category.build
restaurant = category.restuarant.build
end

Couldn't find Team with ID=2 for Match with ID=

Trying to do a many to many relations ship.
I have two models, Teams and Matches and i'm doing a many-to-many relations ship but getting this error when trying to save a nested model.
'Couldn't find Team with ID=2 for Match with ID='
I know that my model is new so it doesn't have an id yet, but i'm using the Match.new(params[:match] method which should work.
Team:
class Team < ActiveRecord::Base
attr_accessible :description, :name, :status
attr_protected :id
has_many :matchships
has_many :matches, :through => :matchships
end
Match:
class Match < ActiveRecord::Base
attr_accessible :date, :name
attr_protected :id
has_many :matchships
has_many :teams , :through => :matchships
accepts_nested_attributes_for :teams
end
MatchShips:
class Matchship < ActiveRecord::Base
attr_accessible :match_id, :team_id
belongs_to :match
belongs_to :team
end
Match Controller:
New:
def new
#match = Match.new
#match.teams.build
end
Create:
def create
'failing here' --------> #match = Match.new(params[:match])
#team = Team.find(params[:team_id])
#match.teams << #team
#team.matches << #match
Form:
<%= nested_form_for(#match) do |f| %>
<% if #match.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#match.errors.count, "error") %> prohibited this match from being saved:</h2>
<ul>
<% #match.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :date %><br />
<%= f.date_select :date %>
</div>
<%= f.fields_for :teams, :html => { :class => 'form-vertical' } do |builder| %>
<%= builder.label "Team Name:" %>
<%= builder.autocomplete_field :name, autocomplete_team_name_teams_path, :update_elements => {:id => "##{form_tag_id(builder.object_name, :id)}" },:class => "input-small",:placeholder => "Search" %>
<%= builder.hidden_field :id %>
<% end %>
<%= f.link_to_add raw('<i class="icon-plus-sign"></i>'), :teams, :class => 'btn btn-small btn-primary' %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Controller

Mass Assignment in nested form

I am following railscasts 196 episode and i try exactly what he has, but i am having the following error
ActiveModel::MassAssignmentSecurity::Error in SurveysController#create
Can't mass-assign protected attributes: questions_attributes
Rails.root: /home/jean/rail/surveysays
Here my code so far
Survey Form
<%= form_for(#survey) do |f| %>
<% if #survey.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#survey.errors.count, "error") %> prohibited this survey from being saved:</h2>
<ul>
<% #survey.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<%= f.fields_for :questions do |bf|%>
<%= bf.label :content, "Question" %><br />
<%= bf.text_area :content, :rows=> 3 %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Survey Relationship
class Survey < ActiveRecord::Base
has_many :questions, :dependent => :destroy
accepts_nested_attributes_for :questions
attr_accessible :name
end
Question Relationship
class Question < ActiveRecord::Base
belongs_to :survey
attr_accessible :content, :survey_id
end
New Controller
def new
#survey = Survey.new
3.times {#survey.questions.build }
respond_to do |format|
format.html # new.html.erb
format.json { render json: #survey }
end
end
change your class to this
class Survey < ActiveRecord::Base
has_many :questions, :dependent => :destroy
accepts_nested_attributes_for :questions
attr_accessible :name, :questions_attributes
end
look for topic "Using with attr_accessible" here for more information

Resources