Been searching stackoverflow for an answer to this one all day. I have a form to create a new topic. The first post should also be created with the topic. All is well except user_id is not being saved to the post.
Post Model
class Post < ActiveRecord::Base
belongs_to :topic
belongs_to :user
end
Topic Model
class Topic < ActiveRecord::Base
belongs_to :forum
belongs_to :user
has_many :posts
accepts_nested_attributes_for :posts
end
Post Controller
class PostsController < ApplicationController
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
if #post.save
redirect_to topic_path(#post.topic_id)
else
render 'new'
end
end
private
def post_params
params.require(:post).permit(:content, :topic_id, :topic_name, :user_id)
end
end
Topic Controller
class TopicsController < ApplicationController
def new
#topic = Topic.new
#topic.posts.build
end
def create
#topic = Topic.new(topic_params)
if #topic.save
redirect_to #topic
else
render 'new'
end
end
private
def topic_params
params.require(:topic).permit(
:topic_name,
:forum_id,
:user_id,
posts_attributes: [:id, :content, :topic_id, :topic_name, :user_id ] )
end
end
new/topic View
<%= form_for(#topic) do |f| %>
<%= f.hidden_field :forum_id, :value => params[:forum_id] %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<%= f.label :topic_name %>
<%= f.text_field :topic_name %>
<%= f.fields_for :posts do |p| %>
<%= p.label :content %>
<%= p.text_area :content %>
<% end %>
<%= f.submit "Post Topic", class: "btn btn-large btn-success" %>
<% end %>
I am not entirely sure why the user_id is not being passed to the post. Hopefully someone smarter than me can help me learn what to do :)
UPDATE
I changed the strong params in my topics controller to this.
def topic_params
params.require(:topic).permit(
:topic_name,
:forum_id,
posts_attributes: [:content, :topic_id, :id, '_destroy' ] ).merge(:user_id => current_user.id, posts_attributes: [:user_id => current_user.id])
end
Now the user_id is working but none of the posts_attributes like :content are being saved. I'm having a lot of fun with this one..
Notice the form attributes that being generated in the browser, all the nested attributes for post have a prefix like topic[post_attributes], try change the form to:
<%= form_for(#topic) do |f| %>
<%= f.hidden_field :forum_id, :value => params[:forum_id] %>
<%= f.label :topic_name %>
<%= f.text_field :topic_name %>
<%= f.fields_for :posts do |p| %>
<%= p.hidden_field :user_id, :value => current_user.id %>
<%= p.label :content %>
<%= p.text_area :content %>
<% end %>
<%= f.submit "Post Topic", class: "btn btn-large btn-success" %>
<% end %>
Short answer, user_id is not in the posts_attributes since the only attributes there is content, which means that allowing other attributes like topic_id and topic_name is useless.
Now that we cleared that, you SHOULD NOT use a form input for the value of the creator of any model, because it's easy for anyone to tamper with the form and set the value to anything else, like other user's id. Alternatively, you should set the user_id value in the controller, in your case, the TopicsController. Here is the code:
def create
_params = topic_params.deep_merge(user: current_user, posts_attributes: {user: current_user})
#topic = Topic.new(_params)
if #topic.save
redirect_to #topic
else
render 'new'
end
end
and remove the user_id hidden field from the form.
UPDATE: Your last code update contains an error; it should be .merge(:user_id => current_user.id, posts_attributes: {:user_id => current_user.id}). You used a square brackets around :user_id => current_user.id instead of curly ones.
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 %>.
It's a lot of question about it all around, but I can't find the answer.
I've:
group.rb
class Group < ActiveRecord::Base
has_many :descriptions, :as => :describable
accepts_nested_attributes_for :descriptions
end
description.rb
class Description < ActiveRecord::Base
belongs_to :describable, :polymorphic => true
end
groups_controller.rb
def update
#group = Group.find(params[:id])
if #group.update_attributes(group_params)
flash[:success] = "yes"
redirect_to groups_path
else
render 'edit'
end
end
private
def group_params
params.require(:group).permit(:owner_id, :domain, descriptions_attributes: [:id, :content])
end
edit.html.erb
<%= simple_form_for #group do |f| %>
<% if #group[:domain].blank? %>
<%= f.input :domain %>
<% else %>
<%= f.input :domain, readonly: true %>
<% end %>
<%= f.input :owner_id, readonly: true %>
<%= f.simple_fields_for :descriptions do |description| %>
<%= description.input :content %>
<% end %>
<%= f.button :submit %>
<% end %>
In console I've Unpermitted parameter: description and nested attribute does not created. What should I do to save it at last?
I suppose Rails does not convert form name to names_attributes when generating form for nested polymorphic connection, this:
... description: [:content, :other_param, ...]
Works fine for me for polymorphic child.
I have a route that looks like this
resources :questions do
resources :answers do
resources :comments
end
end
However, when I try to build a comment
<%= form_for([#answer, #answer.comments.build]) do |f| %>
<p>
<%= f.label :comment %>
<%= f.text_area :comment, :cols => "50", :rows => "30"%>
</p>
<p>
I get undefined method comments. This is what my create comment looks like
def create
#answer = Answer.find(params[:answer_id])
#comment = #answer.comments.create(params[:comment])
redirect_to question_path(#question)
end
And answer has_many comments, and comments belongs to answer. Any ideas? Thanks!
You can access models from any controller in your app (just call Model.class_method)
Looking at your code, I'd recommend doing something like this:
#app/views/answers/show.html.erb
<% #answer.comments.each do |comment| %>
<%= comment.value %>
<% end %>
<%= form_for #new_comment do |f| %>
<%= f.text_area :comment, :cols => "50", :rows => "30"%>
<% end %>
#app/controllers/answers_controller.rb
def new
#answer = Answer.find(params[:id])
#new_comment = Comment.new
end
#app/controllers/comments_controller.rb
def create
#comment = Comment.new(comment_params)
#comment.save
end
private
def comment_params
params.require(:comment).permit(:your, :attributes, :here)
end
I'm trying to create one resource with another nested resource at the same time. I'm using Rails4 and simple_form 3.0.0rc. Here is my code.
Models:
class User < ActiveRecord::Base
has_one :profile
accepts_nested_attributes_for :profile
end
class Profile < ActiveRecord::Base
belongs_to :user
end
Controller:
class UsersController < ApplicationController
def new
#user = User.new
#user.build_profile
end
def create
user = User.new user_params
user.save
redirect_to root_url
# #par =params
end
private
def user_params
params.require(:user).permit(:email, profile_attributes: [:name])
end
end
View (form for new user)
<%= simple_form_for #user do |f| %>
<%= f.input :email %>
<%= simple_fields_for :profile do |p| %>
<%= p.input :name %>
<% end %>
<%= f.submit %>
<% end %>
When I submit the form, the create action receives this params:
{"utf8"=>"✓",
"authenticity_token"=>"dJAcMcdZnrtTXVIeS2cNBwM+S6dZh7EQEALZx09l8fg=",
"user"=>{"email"=>"vasily.sib#gmail.com"},
"profile"=>{"name"=>"Vasily"},
"commit"=>"Create User",
"action"=>"create",
"controller"=>"users"}
And after calling user_params the only thing that left is
{"email"=>"vasily.sib#gmail.com"}
And, as you can see, there is nothing about profile, so no profile will be created.
What am I doing wrong?
Use f.simple_fields_for instead of simple_fields_for:
<%= f.simple_fields_for :profile do |p| %>
<%= p.input :name %>
<% end %>
In my case I had the object "book" which belongs to "tour" and "tour" has_many "books".
In the "BookController" in the method "new" I find the tour and initialize the book object:
#tour = Tour.find(params[:tour_id])
#book = Book.new
This is the partial form to create a book: _form.html.erb
<%= simple_form_for [#tour, #book] do |f| %>
<%= f.input :name, label: "Name"%>
<%= f.input :NoReservations, label: "Number of Reservations" %>
<%= f.input :email, label: "Email" %>
<h3>Num of available places</h3>
<%= f.button :submit %>
<% end %>
Hi I'm trying to create a form, that at the same time, creates a list and associates products to it.
The problem is that the form keeps raising
wrong number of arguments (0 for 1)
Extracted source (around line #10):
7: <%= f.text_area :description, placeholder:
8: "Compose a description for it ..." %>
9: </div>
10: <%= l.fields_for :products do |builder| %>
11: <%= render 'shared/product_form', :l => builder %>
12: <% end %>
13: <%= l.submit "Create", class: "btn btn-large btn-primary" %>
App Trace is
app/views/shared/_list_form.html.erb:10:in `block in _app_views_shared__list_form_html_erb__184644094_33330696'
app/views/shared/_list_form.html.erb:1:in `_app_views_shared__list_form_html_erb__184644094_33330696'
app/views/lists/new.html.erb:7:in `_app_views_lists_new_html_erb__973495114_33282228'
The code is as follows:
---view----
--list_form--
<%= form_for(#list) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_field :name, placeholder:
"Come up with a name for your list" %>
<%= f.text_area :description, placeholder:
"Compose a description for it ..." %>
</div>
<%= f.fields_for :products do |builder| %>
<%= render 'shared/product_form', :f => builder %>
<% end %>
<%= f.submit "Create", class: "btn btn-large btn-primary" %>
<% end %>
--product_form--
<%= f.text_field :name, "Name:" %>
<%= f.text_area :description, :rows => 3 %>
---model---
--list--
class List < ActiveRecord::Base
attr_accessible :description, :name
belongs_to :user
has_many :products, :dependent => :destroy
accepts_nested_attributes_for :products, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => true
has_many :list_categorization
has_many :category, :through => :list_categorization
validates :user_id, presence: true
validates :name, presence: true, length: {maximum: 10}
validates :description, length: {maximum: 140}
default_scope order: 'lists.created_at DESC'
def categorize!(category_id)
list_categorization.create!(category_id: category_id)
end
end
--product--
class Product < ActiveRecord::Base
attr_accessible :description, :donated, :name
validates :list_id, presence: true
belongs_to :list
end
---controllers---
--list_controller--
def new
#list = List.new
#products = #list.products.build
end
def create
#list = current_user.lists.build(params[:list]) if signed_in?
if #list.save
flash[:success] ="List " + #list.name + "created!"
render 'new'
end
--product_controller--
def new
#product = Product.new
end
def create
#product = #product.build(params[:product]) if signed_in?
if #product.save
flash[:success] ="Product " + #product.name + "created!"
end
You were right, I actually realized it after posting this, but now while trying to submit the form this happens:
The form contains 1 error.
* Name can't be blank
event tough I filled it correctly, this is what is getting passed
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
utf8: ✓
authenticity_token: 38CXjVORlj2RBgoTetIMoHomcVgOIlBU5rW3NTgkRkU=
list: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
name: list
description: this is a list
products_attributes: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
'0': !ruby/hash:ActiveSupport::HashWithIndifferentAccess
name: p1
description: this is a product
commit: Create
action: create
controller: lists
Where did that l come from? I'm pretty sure you need to change it to f:
<%= form_for(#list) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_field :name, placeholder: "Come up with a name for your list" %>
<%= f.text_area :description, placeholder: "Compose a description for it ..." %>
</div>
<%= f.fields_for :products do |builder| %>
<%= render 'shared/product_form', :l => builder %>
<% end %>
<%= f.submit "Create", class: "btn btn-large btn-primary" %>
<% end %>
Update
There are a few problems with your code. First of all when you call #list = current_user.lists.build(params[:list]) if signed_in? it means that if there is no user signed in that object won't be created at all. The proper way to do something like this would be with a before_filter in your controller.
Secondly #product = #product.build(params[:product]) won't work. You haven't initialized a Product object yet, and you haven't assigned it to #product yet. Also build is used for associations. You need to change this to #product = Product.new(params[:product]).
Lists controller:
before_filter :user_signed_in? # add to products controller as well
# if you need this filter only on certain actions then do:
# before_filter :user_signed_in?, only: [:new, :create]
def new
#list = current_user.lists.build
#products = #list.products.build
end
def create
#list = current_user.lists.build(params[:list])
if #list.save
flash[:success] = "List " + #list.name + " created!"
redirect_to lists_path # this part was missing!
else # this was also missing
render 'new'
end # you had an 'if' with no 'end'
end
private
# add the following to Products controller as well, or if you
# use it a lot then place it in your application controller
def user_signed_in?
unless signed_in?
flash[:notice] = "You must first sign in"
redirect_to sign_in_path
end
end
Products controller:
def new
#product = Product.new
end
def create
#product = Product.new(params[:product]
if #product.save
flash[:success] = "Product " + #product.name + " created!"
redirect_to #product
else
render 'new'
end
end
As far as I remember however, the products#create action won't be used when saving a product through a nested form, the lists#create action will be used for both.
To learn more about nested forms have a look at these railscasts.
Once you've updated your code and gone through those videos, if you're still getting errors I would recommend to create a new question since this one is getting long and messy already :)
you forgot to do this:
rails generate migration add_remember_token_to_users