Nested forms and strong parameters - rails 4 - ruby-on-rails

I have a problem with a form and strong parameters. I see many have gone before me with such problems but nothing suggested to them seems to work.
My form:
<%= form_for(#student) do |f| %>
<%= f.label :school_id %>
<%= f.text_field :school_id, class: 'form-control' %>
...
<%= f.fields_for :enrollments do |enrol_form| %>
<%= enrol_form.label :date , "Enrollment date", class: 'form-control' %>
<%= enrol_form.date_field :date , class: 'form-control' %>
<%= enrol_form.hidden_field :reason, value: "Entered on system" %>
<% end %>
...
<% end %>
Models:
class Student < ActiveRecord::Base
include PhoneValid
has_many :enrollments, inverse_of: :student
validates :enrollments, :presence => { message: "Must have at least one enrollment event" }
accepts_nested_attributes_for :enrollments
end
class Enrollment < ActiveRecord::Base
belongs_to :student
default_scope -> { order(date: :asc) }
validates :date, presence:true
validates :enroll_wd, inclusion: {in: %w(ENROLL WD), messages: "%{value} must be either WD or ENROLL"}
end
Controller:
class StudentsController < ApplicationController
def create
#student = Student.new(student_params)
#student.save
end
def student_params
params.require(:student).permit(:school_id, :cellphone, :attendance_officer_id, :attendance_officer_type, :language_preferred, {enrollment_attributes: [:date, :reason, :id]})
end
end
The output of my form according to debug(params) is:
--- !ruby/hash:ActionController::Parameters
utf8: "✓"
authenticity_token: >cuk8Y+d8fHhJ3mU7wtRYtyDJoiYaG8lfzHvdGVBUI+1qmH7xQr20BHsBAFRT4r1EfDb/MMbxMq8rbKw3Cf2Y1A==
student: !ruby/hash:ActionController::Parameters
school_id: '234234'
cellphone: '234234234'
language_preferred: en
enrollments_attributes: !ruby/hash:ActionController::Parameters
'0': !ruby/hash:ActionController::Parameters
date: '2015-07-10'
reason: Entered on system
attendance_officer_id: '8'
attendance_officer_type: User
commit: Save student
controller: students
action: create
The output of student_params is:
{"school_id"=>"234234", "cellphone"=>"234234234", "attendance_officer_id"=>"8", "attendance_officer_type"=>"User", "language_preferred"=>"
I've been trying every different format of student_params that I can find on in the forums and have concluded that the problem must be somewhere else - am I creating the form correctly? Is there something wrong in my models?
Thank you for any help you can give me.
Walter

Try changing your student_params to below
def student_params
params.require(:student).permit(:school_id, :cellphone, :attendance_officer_id, :attendance_officer_type, :language_preferred, enrollments_attributes: [:date, :reason, :id])
end

Related

How to create a form object with multiple input fields with the same name

I am learning rails and will be very grateful for an advice about creating a form object with multiple input fields with the same name. I am having errors with unpermitted params.
I've been searching information for several days, have tried a lot without any result. This is my first question, sorry if it is not easy to understand.
I am using rails 6.0.0, and having 3 models: Recipe, Ingredient, RecipeIngredientRelation. I want to make multiple input fields for Ingredient and RecipesIngredient. Errors:
Parameters: {"authenticity_token"=>"G6vwDdptMBgl2SPvLfS4BoiCjoRnf+FGYuKW0Ykt3Aodt5HfBEx5KHWBEpl4r52ANCv9QWz13rDyAP42qCyESg==", "recipes_ingredient"=>{"name"=>"Recipe", "portions"=>"1", "time_count_id"=>"3", "calories"=>"", "ingredients"=>{"i_name"=>"water"}, "recipe_ingredient_relations"=>{"quantity"=>["100"], "measurement_id"=>"3"}, "recipe"=>"some text", "tips"=>"", "is_public"=>"0"}, "commit"=>"レシピを投稿する"}
Unpermitted parameters: :ingredients, :recipe_ingredient_relations
binding.pry gets me #recipes_ingredient.errors:
#details={:i_name=>[{:error=>:blank}]},
#messages={:i_name=>["can't be blank"]}>
my models are:
Recipe
has_many :recipe_ingredient_relations
has_many :ingredients, through: :recipe_ingredient_relations
accepts_nested_attributes_for :ingredients, :recipe_ingredient_relations, allow_destroy: true
Ingredient
has_many :recipe_ingredient_relations
has_many :recipes, through: :recipe_ingredient_relations
RecipeIngredientRelation
belongs_to :recipe
belongs_to :ingredient
Form Object
class RecipesIngredient
include ActiveModel::Model
attr_accessor :ingredients, :i_name, :recipe_ingredient_relations, :quantity, :measurement_id, :name, :image, :portions, :time_count_id, :recipe, :tips, :calories, :is_public, :user_id
with_options presence: true do
validates :i_name #, uniqueness: true
validates :quantity, length:{maximum: 10}, allow_blank: true
validates :measurement_id, allow_blank: true
validates :name
validates :portions
validates :time_count_id, numericality: {other_than: 1}
validates :recipe
validates :tips, allow_blank: true
validates :calories, allow_blank: true
validates :is_public
end
def save
recipe = Recipe.create(user_id: user_id, name: name, image: image, portions: portions, time_count_id: time_count_id, recipe: recipe, tips: tips, calories: calories, is_public: is_public)
ingredient = Ingredient.create(i_name: i_name)
recipe_ingredient_relation = RecipeIngredientRelation.create(recipe_id: recipe.id, ingredient_id: ingredient.id, quantity: quantity, measurement_id: measurement_id)
end
end
part of input form view:
%= form_with(model: #recipes_ingredient, url:recipes_path, local: true) do |form| %>
<...>
<%= form.fields_for :ingredients do |f| %>
<%= f.text_field :i_name, placeholder: "例)レタス", class: "form-el ingredient-input required" %>
<% end %>
<%= form.fields_for :recipe_ingredient_relations do |f| %>
<div class="quantity">
<%= f.text_field :quantity, multiple: true, placeholder: "例)100", class: "form-el quantity-input required" %>
<%= f.collection_select(:measurement_id, Measurement.all, :id, :name, {}, {class:"after-input-required"}) %>
<p class="after-input after-input-btn-required">+</p>
</div>
</div>
<% end %>
and, finally, the Recipes controller:
class RecipesController < ApplicationController
def index
#recipe = Recipe.all
end
def new
#recipes_ingredient = RecipesIngredient.new
end
def create
#recipes_ingredient = RecipesIngredient.new(recipe_params)
binding.pry
# if #recipe.valid?
# #recipe.save
# redirect_to root_path
# else
# render action: :new
# end
end
private
def recipe_params
params.require(:recipes_ingredient).permit(:name, :image, :portions, :time_count_id, :recipe, :tips, :calories, :is_public, ingredients_attributes: [:id, :i_name], recipe_ingredient_relations_attributes: [:id, :quantity, :measurement_id] ).merge(user_id: current_user.id)
end
end

Validation failed: Park must exist, User must exist

I'm working on a project where Users can see dog Parks and make individual Playdates for each park. The issue I'm having is that the PlaydatesController create action is not persisting the user_id and park_id that each new playdate is associated with. I've tried adding optional: true to my Playdate model, which does save each playdate. However, doing this makes a null column entry for the user_id and park_id.
All I need is the user_id and park_id to create a playdate and keep the association between playdates and parks... Did I mess up my associations? Any help is GREATLY appreciated.
Here's my code:
Playdate MODEL:
class Playdate < ApplicationRecord
belongs_to :park
belongs_to :user
validates :date, presence: true
validates :time, presence: true
end
Park MODEL:
class Park < ApplicationRecord
has_many :playdates
has_many :comments
has_many :users, through: :comments
end
User MODEL:
class User < ApplicationRecord
has_many :parks
has_many :playdates
has_many :comments, through: :parks
end
Playdates CONTROLLER:
def create
#playdate = Playdate.new(playdate_params)
if #playdate.save!
redirect_to park_path(#park)
else
render :new
end
end
private
def playdate_params
params.require(:playdate).permit(:time, :date, :user_id, :park_id)
end
Playdates NEW VIEW:
<%= form_for #playdate do |f| %>
<%= f.label :date %>
<%= f.date_field :date %><br><br>
<%= f.label :time %>
<%= f.time_field :time %><br><br>
<%= hidden_field_tag :user_id, current_user.id %>
<%= hidden_field_tag :park_id, #park%>
<%= f.submit "Add Playdate!" %>
<% end %>
You should use:
f.hidden_field :user_id, value: current_user.id
f.hidden_field :park_id, value: #park.id
The rendered HTML is not the same between "hidden_field_tag" and "hidden_field". Try by yourself to see the difference.

Creating new resources with nested form with polymorphic relationships in Rails 4 presence

I can't seem to successfully create new resources from a nested form that has polymorphic associations.
It seems the problem lies in validations with polymorphic 'belongs_to' relationships. For example:
class DealerUser < User
belongs_to :dealer, polymorphic: true, foreign_key: :loginable_id, foreign_type: :loginable_type
validates :dealer, presence: true
end
I am attempting to create a new resource called Dealer.
class Dealer < ActiveRecord::Base
has_many :dealer_locations, dependent: :destroy
has_one :dealer_user, as: :loginable, dependent: :destroy
accepts_nested_attributes_for :dealer_locations, allow_destroy: true
accepts_nested_attributes_for :dealer_user, allow_destroy: true
validates :name, presence: true, length: { maximum: 255 }, uniqueness: { case_sensitive: false }
validates_associated :dealer_locations
end
My form code:
<%= form_for(#dealer) do |d| %>
<%= render 'shared/error_messages', object: d.object %>
<br>
<div class="form-group">
<%= d.label :name %>
<%= d.text_field :name, class:"form-input-field" %>
</div>
<h4>Login Credentials</h4><br>
<%= d.fields_for :dealer_user do |du| %>
<div class="form-group">
<%= du.label :email %>
<%= du.text_field :email, class:"form-input-field" %>
</div>
<div class="form-group">
<%= du.label :password %>
<%= du.password_field :password, class:"form-input-field" %>
</div>
<% end %>
<%= d.submit class: "btn btn-large btn-primary" %>
<% end %>
Controller:
class DealersController < ApplicationController
def new
#dealer = Dealer.new
#dealer.build_dealer_user
end
def create
#dealer = Dealer.new(dealer_params)
puts #dealer.inspect
puts #dealer.dealer_user.inspect
if #dealer.save
redirect_to(#dealer, :notice => 'Dealer was successfully created.')
end
def dealer_params
params.require(:dealer).permit(:name, dealer_user_attributes: [:email, :password, :id])
end
end
When I submit the form, I am getting DealerUser.dealer can't be blank error.
#<Dealer id: nil, name: "NAME", created_at: nil, updated_at: nil>
#<DealerUser id: nil, type: "DealerUser", email: "fake#email.com", loginable_id: nil, loginable_type: "Dealer",...
It looks like the associations are working, since you can see type fields are set.
Update 3/11/16: params
{"utf8"=>"✓", "authenticity_token"=>"...",
"dealer"=>{"name"=>"ZZ WATER",
"dealer_user_attributes"=>{"email"=>"...", "password"=>"[FILTERED]"},
"dealer_locations_attributes"=>{"0"=>{"phone"=>"...", "contact_email"=>"...",
"address_attributes"=>{"line_1"=>"line1", "line_2"=>"", "city"=>"city", "country_code"=>"US", "state_code"=>"CA", "zip_code"=>"00000"}}}}, "commit"=>"Create Dealer"}
Is this an order of operations issue? DealerUser needs an ID to pass validation, but the Dealer ID wont get set until it's saved to the DB, which isn't happening since it's not passing validation. Can someone offer me some guidance.
Also, this is a simplified version of what I'm working on, there are nested locations and addresses too. I'm also only a couple months into using ruby on rails. Thank you.
I created a fresh similar project. And I also got the same error. In my attempt to fix this problem, I stumbled upon inverse_of, and it now worked.
Now try
class DealerUser < User
# ..
belongs_to :dealer, polymorphic: true, foreign_key: :loginable_id, foreign_type: :loginable_type, inverse_of: :dealer_user
# ..
end
class Dealer < ActiveRecord::Base
# ..
has_one :dealer_user, as: :loginable, dependent: :destroy, inverse_of: :dealer
# ..
end

Nested attributes validation on updating

I have two model Product and ProductBoxing, product has many product_boxings.
product.rb
class Product < ActiveRecord::Base
has_many :product_boxings
accepts_nested_attributes_for :product_boxings
validates :name, presence: { presence: true, message: 'please give a name' }, on: :update
end
product_boxing.rb
class ProductBoxing < ActiveRecord::Base
belongs_to :product
validates :quantity, presence: { presence: true, message: 'please fill in quantity' }, on: :update
end
_form.html.erb
<%= form_for(#product, html: {class: "form-horizontal", role: "form", multipart: true}) do |f| %>
<%= f.text_field :name%>
<%= f.fields_for :product_boxings do |g| %>
<%= g.text_field :quantity %>
<% end %>
<% end %>
For some reasons, I create both product and product_boxing without validation first. After that, I want to validate both on updating. The validation works for Product, but not for ProductBoxing.
Are there any problem in my code? or it is a rails issue?
BTW, I remove validation option on: :update and validate both on creating, the validations work for both.
update
At first, user will ran the follow code
def new
product = Product.new
p_b= product.product_boxings.build()
product.save!
redirect_to edit_product_path(product)
end
then
def edit
end
and post form
def update
#product.update(product_params)
unless #product.errors.any?
redirect_to products_url
else
render 'edit'
end
end
other info
def product_params
params.require(:product).permit(:name, product_boxings_attributes:[:id, :quantity] )
end
You should ensure the validation of your associated model:
class Product < ActiveRecord::Base
has_many :product_boxings
validates_associated :product_boxings
# ...
end
http://apidock.com/rails/ActiveModel/Validations/ClassMethods/validates_associated
TEST THE MODELS WITH THE CONSOLE
> p = Product.new
> p.valid?
> p.errors
> p.product_boxings.each { |pb| pb.valid?; pb.errors }
If you want to see this product_boxings messages in the error list, you should create a custom validation.
validate :associated_product_boxings
def associated_product_boxings
product_boxings.each do |product_boxing|
errors.add (...) unless product_boxing.valid?
end
end

Rails association cannot be blank

I'm building off the tutorial app in ruby on rails for a project, and I'm trying to create an association between two models.
In my database, there are users, events, and an attendance table that associates with the email from a user and a code from an event.
I've tried to research how to do this myself, but every time I try to validate the attendance email to a user, it states that the user cannot be blank as if I were trying to create a new one.
Still quite new to Ruby on Rails, so any advice would be appreciated! The models are below.
User Model:
class User < ActiveRecord::Base
attr_accessible :email, :name, :password, :password_confirmation
has_secure_password
has_many :attendances, inverse_of: :user
accepts_nested_attributes_for :attendances
before_save { |user| user.email = email.downcase }
before_save :create_remember_token
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates(:name, presence: true, length: { maximum: 50 })
validates(:email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: {case_sensitive: false})
validates(:password, length: { minimum: 6 } )
validates(:password_confirmation, presence: true)
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
Attendance Model:
class Attendance < ActiveRecord::Base
attr_accessible :code, :email
belongs_to :user
validates_presence_of :user
end
So far I'm only trying to enforce the association between User and Attendance, once I get that working I'll do the same to Events. Also, this is Rails 3.2.19 and Ruby 1.9.3.
EDIT: Here is the code I'm using for the form, I believe that it works because until I put the validation into the model it was creating rows into the Attendance table.
<% provide(:title, 'Test Event') %>
<h1>Attendance Registration</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for(#attendance) do |f| %>
<%= render 'shared/attendance_error_messages' %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :code %>
<%= f.text_field :code %>
<%= f.submit "Submit", class: "btn btn-large btn-primary" %>
<% end %>
</div>
</div>
Also, here's the attendance controller, if that helps.
class AttendancesController < ApplicationController
def new
#attendance = Attendance.new
end
def create
#attendance = Attendance.new(params[:attendance])
if #attendance.save
flash[:success] = "Attendance logged."
redirect_to root_path
else
render 'new'
end
end
end
Add this line to your form to avoid the user presence error
<%= f.hidden_field :user_id, current_user.id %>

Resources