I'm new to ruby on rails and I'm not understanding what exactly I'm doing wrong. I have a form for submitting a question for a multiple choice trivia game the question is supposed to be saved to a question database table and the 4 answers saved to an answer table with the question_id as a foreign key. However whenever I click the submit button with valid data entered for all fields, I keep getting the error, "param is missing or the value is empty: answer" from this block of code in my questions controller:
def answer_params
params.require(:answer).permit({:question_id => [#question.id]},:answer_text, :correct)
end
Here is the full class for my questions_controller.rb:
class QuestionsController < ApplicationController
def new
#question = Question.new
#answer1 = Answer.new
#answer2 = Answer.new
#answer3 = Answer.new
#answer4 = Answer.new
end
def create
#question = Question.new(question_params)
if #question.save
#answer1 = Answer.new(answer_params)
#answer2 = Answer.new(answer_params)
#answer3 = Answer.new(answer_params)
#answer4 = Answer.new(answer_params)
if #answer1.save && #answer2.save && #answer3.save && #answer4.save
redirect_to root_path
end
else
render 'new'
end
end
private
def question_params
params.require(:question).permit(:username, :question)
end
def answer_params
params.require(:answer).permit({:question_id => [#question.id]}, :answer_text, :correct)
end
end
answers_controller.rb:
class AnswersController < ApplicationController
def new
#answer = Answer.new(answer_params)
end
def create
#question = Question.find(params[:id])
#answer = Answer.new(answer_params)
if #answer.save
redirect_to root_path
end
end
private
def answer_params
params.require(:answer).permit({:question_id => [#question.id]}, :answer_text, :correct)
end
end
new.html.erb:
<%= form_for(#question) do |f| %>
<%= f.hidden_field :username, :value => current_user.email %>
<br>
<%= f.label :category %>
<%= f.text_field :category %>
<br>
<%= f.label :question %>
<%= f.text_field :question %>
<br>
<%= f.fields_for(#answer1) do |c| %>
<%= c.label :correct_answer %>
<%= c.text_field :answer_text %>
<%= c.hidden_field :correct, :value => true %>
<br>
<% end %>
<%= f.fields_for(#answer2) do |i| %>
<%= i.label :incorrect_answer1 %>
<%= i.text_field :answer_text %>
<%= i.hidden_field :correct, :value => false %>
<br>
<% end %>
<%= f.fields_for(#answer3) do |j| %>
<%= j.label :incorrect_answer2 %>
<%= j.text_field :answer_text %>
<%= j.hidden_field :correct, :value => false %>
<br>
<% end %>
<%= f.fields_for(#answer4) do |k| %>
<%= k.label :incorrect_answer3 %>
<%= k.text_field :answer_text %>
<%= k.hidden_field :correct, :value => false %>
<% end %>
<br>
<%= f.submit "Submit my question", class: "btn btn-primary"%>
<% end %>
question.rb:
class Question < ActiveRecord::Base
belongs_to :category
has_many :answers
end
answer.rb:
class Answer < ActiveRecord::Base
belongs_to :question
end
Can anyone tell me where I'm going wrong?
Related
I have a 'post' controller in that I have two variable title and body which I am passing through strong parameters.But I need to use two other variable which are path and name which are in different model name 'Document'..And also I am saving the content in database ..but unable to do so..getting this error view [posts/_form.html.erb]
undefined method `name' for #
[posts_controller]
class PostsController < ApplicationController
before_action :authenticate_user!
def index
#posts = Post.user_post(current_user).order('created_at DESC').paginate(:page => params[:page], :per_page => 5)
end
def new
#post = Post.new
end
def show
#post = find_params
end
def create
#post = Post.create(post_params)
#post.user = current_user
if #post.save
redirect_to #post
else
render 'new'
end
end
def edit
#post = find_params
end
def update
#post = find_params
if #post.update(post_params)
redirect_to #post
else
render 'edit'
end
end
def destroy
#post = find_params
#post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:title, :body)
Document.new(params,:files=>[])
end
def find_params
Post.find(params[:id])
end
end
[post/_form.html.erb]
<%= form_for #post,html: { multipart: true } do |f| %>
<% if #post.errors.any? %>
<div id="errors">
<h2><%= pluralize(#post.errors.count, "error") %> prevented this post from saving:</h2>
<ul>
<% #post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.label :title %><br>
<%= f.text_field :title %><br>
<br>
<%= f.label :body %><br>
<%= f.text_field :body %><br>
<br>
<%= f.label :name %> <br>
<%= f.text_field :name %><br>
<br>
<br>
<%= f.label :path %><br>
<%= f.file_field :path %><br>
<%= f.submit %>
<% end %>
[document.rb]
class Document < ActiveRecord::Base
validates :name, presence: true
validates :path, presence: true
validates :resource_type, presence: true
validates :resource_id, presence: true
mount_uploader :path, PathUploader
validates :name, presence: true
# def self.abc
# params.permit(:name,:path)
# end
def initialize(params,file)
params=file[:name]
#params.permit(name =>:name,path =>:path)
end
end
undefined method `name' for #
You're referencing a non-existent attributes for your Post form:
<%= form_for #post,html: { multipart: true } do |f| %>
<%= f.label :title %><br>
<%= f.text_field :title %><br>
<br>
<%= f.label :body %><br>
<%= f.text_field :body %><br>
<%= f.submit %>
<% end %>
Remove :name & :path references.
--
If you want to pass "extra" attributes to another model, you need to use accepts_nested_attributes_for or set the params separately to your "primary" model:
#app/models/post.rb
class Post < ActiveRecord::Base
has_many :documents
accepts_nested_attributes_for :documents
end
#app/models/document.rb
class Document < ActiveRecord::Base
belongs_to :post
end
This will allow you to pass the documents as "nested" attributes of your Post model:
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
def new
#post = Post.new
#post.documents.build
end
def create
#post = Post.new post_params
#post.save
end
private
def post_params
params.require(:post).permit(:title, :body, documents_attributes: [:name, :path])
end
end
#app/views/posts/_form.html.erb
<%= form_for #post do |f| %>
<%= f.text_field :title %>
<%= f.text_area :body %>
<%= f.fields_for :documents do |d| %>
<%= d.text_field :name %>
<%= d.text_field :path %>
<% end %>
<%= f.submit %>
<% end %>
So undefined method on a model will indicate that, well, the method doesn't exist on the model. Want to see a model's methods? Post.methods. However, in this example, the column name is not defined on the model., and you're trying to tell Post that it has a name. What you need to do is nest your parameters.
While there is a ton of cleaning up that might want to focus on first, your answer is found in the accepts_nestable_attributes_for class methods, as shown here, http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html, and strong_params documentation as shown here, http://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html
In your case, you want to create a new document from a post. Your permitted params hash will look like this,
params.require(:post).permit(:title, :body, :document_attributes => [:name])
Ensure that document_attributes is singular; if a person has_many pets (for example), then you'd have pets_attributes.
In your form, something that often trips people up is the builder.
<%= form_for #post do |f| %>
<%= f.text_field :title %>
<%= f.text_field :body %>
<%= f.fields_for #post.document do |document_field| %>
<%= document_field.text_field :name %>
<% end %>
<%= f.submit %>
<% end %>
Make sure that you're telling ERB that <%= f.fields_for %>, not just <% f.fields_for %>.
I am trying to build a basic recipes app but am having trouble allowing the user to input multiple ingredients for one recipe. The array of permitted params for the ingredients ends up empty. So I guess my question is - how do I permit the array of ingredients?
My controller:
class RecipesController < ApplicationController
def new
#recipe = Recipe.new
#ingredient = Ingredient.new
end
def create
safe_params = params.require(:recipe).permit(:title, :instruction, :category_id)
ingredient_params = params.require(:recipe).permit(:ingredient => [])
#recipe = Recipe.new(safe_params)
#recipe.save
ingredient_params.each do |i|
#recipe.ingredients << Ingredient.find_or_create_by(name: i[:ingredient][:name])
end
render body: YAML::dump(ingredient_params)
#redirect_to index_path(id: #recipe.id)
end
end
Form:
<%= form_for(#recipe, :url => create_path) do |f| %>
<%= f.label :category %>
<%= f.select :category_id, options_for_select(Category.all.map{|c|[c.title, c.id]}) %>
<%= f.label :title %>
<%= f.text_field :title%>
<%= f.label :instruction %>
<%= f.text_area(:instruction, size: "50x10") %>
<%= f.fields_for "ingredients[]", #ingredient do |i| %>
<%= i.label :name %>
<%= i.text_field :name %>
<%= i.text_field :name %>
<%= i.text_field :name %>
<% end %>
<%= f.submit "Submit" %>
<% end %>
Models:
class Recipe < ActiveRecord::Base
has_and_belongs_to_many :ingredients
accepts_nested_attributes_for :ingredients
belongs_to :category
end
class Category < ActiveRecord::Base
has_many :recipes
end
class Ingredient < ActiveRecord::Base
has_and_belongs_to_many :recipes
end
There are several issues here, I'll just provide what I'd do:
#app/controllers/recipes_controller.rb
class RecipesController < ApplicationController
def new
#recipe = Recipe.new
#recipe.ingredients.new
end
def create
#recipe = Recipe.new safe_params
#recipe.save
end
private
def safe_params
params.require(:recipe).permit(:title, :instruction, :category_id, ingredients_attributes: [:name])
end
end
#app/views/recipes/new.html.erb
<%= form_for #recipe do |f| %>
<%= f.label :category %>
<%= f.collection_select :category_id, Category.all, :id, :name %>
<%= f.label :title %>
<%= f.text_field :title%>
<%= f.label :instruction %>
<%= f.text_area(:instruction, size: "50x10") %>
<%= f.fields_for :ingredients do |i| %>
<%= i.label :name %>
<%= i.text_field :name %>
<% end %>
<%= f.submit "Submit" %>
<% end %>
If you wanted to have multiple ingredients fields, you'll have to build multiple objects in the controller:
def new
#recipe = Recipe.new
3.times do
#recipe.ingedients.build
end
end
Everything else looks like it will work well.
--
As an extra, if you want to populate has_and_belongs_to_many relationships, you'll be able to just pass the [relationship]_ids parameter:
<%= form_for #recipe do |f| %>
<%= f.collection_select :ingredient_ids, Ingredient.all, :id, :name %>
<%= f.submit %>
<% end %>
This will only work for currently existing ingredients. If you want to create new ingredients, the above will work.
Need to make few changes to your code
class RecipesController < ApplicationController
def new
#recipe = Recipe.new
# here you can decide how many ingredients do you want. (Not in form looping through text fields)
3.times do
ingredient = #recipe.ingredients.build
end
end
so fields for ingredients were get generated for three times.
<%= f.fields_for :ingredients do |i| %>
<%= i.label :name %>
<%= i.text_field :name %>
<% end %>
Please go through following link, it will clear your idea about nested forms
http://railscasts.com/episodes/196-nested-model-form-part-1
I cannot pass the lecture_id to the created object:
Basically it should create the project with the lecture_id based on the dropdown menu. It does not seem to pass the data.
If for instance I add <%= f.input :lecture_id %> it will pass the data
<%= simple_form_for(#project) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :title %>
<%= select_tag(:lecture_id, options_for_select(#lecture_options)) %>
<%= f.input :company_name %>
<%= f.input :phone_number %>
<%= f.input :body %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
Controller:
def new
#lecture_options = Lecture.all.map{|u| [u.title, u.id]}
#project = Project.new
respond_to do |format|
format.html
format.js
end
authorize #project
end
def create
#project = current_user.projects.build(project_params)
if #project.save
flash[:success] = "You have successfully created a project."
redirect_to profile_path(current_user)
else
render action: 'new'
end
authorize #project
end
def project_params
params.require(:project).permit(:company_name, :phone_number, :body, :user_id, :title, :lecture_id)
end
Models:
Lecture.rb
has_many :projects
Project.rb
class Project < ActiveRecord::Base
belongs_to :user
belongs_to :lecture
end
You should change select_tag to :
<%= select_tag("project[lecture_id]", options_for_select(#lecture_options)) %>
I'm new to Rails and struggling to get my belongs_to association right. I have an app where a painting belongs to an artist and an artist can have_many paintings. I can create and edit my paintings, however I can not edit or create artists except through the console. Through much Googling I feel I have got myself turned around. Any help would be much appreciated!
Here's my routes.rb file:
MuseumApp::Application.routes.draw do
resources :paintings
resources :paintings do
resources :artists
resources :museums
end
root 'paintings#index'
end
Here's my paintings Controller
def show
#painting = Painting.find params[:id]
end
def new
#painting = Painting.new
##artist = Artist.new
end
def create
safe_painting_params = params.require(:painting).permit(:title, :image)
#painting = Painting.new safe_painting_params
if #painting.save
redirect_to #painting
else
render :new
end
end
def destroy
#painting = Painting.find(params[:id])
#painting.destroy
redirect_to action: :index
end
def edit
#painting = Painting.find(params[:id])
end
def update
#painting = Painting.find(params[:id])
if #painting.update_attributes(params[:painting].permit(:title, :image)) #safe_params
redirect_to #painting
else
render :edit
end
end
Here's the form in my paintings view:
<%= form_for(#painting) do |f| %>
<fieldset>
<legend>painting</legend>
<div>
<%= f.label :title %>
<%= f.text_field :title %>
</div>
<div>
<%= f.label :image %>
<%= f.text_field :image %>
</div>
<%= form_for([#painting,#painting.create_artist]) do |f| %>
<div>
<%= f.label :Artist %>
<%= f.text_field :name %>
</div>
</fieldset>
<%= f.submit %>
<% end %>
<% end %>
Artists Controller:
class ArtistsController < ApplicationController
def index
#artists = Artist.all
#artists = params[:q] ? Artist.search_for(params[:q]) : Artist.all
end
def show
#artist = Artist.find params[:id]
end
def new
#artist = Artist.new
end
def create
#painting = Painting.find(params[:painting_id])
#artist = #painting.create_artist(artist_params)
redirect_to painting_path(#painting)
end
def destroy
#artist = Artist.find(params[:id])
#Artist.destroy
redirect_to action: :index
end
def edit
#artist = Artist.find(params[:id])
end
def update
#painting = Painting.find(params[:painting_id])
#artist = #artist.update_attributes(artist_params)
redirect_to painting_path(#painting)
end
end
private
def artist_params
params.require(:artist).permit(:name)
end
Index view:
<h1> Hello and Welcome to Museum App</h1>
<h3><%= link_to "+ Add To Your Collection", new_painting_artist_path %></h3>
<%= form_tag '/', method: :get do %>
<%= search_field_tag :q, params[:q] %>
<%= submit_tag "Search" %>
<% end %>
<br>
<div id="paintings">
<ul>
<% #paintings.each do |painting| %>
<li><%= link_to painting.title, {action: :show, id:painting.id} %> by <%= painting.artist_name %></li>
<div id = "img">
<br><%= link_to (image_tag painting.image), painting.image %><br>
</div>
<%= link_to "Edit", edit_painting_path(id: painting.id) %>
||
<%= link_to 'Destroy', {action: :destroy, id: painting.id},method: :delete, data: {confirm: 'Are you sure?'} %>
<% end %>
</ul>
</div>
In your case you should use accepts_nested_attributes_for and fields_for to achieve this.
Artist
has_many :paintings, :dependent => :destroy
accepts_nested_attributes_for :paintings
Painting
belongs_to :artist
And also you should try creating artist with paintings like this
form_for(#artist) do |f| %>
<fieldset>
<legend>Artist</legend>
<%= f.label :Artist %>
<%= f.text_field :name %>
<%= fields_for :paintings, #artist.paintings do |artist_paintings| %>
<%= artist_paintings.label :title %>
<%= artist_paintings.text_field :title %>
<%= artist_paintings.label :image %>
<%= artsist_paintings.text_field :image %>
</fieldset>
<%= f.submit %>
<% end %>
Note:
You should be having your Artist Controller with at least new,create,edit and update methods defined in it to achieve this.
Edit
Try the reverse
Artist
has_many :paintings, :dependent => :destroy
Painting
belongs_to :artist
accepts_nested_attributes_for :paintings
form_for(#painting) do |f| %>
<fieldset>
<legend>Painting</legend>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.label :image %>
<%= f.text_field :image %>
<%= fields_for :artists, #painting.artists do |ff| %>
<%= ff.label :Artist %>
<%= ff.text_field :name %>
</fieldset>
<%= f.submit %>
<% end %>
Put this form in paintings views.
I am new to rails and am learning to complete an Inventory System database project for school.
Here is my Item model:
class Item < ActiveRecord::Base
self.primary_key ='item_id'
validates :item_id, :presence => true
has_one :vendor_item, :dependent => :destroy
has_one :vendor, :through => :vendor_item
accepts_nested_attributes_for :vendor_item
end
Here is my item controller:
class ItemsController < ApplicationController
def new
#item = Item.new
#all_vendors = Vendor.all
#item_vendor = #item.build_vendor_item
end
def create
#item = Item.new(item_params)
vendor = params[:vendors][:id]
#item_vendor = #item.build_vendor_item(:vendor_id => vendor)
#item_vendor.save
#raise params.inspect
if #item.save
redirect_to #item
else
render 'new'
end
end
def show
#item = Item.find(params[:id])
#item_vendor = #item.vendor_item
end
def index
#items = Item.all
end
def priceUnder500
#priceUnder500 = Item.where("price < ?", 500)
respond_to do |format|
format.html
format.js
end
end
def priceOver500
#priceOver500 = Item.where("price > ?", 500)
respond_to do |format|
format.html
format.js
end
end
def edit
#item = Item.find(params[:id])
#all_vendors = Vendor.all
#vendor_item = #item.vendor_item
end
def update
#item = Item.find(params[:id])
vendor = params[:vendors][:id]
if #item.vendor_item.blank?
#item.build_vendor_item(:vendor_id => vendor)
end
if #item.update(params[:item].permit(:item_id, :name, :category, :description, :reorder_level, :quantity, :price, :vendor_item_attributes => [:vendor_item_id]))
redirect_to items_path
else
render 'edit'
end
end
def destroy
#item = Item.find(params[:id])
#item.destroy
redirect_to items_path
end
private
def item_params
params.require(:item).permit(:item_id, :name, :category, :description, :reorder_level, :quantity, :price, :vendor_item_attributes => [:vendor_item_id])
end
end
And my _form partial for items:
<%= form_for #item do |f| %>
<% if #item.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#item.errors.count, "error") %> prohibited
this post from being saved:</h2>
<ul>
<% #item.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :item_id, 'Item Id' %><br>
<%= f.text_field :item_id %>
</p>
<%= fields_for #item_vendor do |vii| %>
<div class= "vendorItemId">
<%= vii.label :vendor_item_id%>
<%= vii.text_field :vendor_item_id%><br>
</div>
<% end %>
<p>
<%= f.label :name %><br>
<%= f.text_field :name %>
</p>
<p>
<%= f.label :category %><br>
<%= f.text_field :category %>
</p>
<p>
<%= f.label :description %><br>
<%= f.text_area :description %>
</p>
<p>
<%= f.label :reorder_level %><br>
<%= f.text_field :reorder_level %>
</p>
<p>
<%= f.label :quantity %><br>
<%= f.text_field :quantity %>
</p>
<p>
<%= f.label :price %><br>
<%= f.text_field :price %>
</p>
<h3>Vendors:</h3>
<%= fields_for(#item_vendor) do |ab| %>
<div class= "field">
<%= ab.label "All Vendors:" %><br>
<%= collection_select(:vendors, :id, #all_vendors, :id, :name, {},{:multiple => false})%>
</div>
<% end %>
<p>
<%= f.submit %>
</p>
<% end %>
vendor_item contains a reference to item_id, vendor_id, and has another attribute of vendor_item_id. When the program saves, item_id and vendor_id save but vendor_item_id does not save. I have tried every online source but cannot seem to get that one attribute to save. I am using rails 4. Thanks in advance for any help provided.
You haven't saving the result in an instance variable in create action.
Try giving like this in your create action.
def create
#item = Item.new(item_params)
vendor = params[:vendors][:id]
#item_vendor = #item.build_vendor_item(:vendor_id => vendor)
if #item.save
redirect_to #item
else
render 'new'
end
end
def create
#item = Item.new(item_params)
#vendor = params["item"]["vendors"]
if #item.save
#item.create_vendor_item(#vendor.id => vendor).save
redirect_to #item
else
render 'new'
end
end