This question already has answers here:
form_for with nested resources
(3 answers)
Closed 7 years ago.
simple question that I am not able to solve some how.
I am trying to mimic the first few steps of this Railscast Episode. I have a picture-Model and I am trying to instantiate an object of this kind on the index page. Therefor I am using those lines:
index.erb.html
<%= form_for Picture.new do |f| %>
<%= f.label :image, "Upload" %>
<%= f.file_field :image, multiple: true %>
<% end %>
But I am getting this error:
undefined method `pictures_path' for #<#<Class:0xb465af0>:0x58fc488>
If I remove the form it works perfectly. Seems simple but I can't solve it. I would appreciate some help.
PicturesController
class PicturesController < ApplicationController
respond_to :html
def index
#house = House.find(params[:house_id])
#pictures = #house.pictures
respond_with(#pictures)
end
def new
#picture = Picture.new
end
def create
end
def destroy
end
private
def picture_params
params.require(:picture).permit(:id, :name, :house_id, :image, :_destroy)
end
routes.rb
Rails.application.routes.draw do
resources :houses do
resources :pictures, only: [:index]
end
end
With your given routes info, you don't really have pictures_path. You have only these routes (if you do a rake routes):
house_pictures GET /houses/:house_id/pictures(.:format) pictures#index
houses GET /houses(.:format) houses#index
That's why you are getting that error.
You have access to house_pictures_path BUT NOT pictures_path.
To solve this issue, you have to use house_pictures_path and send the #house and #pictures as argument to that. something like this:
<%= form_for [#house, #house.pictures.build] do |f| %>
<%= f.label :image, "Upload" %>
<%= f.file_field :image, multiple: true %>
<% end %>
Your pictures resource is nested within your houses resource. There is no route to allow you to create a new Picture without a House to provide the surrounding context, and so form_for cannot automatically produce a URL if you give it only Picture.new.
You need to give it both a house, and a picture.
Typically, your would do something like this:
form_for [#house, #house.pictures.new] do |f|
Related
I have a little bit of a newbie question.
I have a basic form with a dropdown list that looks like the following :
## apply.html.erb
<%= form_for #category do |f| %>
<%= f.label 'parent' , 'Category' %>
<%= f.select :category, [["foo", 0 ], ["bar", 1 ]] %>
<% end %>
The dropdown list values are "foo" and "bar".
I am trying to pull values directly from a database. The problem is that I have no idea how to organize the controller and the model.
Here is the controller :
## Welcome_controller.rb
class WelcomeController < ApplicationController
def index
end
def apply
#category = 'foobar'
end
end
I have not generated the controller yet. I can not find any convincing answers to my question or tutorial on the internet.
Any idea how I can make it happen?
** EDIT **
So I have been doing some edits.Here is what I have:
The view
## apply.html.erb
<%= form_for #category, as: :category do |f| %>
<%= f.label 'Categories' %>
<%= f.select :category, #category %>
<% end %>
The controller :
## welcome_controller.rb
def apply
#category = Category.new
#categories = Category.pluck(:id, :name)
end
The model :
## Category.rb
class Category < ApplicationRecord
end
I get the following in the terminal :
ActionView::Template::Error (undefined method `categories_path' for #<#<Class:0x007ffb7c5a2808>:0x007ffb7c2814f8>):
3: <div id="category_block">
4: <span>What would you like to get financed ?</span>
5:
6: <%= form_for #category, as: :category do |f| %>
7: <%= f.label 'Categories' %>
8: <%= f.select :category, #category %>
9: <% end %>
app/views/welcome/apply.html.erb:6:in `_app_views_welcome_apply_html_erb___747255529581383389_70359048261520'
It looks like the problem comes from #category = Category.new because when I replace Category.new with a string like ' foobar', the error disappears.
Any idea how to fix this problem?
You should read more about the documentation. This is a pretty basic question, and you'll probably have the answer if you actually take the time to read and learn.
That said, the info you provided is lacking so I'll just assume.
First, I assume you have a Category model. It should be placed in app/models/category.rb:
def Category
...
end
Next, you're not actually querying anything from the database. You can do it this way:
## app/controllers/welcome_controller.rb
class WelcomeController < ApplicationController
def index
end
def apply
#category = Category.new
#categories = Category.pluck(:name, :id)
end
end
Category.new generates a new instance of the Category (category.rb) object.
Category.pluck(:name, :id) generates this array: [['foo', 1], ['bar', 2]], where name and id are attributes of the Category model (just change it to your liking).
Once you have your array stored in #categories, you can use it in your form like this:
## app/views/welcome/apply.html.erb
<%= form_for #category do |f| %>
<%= f.label 'parent' %>
<%= f.select :category, #categories %>
<% end %>
You're probably not planning to actually create a form for a Category considering that you have a dropdown for categories so you should just change it to something else. Also, take note of the file names. They're all lowercase and separated by underscores. (Welcome_controller.rb should be welcome_controller.rb)
** EDIT **
Now that you've added an object to the form_for, rails will automatically assign a path for your form unless you change it. For this case, the path is categories_path. Read more here.
What you need to do is modify your routes.rb and add the following line:
resources :categories
That line will automatically generate routes that provide a mapping between HTTP verbs and URLs to controller actions.
Next you need to create a CategoriesController:
## app/controllers/categories_controller.rb
class CategoriesController < ApplicationController
def create
#category = Category.new(category_params)
if #account_user.save
redirect_to apply_path
else
render :new
end
end
private
def category_params
params.require(:category).permit! # MODIFY THIS
end
end
Anyway, this is basically a walkthrough and you should be figuring this out yourself. There are a lot of resources out there -- it's not hard to find one. You can also check this out: video.
This should be super simple, but I can't see what I'm doing wrong.
The form in the 'new' page for uploads is getting an error.
'Uploads' belong to 'Event'
'Event' has many 'Uploads'
routes.rb is (as far as I know) correct.
I'm planning on using Refile to upload files to S3 (as per this tutorial... not sure if this is relevant at all though)
Upload.rb
class Upload < ActiveRecord::Base
belongs_to :event
attachment :upload_file
end
Event.rb
class Event < ActiveRecord::Base
has_many :uploads
end
uploads_controller.rb
class UploadsController < ApplicationController
before_action :set_event
def new
#upload = #event.uploads.create
end
private
def set_event
#event = Event.find(params[:event_id])
end
end
Routes.rb
Rails.application.routes.draw do
devise_for :users
root 'pages#home'
resources :events do
resources :coupons
resources :uploads
member do
post :check
end
end
views/uploads/new.html.erb (example)
<%= form_for #upload do |f| %>
<%= f.text_field :name %>
<% end %>
When I navigate to the 'new' page, I get the following error:
undefined method `upload_path' for #<#:0x007fb8709229f0>
Why can't I add a new Upload associated with Event? I know I'm missing something super simple, but I can't put my finger on it.
As uploads is nested in events, you get url for your upload path as follow:
/events/1/uploads/new
In this case, you have to specify #event in your form_for method like this:
<%= form_for [#event, #upload] do |f| %>
Or simply
<%= form_for #event.upload do |f| %>
<%= f.text_field :name %>
<%= f.submit %>
<% end%>
Rails.logger.info(params[:question])
=> {"title"=>"katt"}
#question_list.questions.create(params[:question])
=> ActiveModel::ForbiddenAttributesError (ActiveModel::ForbiddenAttributesError)
#question_list.questions.create("title"=>"katt")
# SUCCES!
I cannot understand why Rails not accepts the params when the exact same value written by hand works fine?
Update
controller:
def new_question
#question_list.questions.create(params[:question])
render nothing: true
end
private
def set_question_list
#question_list = QuestionList.find(params[:id])
end
def question_list_params
params.require(:question_list).permit(questions_attributes: [:id, :question_list_id, :title, :position, :_destroy])
end
view:
<%= form_for #question_list, url: new_question_question_list_path, remote: true do |f| %>
<%= f.text_field :title %>
<%= f.submit %>
<% end %>
With Rails 4, you would need to whitelist the attributes that you would like to be saved in database thats what Strong Parameters is all about. So, #question_list.questions.create(params[:question]) is not going to work in controller and would result in ActiveModel::ForbiddenAttributesError.
QuestionList has_many Questions, and you are trying to implement nested attributes, you should first start with fixing the form as there are NO nested attributes in there. Looking at the question_list_params, I can say that Question model has a field named title, so, your form should look something like:
<%= form_for #question_list, url: new_question_question_list_path, remote: true do |f| %>
<%= f.fields_for :questions do |ff| %>
<%= ff.text_field :title %>
<% end %>
<%= f.submit %>
<% end %>
Next thing that you can do is update the new_question method as below:
def new_question
#question_list.update(question_list_params)
render nothing: true
end
#question_list.update(question_list_params) would take care of updating the question_list record PLUS the nested attributes of question.
Refer to Active Record Nested Attributes For One-To-Many Association for an example.
It's a Rails 4 new feature, called strong parameters. Check it: http://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html
You need to enable attributes in controller. Try something like that:
class QuestionListsController < ApplicationController
def create
...
#question_list.questions.create(question_params)
end
...
private
def question_params
params.require(:question).permit(:title)
end
end
Just for reference:
#Create question and it's options including correct answers.
def create
if params[:question].present?
#question = Question.create(question_params)
else
message("Failed", "400", "Parameter missing!", "Invalid parameters")
end
end
private
def question_params
params.require(:question).permit(:word_id, :question_string, :level, :answers_attributes => [ :answer_string, :is_correct_choice])
end
I hope it will help you debug your code. And as #kirti has already suggested you can use the same format to do rest of the operation as well.
While learning Rails 4 I stuck while trying to add categories to posts of my simple blog. I have generated model, ran the migration and added a controller. No matter what I do now when trying to create a category, I keep running into same mistake: no route matches [POST], which is weird, as I seem to have all the code in place. Please help!
categories controller
class CategoriesController < ApplicationController
def index
#categories = Category.all
end
def new
#category = Category.new
end
def create
#category = Category.new(category_params)
#category.save
redirect_to new_category_path, alert: "Category created!"
end
def show
#category = Category.find(params[:id])
end
def destroy
#category = Category.find(params[:id])
#category.destroy
redirect_to categories_path
end
private
def category_params
params.require(:category).permit(:name)
end
end
routes.rb
Blog::Application.routes.draw do
get 'tags/:tag', to: 'posts#index', as: :tag
resources :categories
resources :posts do
resources :comments
end
root 'welcome#index'
end
category.rb
class Category < ActiveRecord::Base
validates :name, presence: true
has_many :posts
end
new.html.erb
<%= form_for :category do |f| %>
<p>
<%= f.label :name %><br>
<%= f.text_field :name %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
/categories/new
No route matches [POST] "/categories/new"
You should have in your view
<%= form_for #category do |f| %>
<p>
<%= f.label :name %><br>
<%= f.text_field :name %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
#category object is used by form_for method to figure out form url.
If you pass only Symbol to form_for method, without specifying url explicitly, form will be generated with url being the current url.
Although the #category works, if you read a bit further you will see that they will explain why the your code sends No route matches [POST] "/categories/new".
The guide actually explains that you need to specify the url: posts_path for the form to use the right route.
There's one problem with this form though. If you inspect the HTML
that is generated, by viewing the source of the page, you will see
that the action attribute for the form is pointing at /posts/new. This
is a problem because this route goes to the very page that you're on
right at the moment, and that route should only be used to display the
form for a new post.
The form needs to use a different URL in order to go somewhere else.
This can be done quite simply with the :url option of form_for.
Typically in Rails, the action that is used for new form submissions
like this is called "create", and so the form should be pointed to
that action.
Edit the form_for line inside app/views/posts/new.html.erb to look
like this:
<%= form_for :post, url: posts_path do |f| %>
In this example, the
posts_path helper is passed to the :url option. What Rails will do
with this is that it will point the form to the create action of the
current controller, the PostsController, and will send a POST request
to that route.
I want to add the ability of a user to have several pictures associated with his / her user account.
I have the following classes:
class User < ActiveRecord::Base
has_many :assets
accepts_nested_attributes_for :assets
end
class Asset < ActiveRecord::Base
belongs_to :assetable, :polymorphic => true
belongs_to :user
end
I want to have a screen that just has the upload image functionality:
def add_profile_picture
#user=User.find(params[:id])
1.times {#user.assets.build}
end
form:
<%= form_for #user do |u| %>
<%= u.fields_for :assets do |asset| %>
<%= asset.file_field :asset %>
<%= asset.text_field :description %><br />
<% end %>
<%=u.submit %>
<% end %>
When I submit, it looks like the id value goes in ok in development.log:
"id"=>"1"
but I get the error:
undefined method `update_attributes' for nil:NilClass
Since I just have the asset fields, is there anything special I need to do? Also, because the belongs_to :user exists, could that be causing problems?
Basically:
asset:
user_id:
assetable_type:
assetable_id:
Any help would be appreciated. Don't do much Rails forms stuff.
thx
edit #1
class UsersController < ApplicationController
def add_profile_picture
#user=User.find(params[:id])
1.times {#user.assets.build}
end
thx
Okay - there are a few problems with your code here. I would highly recommend you read both the Action Controller Overview and the Rails Routing guides to get some more information about this.
In any case, you're getting the error because the form you have there will be trying to use the users#update action in the UsersController.
You've got a couple options. One is to create the necessary routes for the custom action, or you can create a nested resource, and make a form for adding the asset.
In this case, you'd do something like this:
in routes.rb
resources :users do
resources :assets, :only => [:new, :create] # Or any other actions you might want. It's best practise to limit these.
end
Then, in the AssetsController, you can do something similar to this:
def new
#asset = Asset.new
end
def create
#asset = Asset.new(params[:asset])
#asset.user_id = params[:user_id] if params[:user_id]
#asset.save!
end
and your form will look something like this:
<%= form_for #asset do |f| %>
<%= f.file_field :asset %>
<%= f.text_field :description %><br />
<%=f.submit %>
<% end %>