I get this uninitialized constant error when i submit my nested forms.
order.rb
class Order < ActiveRecord::Base
has_many :items, :dependent => :destroy
has_many :types, :through => :items
accepts_nested_attributes_for :items
accepts_nested_attributes_for :types
validates_associated :items
validates_associated :types
end
item.rb
class Item < ActiveRecord::Base
has_one :types
belongs_to :order
accepts_nested_attributes_for :types
validates_associated :types
end
type.rb
class Type < ActiveRecord::Base
belongs_to :items
belongs_to :orders
end
new.erb.html
<% form_for #order do |f| %>
<%= f.error_messages %>
<% f.fields_for :items do |builder| %>
<table border="0">
<th>Type</th>
<th>Amount</th>
<th>Text</th>
<th>Price</th>
<tr>
<% f.fields_for :type do |m| %>
<td> <%= m.collection_select :type, Type.find(:all, :order => "created_at DESC"), :id, :name, {:prompt => "Select a Type" }, {:id => "selector", :onchange => "type_change(this)"} %> </td>
<% end %>
<td> <%= f.text_field :amount, :id => "amountField", :onchange => "change_total_price()" %> </td>
<td> <%= f.text_field :text, :id => "textField" %> </td>
<td> <%= f.text_field :price, :class => "priceField", :onChange => "change_total_price()" %> </td>
<td> <%= link_to_remove_fields "Remove Item", f %> </td>
</tr>
</table>
<% end %>
<p><%= link_to_add_fields "Add Item", f, :items %></p>
<p>
<%= f.label :total_price %><br />
<%= f.text_field :total_price, :class => "priceField", :id => "totalPrice" %>
</p>
<p><%= f.submit "Create"%></p>
<% end %>
<%= link_to 'Back', orders_path %>
create method in orders_controller.rb
def create
#order = Order.new(params[:order])
respond_to do |format|
if #order.save
flash[:notice] = 'Post was successfully created.'
format.html { redirect_to(#order) }
format.xml { render :xml => #order, :status => :created,
:location => #order }
else
format.html { render :action => "new" }
format.xml { render :xml => #order.errors,
:status => :unprocessable_entity }
end
end
end
Hopefully you can see what i cant
You need to pay careful attention to pluralization in Rails. In this case you're setting up a singular relationship to a plural thing, so it's presumed that you're actually calling a class named "Types" and not "Type".
has_one, belongs_to are singular
has_many is plural
Possible fixes:
class Item < ActiveRecord::Base
has_one :type
belongs_to :order
accepts_nested_attributes_for :type
validates_associated :type
end
class Type < ActiveRecord::Base
belongs_to :item
belongs_to :order
end
In rails, type is a reserved word. You have to rename your model to something else. Also you have to follow tadman's instruction about singular names for has_one association.
Reference
Reserved words in rails
Related
I'm trying nested objects in a Rails4-app according to this Railscast. The model survey has_many :questions, the model questions in turn has_many :answers, the model answers belongs_to :questions and questions belongs_to :survey.
Now, the models were at first completely separated from one another which worked fine although I did not want it that way. I preferred them to be nested into one another so that I could assign and display the different objects at the same time.
I then had to figure out how to white-list strong parameters of these nested objects/attributes and there were good questions here that helped me with that.
When I create my database entries everything works fine. The problem comes when I want to edit the object in the database. In the log I get "Unpermitted parameter: answers" even though I've whitelisted every attribute including the ones for answers. I simply don't understand why.
Anybody who can point me in the right direction?
My surveys_controller:
class SurveysController < ApplicationController
before_action :set_survey, only: [:show, :edit, :update, :destroy]
def index
#surveys = Survey.all
end
def show
#survey = Survey.find(params[:id])
end
def new
#survey = Survey.new
3.times do
question = #survey.questions.build
4.times { question.answers.build }
end
end
def create
#survey = Survey.new(survey_params)
if #survey.save
flash[:notice] = 'Survey was successfully created.'
redirect_to(:action => 'index')
else
render('new')
end
end
def edit
#survey = Survey.find(params[:id])
end
def update
#Find an existing object using form parameters
#survey = Survey.find(params[:id])
#Update the object
if #survey.update_attributes(survey_params)
flash[:notice] = "Survey updated successfully."
#If update succeeds, redirect to 'show' action.
redirect_to(:action => 'show', :id => #survey.id)
else
#Else redisplay the 'edit' form.
render('edit')
end
end
def delete
#survey = Survey.find(params[:id])
end
def destroy
#survey = Survey.find(params[:id]).destroy
flash[:notice] = "Survey destroyed successfully."
redirect_to(:action => 'index')
end
private
def set_survey
#survey = Survey.find(params[:id])
end
def survey_params
params.require(:survey).permit(:name, questions_attributes: [:survey_id, :id, :content, answers_attributes: [:id, :question_id, :correct_answer, :content]])
end
end
My survey.rb model:
class Survey < ActiveRecord::Base
has_many :questions, :dependent => :destroy
accepts_nested_attributes_for :questions, :reject_if => lambda { |a| a[:content].blank? }
scope :sorted, lambda { order("questions.created_at DESC")}
end
EDIT: My question.rb-model:
class Question < ActiveRecord::Base
belongs_to :survey
has_many :answers, :dependent => :destroy
accepts_nested_attributes_for :answers, :reject_if => lambda { |a| a[:content].blank? }
scope :sorted, lambda { order("questions.created_at DESC")}
end
my answer.rb-model
class Answer < ActiveRecord::Base
belongs_to :question
end
my /surveys/show.html.erb
<td><%= link_to('<< Back to list', {:action => 'index'}, :class => 'action_index') %></td>
<div class="survey show">
<h2><strong>Show survey:</strong></h2>
<table summary="Survey detail view">
<tr>
<th>Survey name: </th>
<td><%= h #survey.name %></td>
</tr>
<tr>
<th>Question: </th>
<td><% for question in #survey.questions do %>
<li><%= h question.content %></li>
<ul>
<% for answer in question.answers do %>
<li><%= h answer.content %></li>
<% end %>
</ul>
<% end %>
</td>
</tr>
<tr>
<th>Created_at: </th>
<td><%= #survey.created_at %></td>
</tr>
<tr>
<td><%= link_to('Edit', {:action => 'edit', :id => #survey.id }, :class => 'action_edit') %></td>
<td><%= link_to('Delete', {:action => 'destroy', :id => #survey.id }, :class => 'action_edit') %></td>
</tr>
</table>
</div>
My _form_for.html.erb
<%= form_for #survey do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<%= f.fields_for :questions do |ff| %>
<%= render 'question_fields', :f => ff %>
<% end %>
<%= f.fields_for :answers do |fff| %>
<%= render 'answer_fields', :f => fff %>
<% end %>
<p><%= f.submit "Submit" %></p>
<% end %>
My _question_field.html.erb
<p>
<%= f.label :content, "Question" %><br />
<%= f.text_area :content, :rows => 3 %><br />
</p>
My _answer_field.html.erb
<p>
<%= f.label :content, "Answer" %>
<%= f.text_field :content %>
<%= f.radio_button :correct_answer, true %>
</p>
You have posted your Survey model twice instead of your Question model. Does your question model accept_nested_attributes_for :answers?
Assuming that you have and that I understand your structure correctly, your problem is most likely in your form - instead of f.fields_for :answers, you should have ff.fields_for :answers, nested within f.fields_for :questions, as this is a nested resource of Question and not Survey. So:
<%= f.fields_for :questions do |ff| %>
<%= render 'question_fields', :f => ff %>
<%= ff.fields_for :answers do |fff| %>
<%= render 'answer_fields', :f => fff %>
<% end %>
<% end %>
I'm getting the following error when I attempt to submit my nested form.
Cannot modify association 'Appointment#addresses' because the source reflection class 'Address' is associated to 'User' via :has_many
I'm not entirely sure which part of my setup is wrong. To briefly explain, I have Users that have multiple Appointments and multiple Addresses. Each Appointment can happen at a different Address, which is why I'm doing a :has_many association through user (which is correct, right?). Why am I getting this error?
Here are my models:
class User < ActiveRecord::Base
has_many :addresses, dependent: :destroy
has_many :appointments, dependent: :destroy
end
class Appointment < ActiveRecord::Base
belongs_to :user
has_many :addresses, :through => :user
accepts_nested_attributes_for :addresses
end
class Address < ActiveRecord::Base
belongs_to :user
end
And this is the create method from my Appointments controller:
class AppointmentsController < ApplicationController
...
def create
#appointment = current_user.appointments.build(appointment_params)
#address = #appointment.addresses.build(appointment_params[:addresses_attributes]["0"])
respond_to do |format|
if #appointment.save
format.html { redirect_to current_user, notice: 'Appointment was successfully created.' }
format.json { render :show, status: :created, location: current_user }
else
format.html { render :new }
format.json { render json: #appointment.errors, status: :unprocessable_entity }
end
end
end
...
private
def appointment_params
params.require(:appointment).permit(:appointment_date, :appointment_start_time, :appointment_end_time, :comments, :phone_number, addresses_attributes: [:user_id, :street_address, :street_address_optional, :city, :state, :zip_code, :primary])
end
end
And finally, this is my form in my view:
<%= form_for(#appointment, :url => {:controller => "appointments", :action => "create"}, :html => {"data-abide" => ""}) do |f| %>
<label>
Appointment Date
</label>
<%= f.date_select :appointment_date %>
<label>
Appointment Timeframe Start
</label>
<%= f.time_select :appointment_start_time %>
<label>
Appointment Timeframe End
</label>
<%= f.time_select :appointment_end_time %>
<%= f.fields_for :addresses do |builder| %>
<%= builder.hidden_field :user_id, :value => current_user.id %>
<label>
Street Address
<%= builder.text_field :street_address %>
</label>
<label>
Street Address (Optional)
<%= builder.text_field :street_address_optional %>
</label>
<label>
City
<%= builder.text_field :city %>
</label>
<label>
State
<%= builder.text_field :state %>
</label>
<label>
Zip Code
<%= builder.number_field :zip_code %>
</label>
<%= builder.check_box :primary %><%= builder.label :primary %>
<% end %>
<label>
Special Instructions
<%= f.text_area :comments %>
</label>
<%= f.submit "Sign Up", :class => "button expand"%>
<% end %>
Thanks in advance for the help :)
A user can have many appointments, but each one is in one address. (unless he can multilocate himself).
So you should do:
class User
has_many :appointments
class Appointment
has_one :address
class Address
belongs_to :appointments
If you want to retrieve the addresses in which the user has appointments you have to do:
#addresses = current_user.appointments.map {|app| app.address}
I have more curious questions for all you amazing people!
I am creating a forum and when you create a topic, you are also creating the first post at the same time.
I need to assign variables to certain fields.
Example: :user_id => current_user.id,
I don't have the param settings correct, so many of the fields are NULL when stored in the database.
Models
class Topic < ActiveRecord::Base
belongs_to :forum
has_many :posts, :dependent => :destroy
belongs_to :user
accepts_nested_attributes_for :posts
end
class Post < ActiveRecord::Base
belongs_to :topic
belongs_to :user
end
Topics Controller
# GET /topics/new
def new
#topic = Topic.new
#topic.posts.build
end
def create
#topic = Topic.new(topic_params)
if #topic.save
##topic.responses = Post.new(params[:responses])
flash[:success] = "Topic Posted"
redirect_to "/forums/#{#topic.forum_id}"
else
render :new
end
end
def topic_params
# last_post_at = (:last_post_at => Time.now)
params.require(:topic).permit(
:name,
:description,
[:last_poster_id => current_user.id],
[:last_post_at => Time.now],
[:user_id => current_user.id],
:forum_id,
posts_attributes: [:id, :content, :topic_id, :user_id => current.user.id] )
end
Post Controller
# GET /posts/new
def new
#post = Post.new
end
def create
#post = Post.new(
:content => params[:post][:content],
:topic_id => params[:post][:topic_id],
:user_id => current_user.id)
if #post.save
#topic = Topic.find(#post.topic_id)
#topic.update_attributes(
:last_poster_id => current_user.id,
:last_post_at => Time.now)
flash[:notice] = "Successfully created post."
redirect_to "/topics/#{#post.topic_id}"
else
render :action => 'new'
end
end
_form for View/Topic
<%= form_for(#topic) do |f| %>
<% if params[:forum] %>
<input type="hidden"
id="topic_forum_id"
name="topic[forum_id]"
value="<%= params[:forum] %>" />
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_field :description %>
</div>
<%= f.fields_for :posts do |p| %>
<%= p.label :content %><br />
<%= p.text_area :content %>
<% end %>
<%= f.submit :class => "btn btn-primary" %>
<% end %>
You'll likely be looking for a function called:
accepts_nested_attributes_for
You put this into the model you're working with (in your case Post) and it will pass paeans for the nested model through to the corresponding controller
There is a good RailsCast about this and I've gr some experience with it too. If you want me to post working live code, let me know (I'm on my iPhone)
Live Code
Models
#app/models/image_post.rb
belongs_to :post, :class_name => 'Post'
belongs_to :image, :class_name => 'Image'
accepts_nested_attributes_for :image, :allow_destroy => true
#app/models/post.rb
has_many :images, -> { uniq }, :class_name => 'Image', :through => :images_posts, dependent: :destroy
has_many :images_posts, :class_name => 'ImagePost'
accepts_nested_attributes_for :images_posts, :allow_destroy => true
Controller
def new
#post = Post.new
#post.images_posts.build.build_image
end
def create
#Using Inherited Resources Gem
create!
end
private
def permitted_params
{:post => params.require(:post).permit(:title, :body, images_posts_attributes: [:caption, image_attributes: [:image]] )}
end
Form
<%= form_for [:admin, resource], :html => { :multipart => true } do |f| %>
<table class="resource_table">
<thead>
<th colspan="2"><%= params[:action].capitalize %> <%= resource_class %></th>
</thead>
<tbody class="form">
<% attributes.each do |attr| %>
<tr class="<%= cycle('odd', '')%>">
<td><%= resource_class.human_attribute_name(attr) %></td>
<td>
<% if attr == "body" %>
<%= f.text_area attr, :rows => 60, :cols => 80, :class => "redactor" %>
<% else %>
<%= f.text_field attr, :value => resource.public_send(attr).to_s %>
<% end %>
</td>
</tr>
<% end %>
<%= f.fields_for :images_posts do |images_posts| %>
<%= images_posts.fields_for :image do |images| %>
<tr>
<td>Image</td>
<td><%= images.file_field :image %></td>
</tr>
<% end %>
<tr>
<td>Caption</td>
<td><%= images_posts.text_field :caption %></td>
</tr>
<% end %>
<tr class="dull">
<td colspan="2"><%= f.submit "Go" %></td>
</tr>
</tbody>
</table>
<% end %>
Use accepts_nested_attributes
class topic
accepts_nested_attributes :posts
end
class post
accepts_nested_attributes :topic
end
Then in form you can use fields_for posts while creating topic form
Also in post form fileds_for for topic
You may want to fetch the post params as
params[:topic].fetch(:post_attributes, nil)
Rails 4 has been sanitized the mass-assignment to be called as strong_params
Example
How can I fill in the hidden fields in app/views/rounds/shot_fields.html.erb?
app/models/player.rb
class Player < ActiveRecord::Base
has_many :shots, :dependent => :destroy
belongs_to :team
belongs_to :user
has_many :games
has_and_belongs_to_many :home_games, :class_name => "Game"
has_and_belongs_to_many :away_games, :class_name => "Game"
end
app/models/round.rb
class Round < ActiveRecord::Base
belongs_to :game, :counter_cache => true
has_many :shots, :dependent => :destroy
accepts_nested_attributes_for :shots, :allow_destroy => true
validates_presence_of :number
validates_numericality_of :number
end
app/models/shot.rb
class Shot < ActiveRecord::Base
belongs_to :player, :counter_cache => true
belongs_to :game
belongs_to :round
belongs_to :team
end
app/models/game.rb
class Game < ActiveRecord::Base
has_many :shots, :dependent => :destroy
has_many :rounds, :order => 'number', :dependent => :destroy
accepts_nested_attributes_for :shots
belongs_to :away, :class_name => 'Team'
belongs_to :home, :class_name => 'Team'
has_and_belongs_to_many :home_players, :class_name => 'Player', :association_foreign_key => "home_player_id"
has_and_belongs_to_many :away_players, :class_name => 'Player', :association_foreign_key => "away_player_id"
accepts_nested_attributes_for :rounds, :allow_destroy => true
end
app/controllers/rounds_controller.rb
def new
#game = Game.find(params[:game_id])
#round = #game.rounds.build
#round.number = #game.rounds.count > 1 ? #game.rounds.count + 1 : 1
end
app/views/rounds/_form.html.erb
<% if #round.errors.any? %>
<div class="error">
<% #round.errors.full_messages.each do |msg| %>
<%= msg %><br/>
<% end %>
</div>
<% end %>
<%= form_for #game do |f| %>
<%= field_set_tag "Rounds" do %>
<table class="sortable">
<thead>
<tr>
<th>Number</th>
<th><%= #game.away_players[0].name %></th>
<th><%= #game.away_players[1].name %></th>
<th><%= #game.away_players[2].name %></th>
<th><%= #game.home_players[0].name %></th>
<th><%= #game.home_players[1].name %></th>
<th><%= #game.home_players[2].name %></th>
<th>Remove</th>
</tr>
</thead>
<tbody>
<%= f.fields_for :rounds do |round_form| %>
<%= render 'round_fields', :f => round_form %>
<% end -%>
</tbody>
</table>
<p>
<%= link_to_add_fields "Add Round", f, :rounds %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
<% end %>
app/views/rounds/round_fields.html.erb
<% 6.times { f.object.shots.build } if f.object.new_record? -%>
<tr>
<td>
<%= f.text_field :number, :size => 3 %>
</td>
<%= f.fields_for :shots do |shot_form| %>
<%= render 'shot_fields', :f => shot_form %>
<% end -%>
<td>
<%= f.check_box(:_destroy) %>
<%= f.hidden_field :id %>
</td>
</tr>
app/views/rounds/shot_fields.html.erb
<td>
<%= f.select :cup, [["Miss", 0], 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ["No Shot", ""], ["Suicide", 11]] %>
<%= f.hidden_field :id %>
<%= f.hidden_field :game_id, :value => params[:id] %>
<%# f.hidden_field :player_id, :value => player.id %>
<%# f.hidden_field :team_id, :value => team.id %>
</td>
Passing them in via locals should work.
<%= render 'shot_fields', :locals => { :f => shot_form, :player => some_player_you_dont_have_defined, :team => some_team_variable_i_dont_see } %>
I don't quite understand what you're trying to do (lots of code, not a lot of context), but this is how you pass information into partials.
Artists have many Events. Events have many Artists. The join between these two models is called Performances.
I'm trying to associate Artists with Events on the Event add/edit page. I would like to be able to add an Artist only if it doesn't exist, and create the join (performance) regardless. An Artist should be associated with an Event only once.
It was suggested that I use find_or_create_by_name instead of accepts_nested_attributes_for.
I'm following the Railscasts #102 instructions for Auto-Complete which say to use virtual attributes. I haven't even gotten to the auto-complete part, just trying to get find_or_create_by_name working.
I'm getting "undefined method `artist_name' for #" on the Event edit and new pages. In the Railscast, Ryan gets an undefined method before he adds the methods to the model. But I have the method in the Model.
No idea what to do.
event.rb
validates_presence_of :name, :location
validates_uniqueness_of :name
validates_associated :performances
has_many :performances, :dependent => :delete_all
has_many :artists, :through => :performances
#accepts_nested_attributes_for :artists, :reject_if => proc {|a| a['name'].blank?}, :allow_destroy => true
def artist_name
artist.name if artist
end
def artist_name=(name)
self.artist = Artist.find_by_name(name) unless name.blank?
end
artist.rb
validates_presence_of :name
has_many :mixes
has_many :performances, :dependent => :delete_all
has_many :events, :through => :performances
perfomance.rb
belongs_to :artist
belongs_to :event
events_controller.rb
def create
#event = Event.new(params[:event])
respond_to do |format|
if #event.save
flash[:notice] = 'Event was successfully created.'
format.html { redirect_to(admin_events_url) }
format.xml { render :xml => #event, :status => :created, :location => #event }
else
format.html { render :action => "new" }
format.xml { render :xml => #event.errors, :status => :unprocessable_entity }
end
end
end
_form.html.erb
<% form_for([:admin,#event]) do |f| %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<p>
<%= f.label :location %><br/>
<%= f.text_field :location %>
</p>
<p>
<%= f.label :date %><br />
<%= f.date_select :date %>
</p>
<p>
<%= f.label :description %><br />
<%= f.text_area :description %>
</p>
<% f.fields_for :artists do |builder| %>
<%= render 'artist_fields', :f => builder %>
<% end %>
<p><%= link_to_add_fields "Add Artist", f, :artists %></p>
<p>
<%= f.submit 'Submit' %> <%= link_to 'Cancel', admin_events_path %>
</p>
<% end %>
_artist_fields.html.erb
<p class="fields">
<%= f.label :artist_name, "Artist"%><br/>
<%= f.text_field :artist_name %>
<%= link_to_remove_fields "remove", f %>
</p>
Personally I would go back to accepts_nested_attributes_for, ryan bates method there was in the days before nested attributes.
In your controller do something like:
def new
#event = Event.find params[:id]
#artist = #event.artists.build
def edit
#event = Event.find params[:event_id]
#artist = #event.artists.find params[:user_id]
While in the view
...
<% f.fields_for :artists, #artist do |builder| %>
...