In my app I want to create events that have nameand price
In these events I want to be able to add participants they have a first_nameand a salary
I am using the gem Cocoon for the nested forms
From the EventsController I am trying to create my participants...
I don't manage to get the right way to do this...
EventsController
class EventsController < ApplicationController
def new
#event = Event.new
#participant = Participant.new
end
def create
#event = Event.create(params_event)
if #event.save
#event.participants.create([{first_name: params[:first_name], salary: params[:salary], event_id: params[:id]}])
#binding.pry
redirect_to #event
end
end
def show
#event = Event.find(params[:id])
end
private
def params_event
params
.require(:event).permit(:name,:total_price,
participants_attributes: [:first_name, :salary, :_destroy ] )
end
end
I want that my create methode render an array of participants in the given event like this...
#event.participants.create!([john={first_name: "John", salary: 2000, event_id: params[:id]}, mike={first_name: "Mike", salary: 1400, event_id: params[:id]}])
Here is the result of binding.pry
8: def create
9: #event = Event.create(params_event)
10: if #event.save
11: #event.participants.create([{first_name: params[:first_name], salary: params[:salary], event_id: params[:id]}])
12: ##event.participants.create!([john={first_name: "John", salary: 2000, event_id: params[:id]}, mike={first_name: "Mike", salary: 1400, event_id: params[:id]}])
13: binding.pry
=> 14: redirect_to #event
15: end
16: end
[1] pry(#<EventsController>)>
[1] pry(#<EventsController>)> #event
=> #<Event:0x007f89f8013c98
id: 67,
name: "Rent a plane",
total_price: 3400,
created_at: Mon, 13 Mar 2017 01:18:24 UTC +00:00,
updated_at: Mon, 13 Mar 2017 01:18:24 UTC +00:00>
[2] pry(#<EventsController>)> #event.participants
Participant Load (0.2ms) SELECT "participants".* FROM "participants" WHERE "participants"."event_id" = $1 [["event_id", 67]]
=> [#<Participant:0x007f89f6773580
id: 30,
first_name: nil,
salary: nil,
created_at: Mon, 13 Mar 2017 01:18:24 UTC +00:00,
updated_at: Mon, 13 Mar 2017 01:18:24 UTC +00:00,
event_id: 67>]
If neeeded here are my models:
class Event < ApplicationRecord
has_many :participants, dependent: :destroy
accepts_nested_attributes_for :participants, reject_if: :all_blank, allow_destroy: true
end
class Participant < ApplicationRecord
belongs_to :event
end
And my forms:
new.html.erb
<%= simple_form_for #event do |f| %>
<%= f.input :name, label: "Event's name" %>
<%= f.input :total_price, label: "What is the total price" %>
<h2> Add your friends to share the bill</h2>
<%= f.simple_fields_for :participants do |participant| %>
<%= render "events/participants_fields", f: participant %>
<% end %>
<%= link_to_add_association 'add a friend', f, :participants, partial: "events/participants_fields", class:"btn btn-primary" %>
<%= f.button :submit %>
<% end %>
partial: _participants_fields.html.erb
<div class='nested-fields participant-background'>
<%= f.input :first_name, label: "Enter your friend's first name" %>
<%= f.input :salary, label: "Enter his/her monthly pay" %>
<%= link_to_remove_association "Remove this friend", f , class: "btn btn-danger btn-xs" %>
</div>
EDIT
These are my logs when I create an event with participants they aren't saved: ( I just kept the #event = Event.new(params_event)
tarted GET "/" for ::1 at 2017-03-13 13:55:20 +0100
ActiveRecord::SchemaMigration Load (7.0ms) SELECT "schema_migrations".* FROM "schema_migrations"
Processing by EventsController#new as HTML
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
Rendering events/new.html.erb within layouts/application
Rendered events/_participants_fields.html.erb (5.8ms)
Rendered events/new.html.erb within layouts/application (98.6ms)
Rendered shared/_navbar.html.erb (2.5ms)
Rendered shared/_flashes.html.erb (0.4ms)
Completed 200 OK in 408ms (Views: 358.0ms | ActiveRecord: 20.9ms)
Started POST "/events" for ::1 at 2017-03-13 13:55:48 +0100
Processing by EventsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"mMuWIZe06ofG6DCafY2EPrJouIcF4YzKKJx6Y9ct5XUZNHqIyvFY4l3dqiEnxC5NhQapvSnFDukgblJnHg+14g==", "event"=>{"name"=>"Rent a van", "total_price"=>"390"}, "commit"=>"Create Event"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
(0.1ms) BEGIN
SQL (11.9ms) INSERT INTO "events" ("name", "total_price", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["name", "Rent a van"], ["total_price", 390], ["created_at", 2017-03-13 12:55:48 UTC], ["updated_at", 2017-03-13 12:55:48 UTC]]
(6.0ms) COMMIT
(0.2ms) BEGIN
(0.2ms) COMMIT
Redirected to http://localhost:3000/events/93
Completed 302 Found in 25ms (ActiveRecord: 18.6ms)
Started GET "/events/93" for ::1 at 2017-03-13 13:55:48 +0100
Processing by EventsController#show as HTML
Parameters: {"id"=>"93"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
Event Load (0.4ms) SELECT "events".* FROM "events" WHERE "events"."id" = $1 LIMIT $2 [["id", 93], ["LIMIT", 1]]
Rendering events/show.html.erb within layouts/application
Participant Load (0.5ms) SELECT "participants".* FROM "participants" WHERE "participants"."event_id" = $1 [["event_id", 93]]
Rendered events/show.html.erb within layouts/application (3.1ms)
Rendered shared/_navbar.html.erb (3.3ms)
Rendered shared/_flashes.html.erb (0.8ms)
Completed 200 OK in 41ms (Views: 34.1ms | ActiveRecord: 1.5ms)
The problem was in my forms....
Cocoon needs special div
<div class="container">
<div class="col-xs-12">
<%= simple_form_for #event do |f| %>
<%= f.input :name, label: "Event's name" %>
<%= f.input :total_price, label: "What is the total price" %>
<h2> Add your friends to share the bill</h2>
<div id="participants">
<%= f.simple_fields_for :participants do |participant| %>
<%= render "events/participants_fields", f: participant %>
<% end %>
</div>
<div class="links">
<%= link_to_add_association 'add a friend', f, :participants, partial: "events/participants_fields", class:"btn btn-primary" %>
</div>
<%= f.button :submit %>
<% end %>
</div>
</div>
I cleaned the controller like so:
class EventsController < ApplicationController
def new
#event = Event.new
end
def create
#event = Event.new(params_event)
if #event.save
redirect_to event_path(#event)
end
end
def show
#event = Event.find(params[:id])
end
private
def params_event
params
.require(:event).permit(:name,:total_price, participants_attributes: [:first_name, :salary, :_destroy ] )
end
end
Related
All help/hints/debugging tips/thoughts are welcome, as I'm pretty much stuck for some time on this issue.
I have a 2-level deep nested form. The parameters of the 1st level are saving correctly (e.g. options_attributes), but unfortunately the parameters of the deepest form are not being sent to the controller (.e.g. option_prices_attributes is not shown at all in my parameters. I use the cocoon gem to create a dynamic nester form.
Interestingly,
(1) in my console I am able to create a 2-level deep object where also the option_price parameters are saving.
(2) when using :option_prices_attributes in the simple_field forms, they are being sent as parameters:
{"utf8"=>"✓", "_method"=>"patch", "authenticity_token"=>"P6oCZkZF8O+, "accommodation_category"=>{"options_attributes"=>{"1568305809712"=>{"name"=>"Option name", "_destroy"=>"false", "option_prices_attributes"=>{"name"=>"Option price", "_destroy"=>"0"}}}}, "commit"=>"Save", "park_id"=>"8", "id"=>"96"}
=> and consequently resulting in an error message in the terminal saying
no implicit conversion of Symbol into Integer
My models
class AccommodationCategory < ApplicationRecord
belongs_to :park
has_many :options, dependent: :destroy
accepts_nested_attributes_for :options, allow_destroy: true
end
class Option < ApplicationRecord
belongs_to :accommodation_category
has_many :option_prices, dependent: :destroy
accepts_nested_attributes_for :option_prices, allow_destroy: true
end
class OptionPrice < ApplicationRecord
belongs_to :option
end
Accommodation_categories_controller.rb
class AccommodationCategoriesController < ApplicationController
# skip_before_action :authenticate_user!
[...]
def update
#park = Park.find(params[:park_id])
#accommodation_category = #park.accommodation_categories.find(params[:id])
authorize #accommodation_category
#accommodation_category = #accommodation_category.update_attributes(accommodation_category_params)
end
def new_options
#accommodation_category = AccommodationCategory.find(params[:id])
#park = #accommodation_category.park
authorize #accommodation_category
#2nd level nesting
# #accommodation_category.options.build
#accommodation_category.options.each do |option|
option.option_prices.build
end
end
private
def accommodation_category_params
params.require(:accommodation_category).permit(:name, :description, :status, :persons_max, :persons_min, :persons_included, :accommodation_count, :enabled_accommodation_count, :thumb, :included_services, :photo,
options_attributes: [:name, :description, :_destroy,
option_prices_attributes: [:name, :price_type, :start_date, :end_date, :price, :duration, :duration_min, :duration_max, :backend_only, :weekend_extra, :_destroy]])
end
end
views/accommodation_categories/new_options.html.erb
<%= render 'options_new_form', park: #park%>
views/accommodation_categories/options_new_form.html.erb (1st level)
<%= simple_form_for [#park, #accommodation_category] do |f|%>
<h1> <%= #accommodation_category.name %> </h1>
<% #accommodation_category.options.each do |option| %>
<%= option %>
<% end %>
<%= f.simple_fields_for :options do |option| %>
<%= render 'option_fields', f: option %>
<% end %>
<div>
<%= link_to_add_association 'add option', f, :options %>
</div>
<%= f.submit "Save", class: "btn btn-primary" %>
<% end %>
views/accommodation_categories/option_fields.html.erb (2nd level)
<%= f.input :name %>
<%= f.check_box :_destroy %>
<%= link_to_remove_association "remove option", f %>
<%= f.simple_fields_for :option_prices do |option_price| %>
<%= render 'option_price_fields', f: option_price %>
<% end %>
<%= link_to_add_association 'add option price', f, :option_prices %>
views/accommodation_categories/option_price_fields.html.erb
<%= f.input :name %>
<%= f.check_box :_destroy %>
<%= link_to_remove_association "remove option price", f %>
The message in my terminal when sending the parameter to the controller is the following:
Processing by AccommodationCategoriesController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"QlROXu9ImP6GSvPJQgd7eVZtaWsiVT6myWzZEFIEtEulSrQmt75XVMEI/avKUzhjZaZG9Kj0Pmih6J/4UYO8IQ==", "accommodation_category"=>{"options_attributes"=>{"1568290804865"=>{"name"=>"option name", "_destroy"=>"false"}}}, "commit"=>"Save", "park_id"=>"8", "id"=>"93"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ /Users/xx/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/activerecord-5.2.3/lib/active_record/log_subscriber.rb:98
Park Load (0.4ms) SELECT "parks".* FROM "parks" WHERE "parks"."id" = $1 LIMIT $2 [["id", 8], ["LIMIT", 1]]
↳ app/controllers/accommodation_categories_controller.rb:29
AccommodationCategory Load (0.2ms) SELECT "accommodation_categories".* FROM "accommodation_categories" WHERE "accommodation_categories"."park_id" = $1 AND "accommodation_categories"."id" = $2 LIMIT $3 [["park_id", 8], ["id", 93], ["LIMIT", 1]]
↳ app/controllers/accommodation_categories_controller.rb:30
(0.2ms) BEGIN
↳ app/controllers/accommodation_categories_controller.rb:32
Option Create (0.3ms) INSERT INTO "options" ("accommodation_category_id", "name", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["accommodation_category_id", 93], ["name", "option name"], ["created_at", "2019-09-12 12:20:14.265335"], ["updated_at", "2019-09-12 12:20:14.265335"]]
↳ app/controllers/accommodation_categories_controller.rb:32
(0.7ms) COMMIT
↳ app/controllers/accommodation_categories_controller.rb:32
AccommodationCategory Load (0.2ms) SELECT "accommodation_categories".* FROM "accommodation_categories" WHERE "accommodation_categories"."park_id" = $1 AND "accommodation_categories"."id" = $2 LIMIT $3 [["park_id", 8], ["id", 93], ["LIMIT", 1]]
↳ app/controllers/accommodation_categories_controller.rb:45
Redirected to http://localhost:3000/accommodation_categories/93/new_discounts
Completed 302 Found in 14ms (ActiveRecord: 2.3ms)
None of your nested-fields partials seem to have a wrapper-class? Cocoon explicitly relies on a specific mark-up, this could cause e.g. the nested fields to appear to be inserted correctly, but inserted outside of the form and then of course never posted to the server/controller.
I am using the gem cocoon for nested attributes
I have a Product model that has many sizes.
Creating Product and its sizes works well
I can update the :size_name and/or the :quantity attributes if a size exist. And I can destroy a size. Good...
But I CANNOT add more size to and existing product.
Is there a solution to add size in an existing product?
In product model I have:
has_many :sizes, inverse_of: :product, dependent: :destroy
accepts_nested_attributes_for :sizes, reject_if: :all_blank, allow_destroy: true
in my products_controller.rb
def update
if #product.update_attributes(params_product)
respond_to do |format|
format.html {redirect_to admin_product_path(#product)}
format.js
end
else
render :edit
end
end
def params_product
params.require(:product).permit(
:id,
:title,
:ref,
:brand,
:description,
:buying_price,
:price,
:category_id,
:color,
{ attachments:[]},
sizes_attributes: [:id, :size_name, :quantity, :_destroy]
)
end
the edit form
<%= f.simple_fields_for :sizes do |size| %>
<%= render "size_fields", f: size %>
<% end %>
<div class="links">
<%= link_to_add_association "Ajouter une taille", f, :sizes, partial: "size_fields", class: "btn btn-secondary btn-lg" %>
</div>
the partial
<div class="nested-fields">
<div class="row">
<div class="col-12 col-md-4">
<%= f.input :size_name, label: false, placeholder: "Taille", autofocus: true %>
</div>
<div class="col-12 col-md-4">
<%= f.input :quantity, label: false, placeholder: "Quantité dans cette taille" %>
</div>
<div class="col-12 col-md-4">
<%= link_to_remove_association "supprimer cette taille", f, class: "btn btn-danger delete_size" %>
<hr>
</div>
</div>
</div>
If you need more code feel free to ask :)
UPDATE
Feature test accept update on existing size, but Capybara don't find the new fields to add a new size... (WIP)
Here are the logs when I am adding a new sier to an existing product
Started PATCH "/admin/products/26" for 127.0.0.1 at 2019-02-16 11:31:51 +0100
[1m[36mUser Load (0.4ms)[0m [1m[34mSELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2[0m [["id", 23], ["LIMIT", 1]]
↳ /Users/johan/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activerecord-5.2.2/lib/active_record/log_subscriber.rb:98
Processing by Admin::ProductsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"xG3tRqzIC0XP4quanEo2589zbsSiJcwICXjIt9ecndDsKjwYIDSBVZ4P62woGNgnCYHhBiM2jXmZlQjz9CK2fQ==", "product"=>{"category_id"=>"8", "brand"=>"Side Park", "title"=>"Jean", "color"=>"Bleu", "price"=>"30.0", "buying_price"=>"5.0", "ref"=>"SP02", "description"=>"Paalablalntalon blab", "sizes_attributes"=>{"0"=>{"size_name"=>"L", "quantity"=>"3", "_destroy"=>"false", "id"=>"97"}, "1"=>{"size_name"=>"S", "quantity"=>"3", "_destroy"=>"false", "id"=>"95"}, "2"=>{"size_name"=>"M", "quantity"=>"4", "_destroy"=>"false", "id"=>"96"}}}, "commit"=>"Mettre à jour l'article", "id"=>"26"}
[1m[36mProduct Load (0.3ms)[0m [1m[34mSELECT "products".* FROM "products" WHERE "products"."id" = $1 LIMIT $2[0m [["id", 26], ["LIMIT", 1]]
↳ app/controllers/admin/products_controller.rb:62
[1m[35m (0.1ms)[0m [1m[35mBEGIN[0m
↳ app/controllers/admin/products_controller.rb:49
[1m[36mSize Load (0.5ms)[0m [1m[34mSELECT "sizes".* FROM "sizes" WHERE "sizes"."product_id" = $1 AND "sizes"."id" IN ($2, $3, $4)[0m [["product_id", 26], ["id", 97], ["id", 95], ["id", 96]]
↳ app/controllers/admin/products_controller.rb:49
[1m[35m (0.2ms)[0m [1m[35mCOMMIT[0m
↳ app/controllers/admin/products_controller.rb:49
Redirected to http://localhost:3000/admin/products/26
Completed 302 Found in 13ms (ActiveRecord: 1.0ms)
I added this to my product controller, I can now add sizes to an existing Product
def new
#product = Product.new
#product.sizes.build
end
def edit
#product.sizes.build
end
My file_field tag is only passing the file name, not the file itself to the controller.
In my User model:
mount_uploader :avatar, AvatarUploader
validate :avatar_size
...
def avatar_size
errors.add :avatar, "should be less than 5MB" if avatar.size > 5.megabytes
end
The AvatarUploader:
class AvatarUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
process resize_to_limit: [50, 50]
if Rails.env.production?
storage :fog
else
storage :file
end
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
def extension_white_list
%w(jpg, jpeg, gif, png)
end
end
And the User Controller:
before_action :logged_in_user, only: [:index, :edit, :update, :destroy, :following, :followers]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: [:destroy]
skip_before_action :verify_authenticity_token, only: [:create]
...
def update
#user = User.find(params[:id])
if #user.update_attributes user_params
flash[:success] = "Profile updated"
redirect_to user_path #user
else
render 'edit'
end
end
...
def user_params
params.require(:user).permit :name, :email, :password, :password_confirmation, :avatar
end
def correct_user
#user = User.find params[:id]
redirect_to root_path unless #user == current_user
end
def admin_user
redirect_to root_path unless current_user.admin?
end
The form in the view:
<%= form_for(#user) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<% if #user.avatar? %>
<%= image_tag #user.avatar.url, size: "50x50" %>
<% else %>
<%= image_tag "stock_ava.png", size: "50x50" %>
<% end %>
<%= f.file_field :avatar, accept: 'image/jpeg,image/gif,image/png' %>
<%= f.submit yield(:button_text), class: "btn btn-primary" %>
<% end %>
In the console I can see the attribute come in as "FinnJake.png", rather than the appropriate ActionDispatch.
Processing by UsersController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"h9tD/g2M1zJ5L4cnysIQwHCWxDdhhhJR1I9a9+FAWeT9aTXPte16Uyn8GvI4w23klfvYtTaLCDrGVonHfmFUag==", "user"=>{"name"=>"Example User", "email"=>"example#user.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "avatar"=>"FinnJake.png"}, "commit"=>"Save Changes", "id"=>"1"}
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
(0.1ms) begin transaction
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) AND ("users"."id" != ?) LIMIT ? [["email", "bexample#user.com"], ["id", 1], ["LIMIT", 1]]
SQL (1.1ms) UPDATE "users" SET "avatar" = ?, "updated_at" = ? WHERE "users"."id" = ? [["avatar", nil], ["updated_at", "2017-10-02 14:50:41.226691"], ["id", 1]]
(142.3ms) commit transaction
Redirected to https://rubyonrails-xxx.c9users.io/users/1
Completed 302 Found in 155ms (ActiveRecord: 144.1ms)
I'm very new to rails, but I cannot figure out why the file_field isn't uploading a file. I can see in the browser that I end up with this tag:
<input accept="image/jpeg,image/gif,image/png" type="file" name="user[avatar]" id="user_avatar">
Which looks right to me. I also cannot seem to manually update that column through the rails console, though I get no errors:
2.4.0 :002 > user = User.first
User Load (0.5ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
=> #<User id: 1, name: "Example User", email: "example#user.com", created_at: "2017-10-02 14:32:22", updated_at: "2017-10-02 16:35:55", password_digest: "$2a$10$sww7gItks0mYz6xz8zV.QOIjr6to/o5Jytjmuo0wxny...", remember_digest: nil, admin: true, activation_digest: "$2a$10$W396r7A8aZaii3DC7b3W7u70etSmXh3MORMIoWtcSHo...", activated_at: "2017-10-02 14:32:22", reset_digest: nil, reset_sent_at: nil, avatar: nil>
2.4.0 :003 > user.update avatar: "String"
(0.2ms) begin transaction
User Exists (0.4ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) AND ("users"."id" != ?) LIMIT ? [["email", "example#user.com"], ["id", 1], ["LIMIT", 1]]
SQL (2.4ms) UPDATE "users" SET "updated_at" = ?, "avatar" = ? WHERE "users"."id" = ? [["updated_at", "2017-10-02 17:27:06.097363"], ["avatar", nil], ["id", 1]]
(11.4ms) commit transaction
=> true
Thanks in advance for any advice you can provide.
You should use multipart form like:
form_for(#user, html: { multipart: true }) do ...
See http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-file_field
Found the problem. See this comment
Essentially, the form in the partial was being wrapped by another form which had a different encoding type.
I am stumped as to why this isn't working. I have a review system, and a link to create a new review passing 2 attributes; user_id and gigid. Both id's are being generated as integers but when I save the review, the gigid saves as 'nil' rather than the integer.
View:
<%= link_to 'Click here to rate this user', new_user_review_path(:user_id => request.user.id, :gigid => request.gig.id) %>
Here both ID's are set correctly (according to server output). User_id is the user which is to be reviewed (works correctly) and gigid is a reference for post validation. (works correctly until I try and save)
_form :
<%= simple_form_for([#user, #user.reviews.build]) do |f| %>
<div id="rating-form">
<label>Rating</label>
</div>
<%= f.input :comment %>
<%= f.button :submit %>
<% end %>
Controller:
def new
if user_signed_in?
#review = current_user.reviews.new
else
redirect_to(root_url)
flash[:danger] = "You must log in to rate a user"
end
end
def create
#review = #user.reviews.new review_params
#review.reviewed_id = current_user.id
if #review.save
redirect_to user_path(#user)
else
render 'new'
end
end
private
def review_params
params.require(:review).permit(:rating, :comment, :gigid, :user_id)
end
end
Server output when clicking link:
Started GET "/users/21/reviews/new?gigid=17&locale=en" for 127.0.0.1 at 2016-01-13 18:08:26 +0100
Processing by ReviewsController#new as HTML
Parameters: {"gigid"=>"17", "locale"=>"en", "user_id"=>"21"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 21]]
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT 1 [["id", 1]]
Rendered reviews/_form.html.erb (4.9ms)
I then add a comment and a rating and click create.
Server output when clicking create:
Started POST "/users/21/reviews?locale=en" for 127.0.0.1 at 2016-01-13 18:10:44 +0100
Processing by ReviewsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"RToZwvodvXxQp1/spEhgemO/oi+4rof8jREuLw27CcEsEBqFWGLeYne1CgH3kvWu9OzAV+bHvOk9g9nq8JMPnw==", "review"=>{"rating"=>"5", "comment"=>"sdfsdfdd"}, "commit"=>"Create Review", "locale"=>"en", "user_id"=>"21"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 21]]
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT 1 [["id", 1]]
(0.1ms) begin transaction
SQL (0.7ms) INSERT INTO "reviews" ("rating", "comment", "user_id", "reviewed_id", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?, ?) [["rating", 5], ["comment", "sdfsdfdd"], ["user_id", 21], ["reviewed_id", 1], ["created_at", "2016-01-13 17:10:44.675386"], ["updated_at", "2016-01-13 17:10:44.675386"]]
(93.6ms) commit transaction
I notice that 'gigid' is being completely ignored here, but EVERYTHING else saves fine.
I don't understand why one attribute saves and the other doesn't, any insight would be greatly appreciated!
Your form doesn't have anything for gigid to send it to create action. You can use hidden field in the form like below
<%= simple_form_for([#user, #user.reviews.build]) do |f| %>
<div id="rating-form">
<label>Rating</label>
</div>
<%= f.input :comment %>
<%= f.input :gigid, :as => :hidden, :input_html => { :value => params[:gigid] }
<%= f.button :submit %>
<% end %>
There is no gigid in review params. Look carefully on your params when you are performing POST: "review"=>{"rating"=>"5", "comment"=>"sdfsdfdd"}. Just review and comment. And after it you are doing:
params.require(:review).permit(:rating, :comment, :gigid, :user_id)
What I want to do is for users to select at least one image (up to 3 images) and to enter f.text_area :content in \views\shared\ _article_form.html.erb.
I added a custom validation check_for_at_least_image in \models\article.rb.
It works (error message is displayed) only in create action, but it doesn't work in update action.
How can I check no image and display error message in update action.
article has many photo.
The logs are as followings.
\log/development.log
no image when new create (error message was displayed as I expect)
Started POST "/articles" for 127.0.0.1 at 2014-09-13 10:40:49 +0900
Processing by ArticlesController#create as HTML
Parameters: {"utf8"=>"笨・, "authenticity_token"=>"xxxx=", "article"=>{"category_id"=>"1379", "photos_attributes"=>{"0"=>{"article_id"=>""}, "1"=>{"article_id"=>""}, "2"=>{"article_id"=>""}}, "content"=>"test"}, "commit"=>"逋サ骭イ縺吶k"}
[1m[35mUser Load (0.0ms)[0m SELECT "users".* FROM "users" WHERE "users"."remember_token" = 'xxxx' LIMIT 1
[1m[36m (0.0ms)[0m [1mbegin transaction[0m
[1m[35m (0.0ms)[0m rollback transaction
[1m[36mCategory Load (1.0ms)[0m [1mSELECT "categories".* FROM "categories" WHERE "categories"."id" = ? LIMIT 1[0m [["id", 1379]]
Rendered shared/_error_messages.html.erb (1.0ms)
Rendered shared/_article_form.html.erb (8.0ms)
Rendered articles/new.html.erb within layouts/application (10.0ms)
Rendered layouts/_header.html.erb (1.0ms)
Rendered layouts/_footer.html.erb (0.0ms)
Completed 200 OK in 63ms (Views: 51.0ms | ActiveRecord: 1.0ms)
delete all(three) images (no error message was displayed)
Started PATCH "/articles/40" for 127.0.0.1 at 2014-09-13 11:10:00 +0900
Processing by ArticlesController#update as HTML
Parameters: {"utf8"=>"笨・, "authenticity_token"=>"xxxx=", "article"=>{"category_id"=>"1379", "photos_attributes"=>{"0"=>{"article_id"=>"40", "_destroy"=>"1", "id"=>"132"}, "1"=>{"article_id"=>"40", "_destroy"=>"1", "id"=>"133"}, "2"=>{"article_id"=>"40", "_destroy"=>"1", "id"=>"134"}}, "content"=>"test"}, "commit"=>"譖エ譁ー縺吶k", "id"=>"40"}
[1m[35mUser Load (0.0ms)[0m SELECT "users".* FROM "users" WHERE "users"."remember_token" = 'xxxx' LIMIT 1
[1m[36mArticle Load (0.0ms)[0m [1mSELECT "articles".* FROM "articles" WHERE "articles"."user_id" = ? AND "articles"."id" = 40 ORDER BY created_at DESC LIMIT 1[0m [["user_id", 1]]
[1m[35mArticle Load (1.0ms)[0m SELECT "articles".* FROM "articles" WHERE "articles"."id" = ? ORDER BY created_at DESC LIMIT 1 [["id", "40"]]
[1m[36m (0.0ms)[0m [1mbegin transaction[0m
[1m[35mPhoto Load (1.0ms)[0m SELECT "photos".* FROM "photos" WHERE "photos"."article_id" = ? AND "photos"."id" IN (132, 133, 134) [["article_id", 40]]
[1m[36m (0.0ms)[0m [1mSELECT COUNT(*) FROM "photos" WHERE "photos"."article_id" = ?[0m [["article_id", 40]]
[1m[35mSQL (1.0ms)[0m DELETE FROM "photos" WHERE "photos"."id" = ? [["id", 132]]
[1m[36mSQL (0.0ms)[0m [1mDELETE FROM "photos" WHERE "photos"."id" = ?[0m [["id", 133]]
[1m[35mSQL (0.0ms)[0m DELETE FROM "photos" WHERE "photos"."id" = ? [["id", 134]]
[1m[36m (4.0ms)[0m [1mcommit transaction[0m
Redirected to http://localhost:3000/users/1
Completed 302 Found in 26ms (ActiveRecord: 7.0ms)
photos table
sqlite> .schema photos
CREATE TABLE "photos" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "articl_id" integer, "image" varchar(255), "created_at" datetime, "updated_at" datetim);
\models\article.rb
# encoding: utf-8
class Article < ActiveRecord::Base
belongs_to :user
belongs_to :category
has_many :photos, dependent: :destroy
accepts_nested_attributes_for :photos, reject_if: :all_blank, allow_destroy: true
default_scope -> { order('created_at DESC') }
validates :content, presence: true, length: { maximum: 140 }
validates :user_id, presence: true
validates :category_id, presence: true
validate :check_for_at_least_image
def build_images
(3 - self.photos.size).times {self.photos.build}
end
def check_for_at_least_image
errors.add(:image, "select...") if self.photos.size <= 0
end
end
\models\photo.rb
class Photo < ActiveRecord::Base
belongs_to :article
mount_uploader :image, ImageUploader
end
\view\articles\edit.html.erb
<div class="row">
<div class="span8">
<%= render 'shared/article_form' %>
</div>
</div>
\view\shared\ _article_form.html.erb
<%= form_for(#article) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.hidden_field :category_id %>
<%= f.fields_for :photos do |p| %>
<%= p.hidden_field :article_id %>
<div class="photo">
<% if p.object.image and p.object.image.file %>
<%= image_tag p.object.image.thumb.url %>
<p>article:<%= #article.id %></p>
<p>photo:<%= p.object.id %></p>
<%= p.hidden_field :image_cache if p.object.image_cache %>
<label><%= p.check_box :_destroy %>delete</label>
<% end %>
<%= p.file_field :image %>
</div>
<% end %>
<%= f.text_area :content, placeholder: "Enter content..." %>
</div>
<%= f.submit class: "btn btn-large btn-primary" %>
<% end %>
\controllers\articles_controller.rb
class ArticlesController < ApplicationController
before_action :signed_in_user, only: [:create, :destroy]
before_action :correct_user, only: [:update, :destroy]
.
.
def new
#article = Article.new
#category = Category.find(params[:category])
#article.category_id = #category.id
3.times { #article.photos.build }
end
def create
#article = current_user.articles.build(article_params)
if #article.save
flash[:success] = "article created!"
redirect_to current_user #root_url
else
#article.build_images
render 'new'
end
end
.
.
def edit
#article = Article.find(params[:id])
#article.build_images
end
def update
#article = Article.find(params[:id])
if #article.update(article_params)
redirect_to current_user
else
render 'edit'
end
end
def destroy
#article.destroy
redirect_to root_url
end
private
def article_params
params.require(:article).permit(:content, :category_id, photos_attributes: [:id, :article_id, :image, :image_cache, :_destroy])
end
.
.
end
Just looked at your question and was thinking that may be your validations are running before the destory call on child objects and searched around a bit and found this post, looks like i was right about validations being run before destory. Just going to post pointer related to your question
The problem here is that accepts_nested_attributes_for call destroy for child objects AFTER validation of the parent object. So the user is able to delete an image. Of course, later, when the user will try to edit an article, he/she will get an error – “Select at least one image.”.
Fix:
accepts_nested_attributes_for :photos, reject_if: proc { |attributes| attributes['image'].blank? } , allow_destroy: true #as discussed in your other question you have to use proc to solve your update problem
validate :check_for_at_least_image
def check_for_at_least_image
errors.add(:image, "select...") if photos.reject(&:marked_for_destruction?).size <= 0
end