Rails Validation Failing for Create Action - ruby-on-rails

So I'm trying to get my new/create actions working with form_for for my resource 'project' but the create form in 'new.html.erb' seems to think that the parameter 'version' is blank when I submit it, even though I am setting it equal to 1.
Projects Controller
def new
#project = current_user.projects.build if user_signed_in?
#project.version = 1
#project.unique_id = rand(1000000)
while(Project.find_by_unique_id(#project.unique_id) != nil)
#project.unique_id = rand(1000000)
end
end
def create
#project = current_user.projects.build(project_params)
if #project.save
flash[:success] = "Project created!"
redirect_to user_url(current_user.username)
else
render 'new'
end
end
private
def project_params
params.require(:project).permit(:description, :name, :media, :content_type, :file_size, :unique_id, :verison)
end
end
New.html.erb
<%= form_for(#project, html: {multipart: true}) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div>
<%= f.label :name %>
<%= f.text_field :name, maxlength: 50, class: 'form-control' %>
</div>
<div>
<%= f.label :description %>
<%= f.text_area :description, placeholder: "What is it all about?", class: 'form-control' %>
</div>
<%= f.file_field :media, accept: 'image/jpeg,image/gif,image/png,audio/mpeg,audio/vnd.wave' %>
<%= f.hidden_field :version %>
<%= f.hidden_field :unique_id %>
<%= f.submit "Create Project", class: "btn btn-primary" %>
<% end %>
Project Model
class Project < ActiveRecord::Base
belongs_to :user
has_many :comments, dependent: :destroy
validates :user_id, presence: true
validates :name, presence: true, length: { maximum: 50 }
validates :unique_id, presence: true
validates :version, presence: true
end
Thanks so much in advance!

I would set it equal to one in the form as a hidden field like this:
<div>
<%= f.hidden_field :version, value: 1 class: 'form-control' %>
</div>
I would also get rid of #project.version = 1 in your project new action.

Figured it out thanks to JTG. There appeared to be a typo in my strong params which was causing 'version' to be filtered out and thus not passed through.
Thanks everyone!

Related

Triple-nested form with Rails form_with and strong parameters

I'm running Rails 6.0.0.rc1 and having trouble getting a triple nested form working. I have products that have options that have option_values. So a Product might have an option called "Color" and an Option Value named "Red." I'd like to create all of these in a classic nested form in the Product form.
The form works and I can save Product with an Option, but not the Option Value on submit. I'm not sure why it's not working when I try to embed fields_for Option Values inside the fields_for Options.
What am I doing wrong here? I feel like I'm missing something obvious, but can't figure it out. (Probably not relevant, but note that I need to scope each object to account_id and my User has_one :account that's the reason for the hidden field.)
Here is my product model:
class Product < ApplicationRecord
belongs_to :account
has_many :options, dependent: :destroy
accepts_nested_attributes_for :options, allow_destroy: true
validates :account_id, presence: true
validates :name, presence: true
end
Option model:
class Option < ApplicationRecord
belongs_to :account
belongs_to :product
has_many :option_values, dependent: :destroy
accepts_nested_attributes_for :option_values, allow_destroy: true
validates :account_id, presence: true
validates :name, presence: true
end
OptionValue model:
class OptionValue < ApplicationRecord
belongs_to :account
belongs_to :option
validates :account_id, presence: true
validates :name, presence: true
end
Here's the Product form:
<%= form_with(model: product, local: true) do |f| %>
<%= f.fields_for :options do |options_form| %>
<fieldset class='form-group'>
<%= options_form.hidden_field :account_id, value: current_user.account.id %>
<%= options_form.label :name, 'Option' %>
<%= options_form.text_field :name, class: 'form-control' %>
</fieldset>
<%= f.fields_for :option_values do |values_form| %>
<fieldset class='form-group'>
<%= values_form.label :name, 'Value' %>
<%= values_form.text_field :name, class: 'form-control' %>
</fieldset>
<% end %>
<% end %>
<% end %>
ProductsController:
class ProductsController < ApplicationController
def new
#product = Product.new
#product.options.build
end
def create
#account = current_user.account
#product = #account.products.build(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: 'Product was successfully created.' }
else
format.html { render :new }
end
end
end
private
def product_params
params.require(:product).permit(
:account_id, :name,
options_attributes: [
:id, :account_id, :name, :_destroy,
option_values_attributes:[:id, :account_id, :name, :_destroy ]
]
)
end
end
At first, you need to change the form to nest option_values inside option, and add account_id field to option values:
<%= form_with(model: product, local: true) do |f| %>
<%= f.fields_for :options do |options_form| %>
<fieldset class='form-group'>
<%= options_form.hidden_field :account_id, value: current_user.account.id %>
<%= options_form.label :name, 'Option' %>
<%= options_form.text_field :name, class: 'form-control' %>
</fieldset>
<%= options_form.fields_for :option_values do |values_form| %>
<fieldset class='form-group'>
<%= values_form.hidden_field :account_id, value: current_user.account.id %>
<%= values_form.label :name, 'Value' %>
<%= values_form.text_field :name, class: 'form-control' %>
</fieldset>
<% end %>
<% end %>
<% end %>
Also you need to build nested records in the controller. Another option is to build them dynamically via javascript (look at the cocoon gem, for example). To build 3 options with 3 values each:
def new
#account = current_user.account
# it is better to create associated product
#product = #account.products.new
3.times do
option = #product.options.build
3.times { option.option_values.build }
end
end
Update:
Because I was trying to follow along this Nested Form Railscast, the biggest problem was that I didn't realize that the version Ryan Bates has working is on "Edit", not "New", so I added the Products, Options, and Values via the console and got the form working with this code:
_form.html.erb
<%= f.fields_for :options do |builder| %>
<%= render 'option_fields', f: builder %>
<% end %>
_option_fields.html.erb
<fieldset class='form-group'>
<%= f.hidden_field :account_id, value: current_user.account.id %>
<%= f.label :name, 'Option' %>
<%= f.text_field :name, class: 'form-control' %>
<br>
<%= f.check_box :_destroy, class: 'form-check-input' %>
<%= f.label :_destroy, 'Remove Option' %>
<small id="optionHelp" class="form-text text-muted">
(e.g. "Size" or "Color")
</small>
<%= f.fields_for :option_values do |builder| %>
<%= render 'option_value_fields', f: builder %>
<% end %>
</fieldset>
_option_value_fields.html.erb
<fieldset class='form-group'>
<%= f.hidden_field :account_id, value: current_user.account.id %>
<%= f.label :name, 'Value' %>
<%= f.text_field :name, class: 'form-control' %>
<br>
<%= f.check_box :_destroy, class: 'form-check-input' %>
<%= f.label :_destroy, 'Remove Value' %>
<small id="optionValueHelp" class="form-text text-muted">
(e.g. "Small, Medium, Large" or "Red, Green, Blue")
</small>
</fieldset>
Also, the only difference with the Railscast is using strong parameters in the controller, so you'll just need to nest them like this:
ProductsController
def product_params
params.require(:product).permit(:account_id, :name, options_attributes [:id, :account_id, :name, :_destroy, option_values_attributes: [:id, :account_id, :name, :_destroy]])
end
OptionsController
def option_params
params.require(:option).permit(:account_id, :name, option_values_attributes [:id, :account_id, :name, :_destroy])
end
OptionValuesController
def option_value_params
params.require(:option_value).permit(:account_id, :option_id, :name)
end
Rather than building the nested objects in the controller I'm going to do it with Javascript as in the Railscast episode or with the Cocoon gem as Vasilisa suggested in her answer.
Just wanted to share the code that actually ended up working in case someone else runs into similar problems. I think the Railscast, though old, is still a great introduction to nested forms in Rails, but you just have to be aware of the changes required to use form_with and strong parameters. Big thanks to Vasilisa for helping me figure this out.
The main "gotchas" that you need to look out for when following the Rails Nested Form Railscast are this:
form_with has some different syntax than the older rails form_tag
Make sure you don't have any typos or name problems when creating your form blocks because these are nested twice
Same with nesting parameters in your controllers, just be aware of your syntax and typos
Be aware that Ryan Bates is demoing with data that wasn't added through the form he's building so if you'd like to follow along you'll need to create some data in the console
With strong parameters you'll have to explicitly list :_destroy as a parameter in order for his "Remove" checkboxes to work

My submit button is not working on my form_for in Rails - no error message

Hello I am back for my second question.
My submit button on my form_for does not do anything when I click on it. I did have an error message in the console that said 'Unpermitted parameter: :photo_cache' however when I saw this I permitted the 'photo_cache' in the params in my controller, BUT the submit button on my form still doesn't work.
Context: I am trying to create a hairdressers which has the following params: name, description, location, photo and address.
Any help would be appreciated, thanks!
My form:
<%= simple_form_for(#hairdresser) do |f| %>
<%= f.error_notification %>
<%= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present? %>
<!-- [...] -->
<div class="form-inputs">
<%= f.input :name %>
</div>
<div class="form-inputs">
<%= f.input :address %>
</div>
<div class="form-inputs">
<%= f.input :location %>
</div>
<div class="form-inputs">
<%= f.input :description %>
</div>
<div class="form-inputs">
<%= f.input :photo %>
<%= f.input :photo_cache, as: :hidden %>
</div>
<div class="form-actions">
<%= f.button :submit, label: "Submit Form", class: "btn btn-primary" %>
</div>
<!-- [...] -->
<% end %>
My controller:
class HairdressersController < ApplicationController
def index
#hairdressers = Hairdresser.all
end
def show
#hairdresser = Hairdresser.find(params[:id])
end
def new
#hairdresser = Hairdresser.new
end
def create
#hairdresser = Hairdresser.new(hairdresser_params)
# #hairdresser.save ? (redirect_to hairdresser_path(#hairdresser)) : (render 'new')
if #hairdresser.save
redirect_to hairdresser_path(#hairdresser)
else
render 'new'
end
end
def edit
#hairdresser = Hairdresser.find(params[:id])
end
def update
#hairdresser = Hairdresser.find(params[:id])
end
def destroy
#hairdresser = Hairdresser.find(params[:id])
end
end
private
def hairdresser_params
params.require(:hairdresser).permit(:name, :address, :photo, :location, :description, :photo_cache)
end
My model:
class Hairdresser < ApplicationRecord
belongs_to :user
validates :name, presence: true
validates :location, presence: true
validates :description, presence: true
validates :location, presence: true
mount_uploader :photo, PhotoUploader
end
Hi I actually found an answer to this online. Here is the answer for anyone who has lost time with this issue! :
"If you're on Rails 5, you'll need to update your user association to:
belongs_to :user, optional: true"

Unchangable checkbox

I've been struggling with this issue for 2 days now and I think I'm slowly starting to lose my mind.
I'm trying to update boolean 'schedule_display' for 'profiles' table in my nested form. Everything except this checkbox works just fine. At the present state html looks like this:
<%= nested_form_for #profile, html: { multipart: true } do |f| %>
<span class="picture">
<%= f.file_field :picture, accept: 'image/jpeg,image/gif,image/png' %>
</span>
<%= f.label :description %>
<%= f.text_field :description %>
<%= f.label :schedule_display %>
<%= f.check_box :schedule_display, {}, "true", "false" %>
<%= f.fields_for :buttons %>
<%= f.link_to_add "Add a button", :buttons %>
<%= f.submit "Save changes" %>
<% end %>
Parameters after submit look fine I think:
profile"=>{"description"=>"Dolor et exercitationem.", "schedule_display"=>"true", ...
Schedule_display is also in permited params in the right place:
params.require(:profile).permit(:id, :description, :picture,
:schedule_display, buttons_attributes: [
Corresponding part of Profile model looks like this:
class Profile < ActiveRecord::Base
belongs_to :user
has_many :buttons, :dependent => :destroy
accepts_nested_attributes_for :buttons,
reject_if: proc { |attributes| attributes['user_website_url'].blank? },
:allow_destroy => true
mount_uploader :picture, PictureUploader
attr_accessor :schedule_display
validates :description, presence: true, length: { maximum: 500 }
and update method is simply:
def update
#user = User.find(params[:id])
#profile = #user.profile
if #profile.update_attributes(profile_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
I've tried doing something like this:
if #profile.update_attributes(profile_params)
params[:profile][:schedule_display] == '1' ?
profile.turn_schedule_on : profile.turn_schedule_off
with these turn_schedule_ functions and simple update_attribute(), but it didn't work either.
Why isn't it working?

Rails update model wont work : No route matches, missing required keys: [:id]

I just started working on my second Rails project and am having some trouble with something that seems pretty basic. I am completely confounded though.
I've set up two models, User and Profile. A Profile belongs_to a User and the User has_one Profile.
I am setting up the edit form for a Profile right now and cannot get past this. The edit form loads correctly and inserts the data in the database but when I submit the form I get the following error
No route matches {:action=>"show", :controller=>"profiles", :format=>nil, :id=>#<Profile id: 7, user_id: 7, content: "Fill this in", created_at: "2015-01-08 19:08:22", updated_at: "2015-01-08 19:08:22", title: "fooobar", department: "one bar", school: "dsadsa", education: "two bar", academic_focus: "my bar is broken", website: "who bar is bokrbne">} missing required keys: [:id]
My models look like this
Class User < ActiveRecord::Base
has_one :profile, :class_name => 'Profile', :foreign_key => 'user_id'
attr_accessor :remember_token
end
class Profile < ActiveRecord::Base
belongs_to :user, :class_name => "User", :foreign_key => 'user_id'
attr_accessor :id
validates :content, presence: true, length: {maximum: 5000}
validates :user_id, presence: true
end
This is the form. I've tried various ways of adding a url: parameter to form_for but had no luck.
<%= form_for #profile do |f| %>
<%= f.label :title, class: 'col-lg-3 control-label' %>
<%= f.text_field :title, class: 'form-control' %>
<%= f.label :department, class: 'col-lg-3 control-label' %>
<%= f.text_field :department, class: 'form-control' %>
<%= f.label :school, class: 'col-lg-3 control-label' %>
<%= f.text_field :school, class: 'form-control' %>
<%= f.label :education, class: 'col-lg-3 control-label' %>
<%= f.text_field :education, class: 'form-control' %>
<%= f.label :academic_focus, class: 'col-lg-3 control-label' %>
<%= f.text_field :academic_focus, class: 'form-control' %>
<%= f.label :website, class: 'col-lg-3 control-label' %>
<div class="col-lg-8">
<%= f.text_field :website, class: 'form-control' %>
<%= f.hidden_field :id %>
<%= f.submit "Save changes", class: "btn btn-primary" %>
<% end %><!-- End of Form -->
Finally, here is the controller I am using.
class ProfilesController < ApplicationController
include ApplicationHelper
def index
# #profiles = Profile.limit(5)
#profiles = Profile.limit(15)
end
def show
#profile = Profile.find(params[:id])
end
def new
end
def create
#profile = Profile.new(profile_params)
if #profile.save
#successful save
flash[:success] = "Look at that. Ur Profile was ssaved"
redirect_to #profile
else
#unsuccesful
render 'new'
end
end
def edit
#user = current_user
#profile = Profile.find(#user.id)
end
def update
#profile = Profile.find(params[:id])
if #profile.update_attributes!(profile_params)
flash[:notice] = 'The User is successfully updated!'
redirect_to edit_profile_path
else
flash[:error] = #profile.errors.full_messages[0]
redirect_to edit_user_path
end
end
def view
#profile = Profile.find(params[:id])
end
private
def profile_params
params.require(:profile).permit(:id, :user_id, :content, :title, :department, :school, :education, :academic_focus, :website)
end
end
It is weird because I am using pretty much the exact same code I use for editing/creating users, but it isn't working. I figure it has something to do with relationship in the database.
Any insight would be much appreciated.
Thanks.
EDIT #1
Here's a photo of what happens when I submit the form
EDIT #2
If that image is too small here is a better one
http://i.imgur.com/Wu3v7Gu.png
EDIT #3
I changed the form_for arguments to
<%= form_for #profile, url: profile_update_path(#profile) do |f| %>
Now when the form is submitted I get this error
undefined method `profile_update_path' for #<#<Class:0x007fe1ae0e5618>:0x007fe1aca90458>
Remove the attr_accessor :id statement, it's likely interfering with the ActiveRecord default accessors in some way.

Rails nested Attributes of join table won't be saved

Nested attributes of join model won't be saved. relation id's seems to be missing. The following error messages are added when the fields get validated:
* Assigned projects user can't be blank
* Assigned projects project can't be blank
The submitted params look like this ( <%= debug(params) %> )
--- !map:ActionController::Parameters
utf8: "\xE2\x9C\x93"
authenticity_token: HrF1NHrKNTdMMFwOvbYFjhJE1ltlKbuz2nsfBYYBswg=
project: !map:ActionController::Parameters
name: Peter Pan
assigned_projects_attributes: !map:ActiveSupport::HashWithIndifferentAccess
"0": !map:ActiveSupport::HashWithIndifferentAccess
position: Group Leader
currency: " Neverland Dollars"
commit: Add Project
action: create
controller: projects
I have 3 models, as followed:
class User < ActiveRecord::Base
has_many :assigned_projects
has_many :projects, :through => :assigned_projects
has_many :created_projects, :class_name => "Project", :foreign_key => :creator_id
end
class AssignedProject < ActiveRecord::Base
belongs_to :user, class_name: "User"
belongs_to :project, class_name: "Project"
attr_accessible :project_id, :user_id, :position, :project_attributes
accepts_nested_attributes_for :project
validates :user_id, presence: true
validates :project_id, presence: true
validates :position, presence: true
end
class Project < ActiveRecord::Base
belongs_to :user
has_many :assigned_projects
has_many :users, :through => :assigned_projects
belongs_to :creator, :class_name => "User", :foreign_key => :creator_id
attr_accessible :name, :creator_id, :currency :assigned_projects_attributes
accepts_nested_attributes_for :assigned_projects
validates :name, presence: true, length: { minimum: 3, maximum: 100 }
validates :currency, presence: true, length: { minimum: 1, maximum: 5 }
validates :creator_id, presence: true
end
So each User can create a Project. He can add any User to the Project through the join model.
Each Project belongs to a User resp. Creator and has_many user through assigned_projects
I want to give each user of a project a "position", which should be saved in the join model: assigned_project :position
the Project controller looks like that:
class ProjectsController < ApplicationController
def new
#project = Project.new
#project.assigned_projects.build(user_id: current_user)
end
def create
#project = current_user.assigned_projects.build.build_project(params[:project])
#project.creator = current_user
if #project.save
redirect_to current_user
else
render 'new'
end
end
end
and the project/new.html.erb form looks like that:
<%= form_for( #project ) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.fields_for :assigned_projects do |ff| %>
<%= ff.label :position %>
<%= ff.text_field :position%>
<% end %>
<%= f.label :currency %>
<%= f.text_field :currency %>
<%= f.submit "Add Project", class: "" %>
<% end %>
UPDATE: current controller & view
def create
#project = Project.new(params[:project])
if #project.save
redirect_to current_user
else
render 'new'
end
end
<%= form_for( #project ) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.hidden_field :creator_id, value: current_user.id %>
<%= f.fields_for :assigned_projects, #project.assigned_projects do |ff| %>
<%= ff.label :position %>
<%= ff.text_field :position%>
<% end %>
<%= f.label :currency %>
<%= f.text_field :currency %>
<%= f.submit "Add Project", class: "" %>
<% end %>
View:
I think you need to pass the objects collection #project.assigned_projects you built in the new action to the fields_for:
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.fields_for :assigned_projects, #project.assigned_projects do |ff| %>
<%= ff.label :position %>
<%= ff.text_field :position%>
<% end %>
<%= f.label :currency %>
<%= f.text_field :currency %>
<%= f.submit "Add Project", class: "" %>
Controller:
If i understood the first line in the create action i think you try to re-build the project assigned_projects in-order to stamp the creator attribute !!
Instead you could remove this line and put a hidden field in the nested for, something like:
<%= ff.hidden_field :creator, current_user %>
So your controller looks pretty basic now:
def create
#project = Project.new(params[:prject])
if #project.save #nested objects will be saved automatically
redirect_to current_user
else
render 'new'
end
end
What does the build_project method do?
I think in your controller you should just have build, not build.build_project, so like this:
#project = current_user.assigned_projects.build(params[:project])
of if build_project is a method used to create the params then
#project = current_user.assigned_projects.build(project_params)
in the case of rails 4 you would need something like this:
def project_params
params.require(:project).permit(:your_params)
end
In the case of rails 3 I think you need to add
attr_accessible :param1, :param2
in the project model for the parameters you want to set.
Problem is solved by removing the validations for project_id and user_id in the join table "AssignedProject"
So the join Model looks like that:
# Join Model AssignedProject
class AssignedProject < ActiveRecord::Base
belongs_to :user#, class_name: "User"
belongs_to :project#, class_name: "Project"
attr_accessible :project_id, :user_id, :position, :project, :project_attributes
accepts_nested_attributes_for :project
validates :position, presence: { message: " can't be blank." }
end
The New and Create methods look like that:
# Projects Controller
class ProjectsController < ApplicationController
def new
#project = Project.new
#project.assigned_projects.build(user_id: current_user)
end
def create
#project = Project.new(params[:project])
if #project.save
#project.assigned_projects.create(user_id: current_user)
redirect_to current_user
else
render 'new'
end
end
end
And the form in the view for the new method looks like that:
<%= form_for( #project ) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.hidden_field :creator_id, value: current_user.id %>
<%= f.fields_for :assigned_projects, #project.assigned_projects do |ff| %>
<%= ff.hidden_field :project_id, value: #project %>
<%= ff.hidden_field :user_id, value: current_user.id %>
<%= ff.label :position %>
<%= ff.text_field :position%>
<% end %>
<%= f.label :currency %>
<%= f.text_field :currency %>
<%= f.submit "Add Project", class: "" %>
<% end %>

Resources