I'm trying to build a simple product backlog application to teach myself Rails. For each product, there can be multiple product backlog entries, so I want to create a product view that shows the product information, all the backlog entries for the product, and includes a nested form for adding more backlog entries.
Everything works until I try to add the form to the view, which then results in the following error:
NoMethodError in Products#show
Showing app/views/products/show.html.erb where line #29 raised:
undefined method `pblog_ref' for #<Product:0x10423ba68>
Extracted source (around line #29):
26: <%= f.error_messages %>
27: <p>
28: <%= f.label :pblog_ref %><br />
29: <%= f.text_field :pblog_ref %>
30: </p>
31: <p>
32: <%= f.label :product %><br />
The product view where the problem is reported is as follows (the partial works fine, so I won't include that code):
<h1>Showing product</h1>
<p>
<b>Product ref:</b>
<%=h #product.product_ref %>
</p>
<p>
<b>Description:</b>
<%=h #product.description %>
</p>
<p>
<b>Owner:</b>
<%=h #product.owner %>
</p>
<p>
<b>Status:</b>
<%=h #product.status %>
</p>
<h2>Product backlog</h2>
<div id="product-backlog">
<%= render :partial => #product.product_backlogs %>
</div>
<% form_for(#product, ProductBacklog.new) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :pblog_ref %><br />
<%= f.text_field :pblog_ref %>
</p>
<p>
<%= f.label :product %><br />
<%= f.text_field :product %>
</p>
<p>
<%= f.label :description %><br />
<%= f.text_field :description %>
</p>
<p>
<%= f.label :owner %><br />
<%= f.text_field :owner %>
</p>
<p>
<%= f.label :status %><br />
<%= f.text_field :status %>
</p>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>
<%= link_to 'Edit', edit_product_path(#product) %> |
<%= link_to 'Back', products_path %>
This is the Product model:
class Product < ActiveRecord::Base
validates_presence_of :product_ref, :description, :owner
has_many :product_backlogs
end
This is the ProductBacklog model:
class ProductBacklog < ActiveRecord::Base
belongs_to :product
end
These are the routes:
map.resources :product_backlogs
map.resources :products, :has_many => :product_backlogs
All the columns exist in the schema for this model:
create_table "product_backlogs", :force => true do |t|
t.string "pblog_ref"
t.integer "product_id"
t.string "description"
t.string "owner"
t.string "status"
t.datetime "created_at"
t.datetime "updated_at"
end
I've checked what I'm doing against the Creating a weblog in 15 minutes with Rails 2 screencast, and in principle I seem to be doing the same thing as him - only his nested comments form works, and mine doesn't!
I hope someone can help with this, before I turn mad! I'm sure it's something trivial.
check your database, maybe some migration failed and you have only part of fields there
you can also check with running script/console first, and then issuing "p Product.new". It will show you an empty Product object with all known fields, like:
#./script/console
Loading development environment (Rails 2.3.5)
>> p User.new
#<User id: nil, type: nil, login: nil, name: "", email: nil, crypted_password: nil, salt: nil, created_by_id: nil, created_at: nil, updated_at: nil, remember_token: nil, remember_token_expires_at: nil, male: true, dept_id: nil, deleted: nil>
=> nil
It seems that you don't have pblog_ref field in your model. Take a look into migration that created products table, is there pblog_ref field?
EDIT:
Ok, it seems that this line is wrong:
<% form_for(#product, ProductBacklog.new) do |f| %>
It creates form for your #product not for ProductBacklog.new. Try:
<% form_for(ProductBacklog.new) do |f| %>
I always have problems with form_for arguments with some nested models, so I prefer using accepts_nested_attributes_for and then creating subobjects with fields_for.
In your case form_for should look like this:
<% form_for ProductBacklog.new, :url => new_product_product_backlog_path(#product) do |f| %>
However I didn't check it and it probably is wrong (as I said I always have problems with those paths).
The solution was to replace the line:
<% form_for(#product, ProductBacklog.new) do |f| %>
with:
<% form_for [#product, ProductBacklog.new] do |f| %>
Note the space after form_for, and the square brackets.
Related
I have an Orders model that has many Items and Customers. For some reason, and I have looked high and low for a solution, I am unable to save the attributes for Items and Customers in their respected tables when using a nested form.
I'm relatively new to Rails, so my apologies if I've missed something obvious.
Started POST "/orders" for ::1 at 2017-05-16 16:12:17 -0600 Processing
by OrdersController#create as HTML Parameters: {"utf8"=>"✓",
"authenticity_token"=>"k1QPQ560j44Mx0SNeCMBQAUGLQfEi4g0QpDfHuYeP4Zd7nVgcHJf3qXNsVQv29dV+5G0oJ7wiaoQ3Idw+DP+iw==",
"order"=>{"customer"=>{"name"=>"Test", "email"=>"Test",
"phone"=>"Test"}, "item"=>{"name"=>"Test", "ref_num"=>"Test",
"retail"=>"122"}, "status"=>"Test", "arrival"=>"06/06/19",
"tracking"=>"Test439"}, "commit"=>"Save Order"} Unpermitted
parameters: customer, item
Orders Controller
def create
#order = Order.new(order_params)
#order.items.build
#order.customers.build
#order.save
redirect_to #order
end
private
def order_params
params.require(:order).permit(:status, :arrival, :tracking,
customers_attributes: [:name, :phone, :email],
items_attributes: [:name, :ref_num, :retail])
end
Orders Model
class Order < ApplicationRecord
has_many :items
has_many :customers
accepts_nested_attributes_for :items, :customers
end
Orders Form (New)
<%= form_for :order, url: orders_path do |f| %>
<%# customer parameters%>
<%= f.fields_for :customers do |cu| %>
<p>
<%= cu.label :customer_name %><br>
<%= cu.text_field :name %>
</p>
<p>
<%= cu.label :email %><br>
<%= cu.text_field :email %>
</p>
<p>
<%= cu.label :phone %><br>
<%= cu.text_field :phone %>
</p>
<% end %>
<%# Item parameters %>
<%= f.fields_for :items do |it| %>
<p>
<%= it.label :item_name %><br>
<%= it.text_field :name %>
</p>
<p>
<%= it.label :reference_number %><br>
<%= it.text_field :ref_num %>
</p>
<p>
<%= it.label :retail %><br>
<%= it.text_field :retail%>
</p>
<% end %>
<%# order parameters%>
<p>
<%= f.label :status %><br>
<%= f.text_field :status %>
</p>
<p>
<%= f.label :est_arrival %><br>
<%= f.text_field :arrival %>
</p>
<p>
<%= f.label :tracking %><br>
<%= f.text_field :tracking %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
<%= link_to 'Back', orders_path %>
This is happening because form_for :order doesn't make reference to your model object, it makes a simple :order param, which doesn't takes into consideration your model's accepts_nested_attributes_for, thus not building f.fields_for :customers with _attributes suffix. You should instantiate a Order object for the expected behavior to happen in your form_for #object instead of just referencing a :object which renders only the name of the param.
I'm trying to finish the codecademy ruby on rails track. I'm getting this error:
Showing /home/ccuser/workspace/learn-rails_innovation-cloud/innovation
cloud/app/views/signups/new.html.erb where line #41 raised:
undefined method `content' for #<Signup id: nil, email: nil, created_at: nil, updated_at: nil>
Extracted source (around line #41):
<%= form_for(#signup) do |f| %>
<div class="field">
<%= f.label :message %><br>
<%= f.text_area :content %>
</div>
<div class="actions">
<%= f.submit "Create" %>
Here's my controller:
class SignupsController < ApplicationController
def new
#signup = Signup.new
end
end
And finally my migration file:
class CreateSignups < ActiveRecord::Migration
def change
create_table :signups do |t|
t.text :email
t.timestamps
end
end
end
Any insight is appreciated to understand what I'm doing wrong.
content field is missing in your signups table. Please add using migration.
Your form code should look like:
<%= form_for(#signup) do |f| %>
<div class="field">
<%= f.label :email %><br>
<%= f.text_field :email %>
</div>
<div class="actions">
<%= f.submit "Create" %>
And it should work now.
I'm following this tutorial to create tags for a model (in my case the model Post):
controllers/posts_controller.rb:
def create
#user = current_user
#post = #user.posts.new(params[:post])
if #post.save
redirect_to #post, notice: 'post was successfully created.'
else
render action: "new"
end
#post.tag!(params[:tags])
end
views/posts/_form.html.erb:
<%= form_for(#post) do |f| %>
<%= render 'shared/error_messages' %>
<div class="field">
<%= f.label :title %><br />
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :content %><br />
<%= f.text_area :content %>
</div>
<div class="field">
<%= f.label :tags %>
<%= f.text_field :tags, params[:tags] %>
</div>
<div class="actions">
<%= f.submit %>
</div>
views/posts/show.hmtl.erb:
<div class="tags">
<h4>Tags:</h4>
<%= render #post.tags %>
</div>
models/post.rb:
class Post < ActiveRecord::Base
has_and_belongs_to_many :tags
def tag!(tags)
tags = tags.split(" ").map do |tag|
Tag.find_or_create_by_name(tag)
end
self.tags << tags
end
end
models/tag.rb:
class Tag < ActiveRecord::Base
has_and_belongs_to_many :posts
end
db/migrate/(etc...)_create_tags.rb:
class CreateTags < ActiveRecord::Migration
def change
create_table :tags do |t|
t.string :name
end
create_table :tags_posts, :id => false do | t |
t.integer :tag_id, :post_id
end
end
end
Now, when I visit the posts form I get this error:
undefined method `merge' for nil:NilClass
Extracted source (around line #13):
10: </div>
11: <div class="field">
12: <%= f.label :tags %>
13: <%= f.text_field :tags, params[:tags] %>
14: </div>
15: <div class="actions">
16: <%= f.submit %>
When I visit a post I get this error:
SQLite3::SQLException: no such table: posts_tags: SELECT "tags".* FROM "tags" INNER JOIN "posts_tags" ON "tags"."id" = "posts_tags"."tag_id" WHERE "posts_tags"."post_id" = 7
Extracted source (around line #24):
21:
22: <div class="tags">
23: <h4>Tags:</h4>
24: <%= render #post.tags %>
25: </div>
26:
27: </div>
But I do have these tables as you can see in my schema.rb file:
create_table "tags", :force => true do |t|
t.string "name"
end
create_table "tags_posts", :id => false, :force => true do |t|
t.integer "tag_id"
t.integer "post_id"
end
Any suggestions to fix this?
Just in case anyone else comes across this old question through google:
You need to use 'value:' for the default value in a simple form.
<%= f.text_field :tags, value: params[:tags] %>
You have the table name backwards. HABTM looks for the models alphabetically. Look at the error carefully. It says posts_tags cannot be found. You create tags_posts. So change your table name to posts_tags.
try this <%= f.text_field_tag :tags, params[:tags] %> in your partial _form.html.erb.
<%= form_with(
url: order_register_path(session[:order_id]),
method: :put
) do |form| %>
<%= form.check_box :review_budget %>
<%= form.check_box :fast_delivery %>
<%= form.check_box :replace_similar %>
<div>
<%= f.submit "Continuar" %>
</div>
<% end %>
f.submit instead of that submit_tag
I want to input selections and input it into micropost table. The selections is from categories table. It encounter problem of category column of micropost is blank so I suspect it cannot input data into the table micropost column of category. I search all the select, select_tag but cannot solve it... Is there anything I miss out?
<%= form_for #micropost do |f| %>
<%= render 'shared/error_messages', :object => f.object %>
<%= f.label :title %><br />
<%=h f.text_field :title %><br />
<%= f.label :content %><br />
<%=h f.text_area :content, :row => 30, :cols=> 30 %><br />
<% #category = Category.select("category").group("category")
cat = #category.map{|u| u.category}
%>
<%= select_tag :category, options_for_select(cat) %>
<%= f.submit "Post" %>
<% end %>
If you check your query you should see that parameter category is sent separately from micropost attributes because
<%
#category = Category.select("category").group("category")
cat = #category.map{|u| u.category}
%>
<%= select_tag :category, options_for_select(cat) %>
generates field inside form with name category not micropost[category] to fix it you can use select instead
<%
cat = Category.select( "category" ).group( "category" ).map( &:category )
%>
<%= select :micropost, :category, options_for_select( cat ) %>
I am examining the complex_form_example project on github. I modified the migration file so that :content changed from t.text to t.string (as shown below): Please notice that t.string is the only place I changed in the github example project and before the change it all worked.
class CreateQuestions < ActiveRecord::Migration
def self.up
create_table :questions do |t|
t.integer :survey_id
t.string :content
##t.text :content
t.timestamps
end
end
def self.down
drop_table :questions
end
end
Here is the nested_form_for code:
<%= nested_form_for #survey do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<%= f.fields_for :questions, :questions do |g|%>
<p>
<%= g.label :content, "Question" %><br />
<%= g.input :content, :rows => 3 %><br />
<%= g.link_to_remove "remove" %>
</p>
<%= g.fields_for :answers %>
<p><%= g.link_to_add "Add an answer", :answers %></p>
<% end %>
<p><%= f.link_to_add "Add a question", :questions %></p>
<p><%= f.submit "Submit" %></p>
<% end %>
And now I get the following error message:
undefined method `content' for :questions:Symbol
Extracted source (around line #10):
7: <%= f.fields_for :questions, :questions do |g|%>
8: <p>
9: <%= g.label :content, "Question" %><br />
10: <%= g.text_field :content, :rows => 3 %><br />
11: <%= g.link_to_remove "remove" %>
12: </p>
13: <%= g.fields_for :answers %>
My question is, is there a fields_for field name that supports string type directly?
You have :questions, :questions...Do you see that? :questions appears two times before do
The fields_for helper is designed to take an object for which you are building the fields for or, alternatively, the name of the collection of objects which to build fields for.
The first example is something like this:
<%= f.fields_for :object do |object_fields| %>
# fields go here
In this case, it will build the fields for that object.
The second example is the same, but different:
<%= f.fields_for :questions do |question_fields| 5>
# fields go here
<% end %>
Assuming you have a questions method on whatever object f is representing, this will iterate through all those objects in that collection and present the same fields for each of the objects.
Please note: it is not necessary to specify :questions a second time. You only need to tell Rails once.
Now, if you're doing this you're probably going to want to have accepts_nested_attributes_for :questions in the parent model (whatever it is that has_many :questions) so that the fields are passed through to the controller and into the model's create or update_attributes calls successfully.
I seem to have resolved my own issue, by carefully matching fields_for tag with end tag. This works as follows: in view/survey
<%= nested_form_for #survey do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<%= f.fields_for :questions do |g|%>
<%= render "question_fields", :f => g %>
<% end %>
<p><%= f.link_to_add "Add a question", :questions %></p>
<p><%= f.submit "Submit" %></p>
<% end %>
In partial _question_fields:
<p>
<%= f.label :content, "Question" %><br />
<%= f.text_field :content, :rows => 3 %><br />
<%= f.link_to_remove "remove" %>
</p>
<%= f.fields_for :answers %>
<p><%= f.link_to_add "Add an answer", :answers %></p>
Note that the "f.fields_for :answers" uses the partial _answer_fields by default.