Having trouble submitting nested resource through form [Rails 5] - ruby-on-rails

I have a Document that has_many Section, and each section has_one Comment. I want to be able to create both sections and comments in the Document show view, but I'm having trouble getting comments to go through.
Here's the relevant code with the closest I've got:
class CommentsController < ApplicationController
def create
#section = Section.find(params[:id])
#section.comment.create(comment_params)
end
private
def comment_params
params.require(:comment).permit(:body)
end
end
The routing:
resources :documents, shallow: true do
resources :sections do
resources :comments
end
end
And the view with the form:
# app/views/documents/show.html.erb
<% #document.sections.each do |section| %>
<%= section.body %>
<% if section.comment %>
<p>
<%= section.comment %>
</p>
<% else %>
<%= form_with url: section_comments_path(section.id), scope: 'comment' do |form| %>
<%= form.text_field :body, placeholder: "Comment" %>
<%= form.submit %>
<% end %>
<% end %>
<% end %>
It all seems to check out for me, but when I try to post a comment, here's what I get:
Started POST "/sections/51/comments" for ::1 at 2019-05-24 23:29:06 +0000
Processing by CommentsController#create as JS
Parameters: {"utf8"=>"✓", "authenticity_token"=>[...], "comment"=>{"body"=>"asdas"}, "commit"=>"Save comment", "section_id"=>"51"}
Section Load (0.5ms) SELECT "sections".* FROM "sections" WHERE "sections"."id" = ? LIMIT ? [["id", 51], ["LIMIT", 1]]
comment Load (0.4ms) SELECT "comments".* FROM "comments" WHERE "comments"."section_id" = ? LIMIT ? [["section_id", 51], ["LIMIT", 1]]
Completed 500 Internal Server Error in 11ms (ActiveRecord: 0.9ms)
NoMethodError (undefined method `create' for nil:NilClass):
app/controllers/comments_controller.rb:4:in `create'
Any ideas?

A has_one relationship returns the object itself. Therefore, #section.comment.create(comment_params) will not work because #section.comment is nil. Instead, try something like...
def create
#section = Section.find(params[:section_id])
#comment = Comment.create(comment_params)
#section.comment = #comment
...
end
Or, as stated in the Rails Guides...
When initializing a new has_one or belongs_to association you must use
the build_ prefix to build the association, rather than the
association.build method that would be used for has_many or
has_and_belongs_to_many associations. To create one, use the create_
prefix.
Which would look like this
def create
#section = Section.find(params[:section_id])
#section.create_comment(comment_params)
...
end

You likely need to change:
#section.comment.create(comment_params)
to:
#section.comments.create(comment_params)
If that doesn't work, try:
#section.comment.create!(comment_params)
and see what the exception says

Related

Rails 7 - has_many_attached delete old attachment when new is loaded

I'v got problem with Active Storage under Rails 7.
I read some posts but all are about Rails 5-6 and some methods was depreciated since then.
Problem - when I try to upload second photo, old is deleted. Why? How to fix this?
I have model:
class Profile
has_many_attached :photos
has_one_attached :avatar
in controller:
class ProfilesController < ApplicationController
def update
Rails.logger.debug params.inspect
#profile = current_user.profile
#profile.update(profiles_params)
private
# Only allow a list of trusted parameters through.
def profiles_params
params.require(:profile).permit(:id, :email, :marketing,
:marketing_second, :terms,
:personal_data,
:birthdate, :avatar, :photos )
end
in view:
<%= form_for #profile do |f| %>
<div class='upload_container'>
<%= f.file_field :photos, accept:'image/*',
:class=>'file_field_custom', required: true %>
<%= f.button "Upload", type:'submit', :class=>'photos_submit_button' %>
</div>
<% end %>
This was removed from Rails 7:
config.active_storage.replace_on_assign_to_many = false
logs:
#<ActionController::Parameters {"_method"=>"patch",
"authenticity_token"=>"y5n4UgRhRWFB9Q0L3J1roxK3dhuijRQk7Fng6sjHU52G0KDs9OJlYt8nmshG8HYnlrk7JB9x3QHDO8_GQnDwsA", "profile"=>{"photos"=>#<ActionDispatch::Http::UploadedFile:0x00007f9773f3db78 #tempfile=#Tempfile:/tmp/RackMultipart20230119-3094-gapmw4.jpg, #original_filename="saturn-planet-3200-1200-1966.jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name="profile[photos]"; filename="saturn-planet-3200-1200-1966.jpg"\r\nContent-Type: image/jpeg\r\n">}, "controller"=>"profiles", "action"=>"update", "id"=>"abcd"} permitted: false>
[1m[36mProfile Load (0.6ms)[0m [1m[34mSELECT profiles.* FROM profiles WHERE profiles.user_id = 1 LIMIT 1[0m
↳ app/models/user.rb:59:in `profile'
[1m[36mActiveStorage::Attachment Destroy (4.4ms)[0m [1m[31mDELETE FROM
`active_storage_attachments` WHERE `active_storage_attachments`.`id` = 334[0m
↳ app/controllers/profiles_controller.rb:133:in `update'
Why action Destroy is fired.
#profile.photos.attach(params[:profile][:photos]) in update action of your controller

Rails 4 Nested Forms, Error: Unpermitted parameter: order_item

My goal is to create a new Order and an associated OrderItem using the same form.
Models
class Order < ActiveRecord::Base
belongs_to :user
has_many :order_items, dependent: :destroy
accepts_nested_attributes_for :order_items
validates_associated :order_items
end
class OrderItem < ActiveRecord::Base
belongs_to :order
default_scope -> { order(created_at: :desc) }
end
View
<% #items.each do |item| %>
<%= form_for(#order) do |f| %>
<%= f.hidden_field :user_id, :value => session[:user_id] %>
<%= f.fields_for :order_items do |oi| %>
<%= oi.hidden_field :product_id, :value => item.id %>
<%= oi.hidden_field :price, :value => item.price %>
<%= oi.number_field :quantity, value: 1, class: 'form-control', min: 1 %>
<% end %>
<%= f.submit "Buy Now", class: "btn btn-primary" %>
<% end %>
Controller
def new
#order = Order.new
#order.order_items.build
end
def create
#order = Order.new(order_params)
if #order.save
redirect_to cart_path
else
redirect_to root_url
end
end
private
def order_params
params.require(:order).permit(:user_id, :custom_item_id, order_items_attributes: [:product_id, :price, :quantity])
end
When submitting the nested form data to the database, the error message Unpermitted parameter: order_item gets returned and only the order data is saved.
Update <-- This is resolved
When I remove the "f." from <%= f.fields_for the form renders correctly and order_params includes the order_items data. This is interesting because the RailsGuide for Form Helpers includes the "f." http://guides.rubyonrails.org/form_helpers.html#nested-forms
Parameter
{"utf8"=>"✓", "authenticity_token"=>"<TOKEN>", "order"=>{"user_id"=>"1", "order_item"=>{"product_id"=>"5", "price"=>"120.0", "quantity"=>"1"}}, "commit"=>"Buy Now"}
The data still does not save to the corresponding models.
Update 2 <-- This is resolved
Updated the createaction in the controller to if #order.save!, below is the error message:
Validation failed: Order items order can't be blank, Order items product can't be blank, Order items quantity can't be blank, Order items price can't be blank, Order items is invalid
I believe that the mistake is in this line of code #order.order_items.build(order_params[:order_items_attributes]) but I am not sure what I need to change.
Update 3 Unpermitted parameter: order_item Error message
From the terminal:
Processing by OrdersController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=><TOKEN>, "order"=> "user_id"=>"1", "order_item"=>{"product_id"=>"5", "price"=>"120.0", quantity"=>"1"}}, "commit"=>"Buy Now"}
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 1]]
Unpermitted parameter: order_item
(0.2ms) begin transaction
SQL (1.4ms) INSERT INTO "orders" ("user_id", "created_at", "updated_at") VALUES (?, ?, ?) [["user_id", 1], ["created_at", "2016-03-18 14:58:21.724246"], ["updated_at", "2016-03-18 14:58:21.724246"]]
(14.4ms) commit transaction
The order_itemsdata does not get saved.
First, it seems like your association is incorrect in OrderItem. It should be belongs_to :order instead of belongs_to :order_item_params.
Second, I believe your form should say <%= f.fields_for :order_items do |oi| %> (:order_items not :order_item)
Finally, you should not need to do this in your controller: #order.order_items.build(order_params[:order_items_attributes])
Controller
def create
#order = Order.new(order_params)
if #order.save
redirect_to cart_path
else
redirect_to root#url
end
end
def order_params
params.require(:order).permit(:user_id, :custom_item_id, order_items_attributes: [:product_id, :price, :quantity])
end
The view that rendered the form was not in another controller and not in the orders controller. The show action of tht view did not include an instance variable for order_items. This caused the error messages. Thank you for all of your help. For anyone looking for resources for nested forms below are some helpful resources.
YouTube Video Instructions API dock for fields_for
Go Rails video for Form Nested Attributes and Fields For in Rails
Active Record Nested Attributes
Rails Guide: Active Record Associations

Issue getting ActiveRecord::StatementInvalid on new record

I'm tryint to save information but seems to be hard.
Here my controller /app/controllers/finance_management/voucher_controller.rb
class FinanceManagement::VoucherController < ActionController::Base
def new
#voucher = Voucher.new
end
def create
Voucher.create(params[:voucher])
end
def voucher_params
params.require(:voucher).permit(:voucher_num)
end
end
Here is my model /app/models/voucher.rb
class Voucher < ActiveRecord::Base
end
Here is my view /app/finance_management/voucher/new.html.erb
<%= form_for :obj_voucher, :url => { :controller => "finance_management/voucher", :action => "create" } do |f| %>
Number<%= f.text_field :voucher_num %>
<%= f.submit :submit %>
<% end %>
Here my routes.rb
Rails.application.routes.draw do
namespace :finance_management do
resources :voucher
end
match ':controller(/:action(/:id(.:format)))', via: [:get, :post]
end
Here my logs
Started POST "/finance_management/voucher" for 127.0.0.1 at 2016-01-17 21:00:39 -0500
Processing by FinanceManagement::VoucherController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"222222=", "voucher"=>{"voucher_num"=>"1111"}, "commit"=>"submit"}
(0.1ms) BEGIN
SQL (1.5ms) INSERT INTO `vouchers` (`created_at`, `updated_at`) VALUES ('2016-01-18 02:00:39', '2016-01-18 02:00:39')
Mysql2::Error: Field 'voucher_num' doesn't have a default value: INSERT INTO `vouchers` (`created_at`, `updated_at`) VALUES ('2016-01-18 02:00:39', '2016-01-18 02:00:39')
(0.2ms) ROLLBACK
Completed 500 Internal Server Error in 5ms (ActiveRecord: 1.8ms)
ActiveRecord::StatementInvalid (Mysql2::Error: Field 'voucher_num' doesn't have a default value: INSERT INTO `vouchers` (`created_at`, `updated_at`) VALUES ('2016-01-18 02:00:39', '2016-01-18 02:00:39')):
I tried several ways but cannot save information but got errors:
<%= form_for :user, :url => {:controller=>"finance_management/voucher",:action=>'create'} do |f|%>
<%= form_for #user, :url => {:controller=>"finance_management/voucher",:action=>'create'} do |f|%>
Also changed this:
def create
#voucher= Voucher.create(params[:voucher_params])
end
def voucher_params
params.require(:voucher).permit(:voucher_num)
end
The field is not saving
Mysql2::Error: Field 'voucher_num' doesn't have a default value: INSERT INTO `vouchers` (`created_at`, `updated_at`) VALUES ('2016-01-18 02:00:39', '2016-01-18 02:00:39')
Try:
def create
Voucher.create(voucher_params)
end
ActionController params can't be used in ActiveModel mass assignment directly, you have to use strong parameters instead.
For benefit of the doubt, here's how the code should be structured:
#config/routes.rb
namespace :finance_management do
resources :vouchers
end
#app/controllers/financial_management/vouchers_controller.rb
class FinanceManagement::VouchersController < ActionController::Base
def new
#voucher = Voucher.new
end
def create
#voucher = Voucher.new voucher_params
#voucher.save
end
private
def voucher_params
params.require(:voucher).permit(:voucher_num)
end
end
#app/views/finance_management/vouchers/new.html.erb
<%= form_for [:financial_management, #voucher] do |f| %>
<%= f.text_field :voucher_num, placeholder: "Number" %>
<%= f.submit %>
<% end %>

undefined method `name' for nil:NilClass rails with slug?

when I attempt to run my code, I got the error above.
I try to transform the url products with FriendlyId gem . It works but when I try to see my product, I got this error :
NoMethodError in Products#show
undefined method `name' for nil:NilClass
Request
Parameters:
{"name"=>"tablette-chocolat-guanaja"}
Here's my models/product.rb :
has_many :order_items, dependent: :destroy
extend FriendlyId
friendly_id :name, use: :slugged
before_save :update_slug
def update_slug
self.slug = name.parameterize
end
def to_param
slug
end
my products_controller.rb :
before_action :set_product, only:[:show]
def show
#order_item = current_order.order_items.new
end
private
def set_product
#product = Product.find_by_slug(params[:id])
end
my view/show.html.erb :
<%= render "product_show", product: #product, order_item: #order_item %>
and _product_show.html.erb :
<p ><%= product.name %></p>
<%= form_for order_item, remote: true do |f| %>
<%= f.number_field :quantity, value: order_item.quantity.to_i, class: "form-control", min: 1, max: 99 %>
<%= f.hidden_field :product_id, value: product.id %>
<p><%= currency_euro product.price %></p></br>
<p><%= image_tag product.image %></p>
<p><%= image_tag product.image_pres1 %></p>
# etc.
Any idea ?
EDIT
Here's the full error message :
Started GET "/products/tablette-chocolat-guanaja" for 127.0.0.1 at 2015-12-20 16:52:06 +0100
Processing by ProductsController#show as HTML
Parameters: {"name"=>"tablette-chocolat-guanaja"}
Product Load (0.4ms) SELECT `products`.* FROM `products` WHERE `products`.`active` = 1 AND `products`.`slug` IS NULL LIMIT 1
Rendered products/_product_show.html.erb (1.8ms)
Rendered products/show.html.erb within layouts/application (2.7ms)
Completed 500 Internal Server Error in 9ms (ActiveRecord: 0.4ms)
EDIT2
My fault, I added "resources :products, param: :name" in product's routes, sorry...
first you don't need to update manually slug, gem will do it for you just execute this code
Product.all.each(&:save)
also this
def set_product
#product = Product.find_by_slug(params[:id])
end
you can replace
def set_product
#product = Product.friendly.find(params[:id])
end
Hope it helps
probably because the creation of this product was before install friendly_id. Did you try this?: Profile.find_each(&:save) to rails console to save slugs to your old products?

Rails 4.1.8 "blog" posts not saving text or title when using strong parameters

I'm a long time lurker who's google-fu is failing him today. I've been learning to use Rails using a video series that was created before Rails 4 came out. As such, the lectures use the attr_accessibly mass assignments. I've been attempting to convert this to working strong parameters, but am having an issue actually saving info to my PostgreSQL database.
Basically, I should go to localhost:3000/new, add a title, body, and category, then submit. This goes off without a hitch, but my /posts (which lists all posts) shows the title as /posts/(whatever row number it's on), does not display the text, and only shows a date-time stamp when clicking on the post (url). Also, my database is only storing a post/row number.
Note: I am aware of things like ActiveAdmin, but would prefer to learn how to make/save posts manually before using such modules.
Here is the post controller:
class PostsController < ApplicationController
def index
#posts = Post.all
end
def show
#post = Post.find(params[:id])
end
def new
#post = Post.new
#category = Category.all
end
def create
#post = Post.create(post_params)
if #post.save
redirect_to posts_path, :notice => "Your post has been saved"
else
render "new"
end
end
def edit
end
def update
end
def destroy
end
private
def post_params
params.require(:post).permit(:title, :body, :category_id, :author_id)
end
end
Here is the html form:
<h1>Add New Post</h1>
<%= form_for #post do |f| %>
<p>
<%= f.label :title %><br />
<%= f.text_field :title %><br />
</p>
<p>
<%= f.label :body %><br />
<%= f.text_area :body %><br />
</p>
<p>
<%= f.select :category_id, Category.all.collect {|x| [x.name, x.id]}, {:include_blank=> "Select One"}%><br />
</p>
<p>
<%= f.submit "Add Post" %>
</p>
<% end %>
And finally, the posts.rb file:
class Post < ActiveRecord::Base
# Deprecated
# attr_accessible :title, :body, :category_id, :author_id
belongs_to :category
accepts_nested_attributes_for :category
end
I am no Ruby expert, so my first thought is that the .save method requires additional arguments when using strong_parameters. Adding (post_params) to the save method didn't seem to have an effect, and I haven't been able to determine my issue using the Ruby documentation. I'd prefer to do this the "right" way, as opposed to just using the protected_attributes gem to use a deprecated (seemingly less secure) method.
Thanks in advance for any assistance you may be able to offer. When I get rich, I'll buy you a Ferrari*.
*May or may not be a Hot Wheels replica.
Eidt 2: Here is what the server throws out when attempting to POST to my database. It mentions a mass assignment error, but I thought I wasn't using them (strong params instead). Pardon my newbiness:
Started POST "/posts" for 127.0.0.1 at 2015-03-11 18:28:13 -0700
Processing by PostsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"9Ybchogw5u+sYbZOFYZtbJbXBkWC5EuCIZNUmAKyAQI=", "post"=> {"title"=>"blahblahblah", "body"=>"blahblahblah", "category_id"=>"1"}, "commit"=>"Add Post"}
WARNING: Can't mass-assign protected attributes for Post: title, body, category_id
app/controllers/posts_controller.rb:18:in `create'
[1m[36m (0.0ms)[0m [1mBEGIN[0m
[1m[35mSQL (1.0ms)[0m INSERT INTO "posts" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", "2015-03-12 01:28:13.990971"], ["updated_at", "2015-03-12 01:28:13.990971"]]
[1m[36m (2.0ms)[0m [1mCOMMIT[0m
[1m[35m (0.0ms)[0m BEGIN
[1m[36m (0.0ms)[0m [1mCOMMIT[0m
Redirected to http://localhost:3000/posts
Completed 302 Found in 10ms (ActiveRecord: 3.0ms)
And the index view. This is a training exercise, so it's just text, no styling:
<h1>Blog Posts</h1>
<% #posts.each do |post| %>
<h3><%= link_to post.title, post %></h3>
<p><%= post.body %></p>
<% end %>
uncomment your request/permit line in post_params and actually add the param names into it
def post_params
params.require(:post).permit(:title, :body, :category_id, :author_id)
end
That is why you are getting no data in your posts - because you aren't getting any data out of params anymore.
If the permit/require line is causing a different bug for you - we will help you fix that, but commenting out the security measure is not the way to solve it.

Resources