Devise Unpermitted parameter nested form - forms

I have a user model parent and and child model patient. I want to add patient related attributes in the patients model through devise(user) signup form, but the data is not saving in patient model.
class RegistrationsController < Devise::RegistrationsController
def new
build_resource({})
resource.build_patient
respond_with self.resource
end
def create
super
end
end
private
def sign_up_params
params.require(resource_name).permit(:email, [patient_attributes:[:user_id, :phone, :address,:age]], :password, :password_confirmation)
end
This is my user and patient models:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :patient
accepts_nested_attributes_for :patient
end
#####
class Patient < ActiveRecord::Base
belongs_to :user
end
This is the nested form:
<div class="field">
<%= f.fields_for :patient do |p| %>
phone <%= p.text_field :phone %>
address <%= p.text_field :address %>
age <%= p.text_field :age %>
<%end%>
</div>
When I fill the form and click submit button these are the params:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"b+5GjScdG1gSnPL1eRDMW9U6tWiL1+liJMHvBCWYO2DEqRPJIBKzpXE3HGHlDgJPVcB+ro3ZVi+fHmNCdri1Zw==", "user"=>{"username"=>"q", "email"=>"kh1#gmail.com", "patient_attributes"=>{"phone"=>"444444", "address"=>"lllllllll", "age"=>"55"}, "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "user_type"=>"1"}, "commit"=>"Sign up"}
**Unpermitted parameter: patient_attributes**

So the answer is simple
i add parameters is like this in application controller
devise_parameter_sanitizer.permit(:sign_up) do
|u| u.permit(:email, :password, :password_confirmation, :username,:user_type,patient_attributes: [:user_id, :phone, :address,:age])
end
and change form little bit
<div class="field">
<%= f.fields_for :patient, Patient.new do |p| %>
</br>
phone <%= p.text_field :phone %>
</br>
address <%= p.text_field :address %>
</br>
age <%= p.text_field :age %>
<%end%>
</div>

This might help you.
Just remove square bracket from [patient_attributes:[:user_id, :phone, :address,:age]]
and use
patient_attributes:[:user_id, :phone, :address,:age]
only. This should help you.

Related

Standard Rails form + Cocoon gem: undefined method `new_record?' for nil:NilClass error

I'm following this tutorial and the author is using Slim. Since I more familiar with standard Rails form, I try to change the slim markup to normal form like so:
new.html.erb
<%= render 'form' %>
_form.html.erb
<%= form_for(#user) do |f| %>
<%= f.text_field :name %>
<br><br>
<%= fields_for :user_photos do |photo| %>
<%= render "user_photo_fields", f: photo %>
<span class="links">
<%= link_to_add_association "add photo", f, :user_photos %>
</span>
<% end %>
<%= f.submit %>
<% end %>
_user_photo_fields.html.erb
<div class="nested-fields">
<div class="field">
<%= f.file_field :photo %>
</div>
<%= link_to_remove_association "remove", f %>
</div>
And, this is my models:
class User < ActiveRecord::Base
has_many :user_photos
validates_presence_of :name
accepts_nested_attributes_for :user_photos, allow_destroy: true
end
class UserPhoto < ActiveRecord::Base
belongs_to :user
mount_uploader :photo, PhotoUploader
end
And lastly, strong params inside the users_controller.rb. I didn't touch the rest methods inside the controller because I'm using rails g scaffold user name:string generator.
def user_params
params.require(:user).permit(:name, user_photos_attributes: [:id, :photo, :_destroy])
end
I get this error:
undefined method `new_record?' for nil:NilClass
What am I missing here?
I believe it's just a simple typo - your fields_for :user_photos should be f.fields_for :user_photos (so that it's properly connected to the parent form).
please try with this.
class User < ActiveRecord::Base
has_many :user_photos
validates_presence_of :name
accepts_nested_attributes_for :user_photos, allow_destroy: true
end
can you try to fix this by removing the f
<div class="nested-fields">
<div class="field">
<%= file_field :photo %>
</div>
<%= link_to_remove_association "remove" %>
</div>

Rails 4.2 Creating Multiple New Records with One Form

I have three models: Lesson, Questions and Answers.
What I'm trying to do is on the show lesson view, display the questions and allow users to create answers for each answer. However, I'm not sure the best way to do this.
I tried this approach on my lesson#showview:
<% #questions.each do |question| %>
<%= question.content %><br /><br />
<%= simple_form_for :answers do |f| %>
<%= f.input :content %>
<%= f.hidden_field :question_id, :value => question.id %>
<%= f.button :submit %>
<% end %>
<% end %>
With this code, I receive the error param is missing or the value is empty: lesson
Answer has two fields: content, question_id.
My other concern is that I'd like to have this be user friendly, so if there are multiple questions, there should be multiple input boxes for the answers (one per each question) and one submit button (so multiple answers can be posted at one time).
I think that my approach my bad, but I'm not sure how else to do this, so any help would be greatly appreciated.
Here's what I have so far:
Models:
class Lesson < ActiveRecord::Base
has_many :questions, dependent: :destroy
has_many :answers, through: :questions
accepts_nested_attributes_for :questions, reject_if: :all_blank, allow_destroy: true
accepts_nested_attributes_for :answers, reject_if: :all_blank, allow_destroy: true
end
class Question < ActiveRecord::Base
belongs_to :lesson
has_many :answers, dependent: :destroy
end
class Answer < ActiveRecord::Base
belongs_to :question
end
Lessons Controller
class LessonsController < ApplicationController
def show
#questions = #lesson.questions
end
# PATCH/PUT /lessons/1
# PATCH/PUT /lessons/1.json
def update
respond_to do |format|
if #lesson.update(lesson_params)
format.html { redirect_to #lesson, notice: 'Lesson was successfully updated.' }
format.json { render :show, status: :ok, location: #lesson }
else
format.html { render :edit }
format.json { render json: #lesson.errors, status: :unprocessable_entity }
end
end
end
private
def lesson_params
params.require(:lesson).permit(:name,
answers_attributes: [:id, :content, :question_id]
)
end
end
routes.rb
resources :lessons
post '/lessons/:id', to: "lessons#update"
Add gem in Gemfile and run bundle install:-
gem "nested_form"
On lession show page:-
<%= nested_form_for #lession do |lession_form| %>
<%= #lession.content %>
<%= lession_form.fields_for :questions do |question_form| %>
<% #questions.each do |question| %>
<%= question.content %><br /><br />
<%= question_form.fields_for :answers do |answer_form| %>
<%= answer_form.text_field :content %>
<%= answer_form.link_to_remove "Remove this answer" %>
<% end %>
<%= question_form.link_to_add "Add more answer", :answers %>
<% end %>
<% end %>
<%= lession_form.submit 'Update' %>
<% end %>
I would have thought you are able to achieve without the use of a Gem.
You may need to specify inverse_of in your model. I have previously found this was required in nested attributes when dealing with forms.
class Question < ActiveRecord::Base
belongs_to :lesson
has_many :answers, dependent: :destroy, :inverse_of => :question
end
class Lesson < ActiveRecord::Base
has_many :questions, dependent: :destroy, :inverse_of => :lessons
has_many :answers, through: :questions
#etc.
end
In your lessons controller:
def show
#lesson = Question.find(params[:id])
#questions = #lesson.questions
x.times { #lesson.questions.answer.build }
end
In your views/lessons/show page:
<%= form_for #lesson do |lesson| %>
<%= #lesson.whatever_attribute %>
<%= lesson.fields_for :questions do |question| %>
<% #questions.each do |question| %>
<%= question.content %>
<% end %>
<div id="answers-div" class='form-group'>
<%= question_form.fields_for :answers do |answer| %>
<%= answer.text_field :content id:"answer-entry" %>
<% end %>
</div>
<% end %>
<% end %>
<%= lesson.submit 'Submit' %>
Below the form add some buttons to add further answers or remove:
<button class="btn btn-default" id="addNewAnswer">Add Another Answer Box</button><br>
<button class="btn btn-default" id="deleteNewAnswer">Delete Last Answer</button>
You can then add & remove answers on the fly with jQuery.
$(document).ready(function(){
$("#addNewAnswer").click(function() {
$("#answers-div").append(createNewInputElement($("#answers-div")));
});
});
function createNewInputElement(form) {
var newIndex = $("#answers-div").children('input#choice-entry').length;
var newInput = $("#answer-entry").clone().attr('name', generateNewInputName(newIndex));
newInput.val('');
return newInput;
};
function generateNewInputName(idx) {
return "question[answers_attributes][" + idx + "][content]"
};
$(document).ready(function(){
$("#deleteNewAnswer").click(function() {
if ($("#answers-div input").length > 1) {
$("#answers-div input:last-child").remove();
}
});
});
The use of a nested form is not an issue. You require a nested form to allow you to nest answers within your lessson.questions but you are only allowing the user to give input
If using Rails 4 (and the Strong params) you will also need to allow these with something along these lines (otherwise the params being passed will not be allowed through).
private (in your Lessons controller)
def lesson_params
params.require(:lesson).permit(:content, answer_attributes:[:content])
end
This may not be perfect but it's the start of some sort of solution to your question I would hope.
Try to testing this code, an email me again
in your lesson#show.html.erb
<% for question in #lesson.questions %>
<%= question.content %><br /><br />
<%= simple_form_for :answers do |f| %>
<%= f.input :content %>
<%= f.hidden_field :question_id, :value => question.id %>
<%= f.button :submit %>
<% end %>
<% end %>

4.2 Without cocoon, simple form or formtastic? Nested forms cookbook

I struggle with nested forms.
There're three classes Recipe, Quantity and Ingredient:
class Recipe < ActiveRecord::Base
belongs_to :user
has_many :quantities
has_many :ingredients, through: :quantities
accepts_nested_attributes_for :quantities
class Quantity < ActiveRecord::Base
belongs_to :recipe
belongs_to :ingredient
accepts_nested_attributes_for :ingredient, :reject_if => :all_blank
class Ingredient < ActiveRecord::Base
has_many :quantities
has_many :recipes, :through => :quantities
Recipe Controller
def new
#recipe = current_user.recipes.build
#quantity = #recipe.quantities.build
end
def create
#recipe = current_user.recipes.build(recipe_params)
if #recipe.save
redirect_to #recipe
else
render 'new'
end
end
private
def recipe_params
params.require(:recipe).permit(
:name,
quantities_attributes: [:id, :amount, :ingredient_id],
)
end
View for recipe#new
<%= form_for #recipe, html: {class: "form-horizontal"} do |f| %>
<li class="control-group">
<%= f.label :name, "Recipe Name", class: "control-label" %>
<div class="controls"><%= f.text_field :name %></div>
</li>
<%= f.fields_for :quantities do |quantity| %>
<%= render 'quantity_fields', f: quantity %>
<% end %>
<%= f.submit %>
<% end %>
_quantity_fields
<%= f.label :amount, "Amount:" %>
<%= f.text_field :amount %>
Here should follow a Select input with content from Ingredient and the POST request should insert the ingredient_id in the column of Quantity.
<%= f.select("ingredient_id", "ingredient_id", Ingredient.all.collect
{|p| [ p.name, p.id ] }, {include_blank: 'Choose'}) %>
getting
NoMethodError in Recipes#new
Showing C:/Sites/4.2/sample_app - Kopie/app/views/recipes/_quantity_fields.html.erb
where line #6 raised:
undefined method `merge' for [["Sugar", 1], ["Butter", 2]]:Array
Any ideas? Thanks!
<%= f.collection_select(:ingredient_id, Ingredient.all, :id, :name) %>
solved the select statement
but how can I create multiple quantities here?

Devise Registration not showing "some" nested form fields Rails 4

I've got a modified Devise "sign_up" form (new registration) that includes fields for child and grandchild objects to be created along with the user. All of the model relationships are set up properly with access to the child's attributes. However, when the form renders, only the fields for the Devise user and one of the grandchildren is shown.
When a User is created, he/she will automatically be assigned a Customer object, an Account object, and an Address object. As you can see by the relationships in the User model below, User has one Customer and Customer has many Accounts and one Address. There was previously an issue with rendering the form at all, which I solved by changing the values passed to the builder method. WHY WON'T THIS WORK??? This is what I have so far:
*user.rb
class User < ActiveRecord::Base
before_create :generate_id
# Virtual attribute for authenticating by either username or email
# This is in addition to a real persisted field like 'username'
attr_accessor :login
has_one :customer, :dependent => :destroy
has_many :accounts, through: :customer
accepts_nested_attributes_for :customer, :allow_destroy => true
accepts_nested_attributes_for :accounts, :allow_destroy => true
has_one :address, through: :customer
accepts_nested_attributes_for :customer, :allow_destroy => true
accepts_nested_attributes_for :address, :allow_destroy => true
has_one :administrator
validates_uniqueness_of :email, :case_sensitive => false
validates_uniqueness_of :id
validates :username,
:presence => true,
:uniqueness=> {
:case_sensitive => false
}
# User ID is a generated uuid
include ActiveUUID::UUID
natural_key :user_id, :remember_created_at
belongs_to :user
# specify custom UUID namespace for the natural key
uuid_namespace "1dd74dd0-d116-11e0-99c7-5ac5d975667e"
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :timeoutable, :recoverable, :trackable, :validatable
# Generate a random uuid for new user id creation
def generate_id
self.id = SecureRandom.uuid
end
# Allow signin by either email or username ("lower" function might have to be removed?)
def self.find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions.to_h).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
else
where(conditions.to_h).first
end
end
end
registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
before_filter :configure_permitted_parameters
# GET /users/sign_up
def new
#user = User.new
build_resource({})
self.resource[:customer => Customer.new, :account => Account.new, :address => Address.new]
respond_with self.resource
end
def create
#user = User.new
# Override Devise default behavior and create a customer, account, and address as well
resource = build_resource(params[:sign_up])
if(resource.save)
sign_in(resource_name, resource)
respond_with resource, :location => after_sign_up_path_for(resource)
else
render :action => "new"
end
end
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u|
u.permit(:username, :email, :password, :password_confirmation,
customer_attributes: [:title, :firstname, :lastname, :phone1, :phone2],
account_attributes: [:acct_type],
address_attributes: [:address1, :address2, :zip_code])
}
end
end
application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_filter :configure_permitted_parameters, if: :devise_controller?
def after_sign_in_path_for(resource)
if current_user.role == 'admin'
adminview_administrator_path(current_user, format: :html)
else
accounts_path(current_user, format: :html)
end
end
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:username, :email, :password, :password_confirmation,
customer_attributes: [:title, :firstname, :lastname, :phone1, :phone2],
account_attributes: [:acct_type],
address_attributes: [:address1, :address2, :zip_code]) }
devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:login, :username, :email, :password) }
devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:username, :email, :password, :password_confirmation, :current_password) }
end
end
views/devise/registrations/new.html.erb
<h1>Create an account</h1>
<div class="panel panel-default" style="width: 50%; padding: 0 25px;">
<%= bootstrap_nested_form_for(resource, as: resource_name, url: user_registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<h3>User Info</h3>
<!-- fields for User object -->
<%= f.text_field :username, autofocus: true %>
<%= f.email_field :email %>
<%= f.password_field :password , autocomplete: "off"%>
<% if #validatable %>
<em>(<%= #minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password_confirmation, autocomplete: "off" %>
<!-- fields for Customer object -->
<%= f.fields_for :customer do |customer_fields| %>
<%= customer_fields.text_field :title %>
<%= customer_fields.text_field :firstname %>
<%= customer_fields.text_field :lastname %>
<%= customer_fields.text_field :phone1 %>
<%= customer_fields.text_field :phone2 %>
<% end %>
<!-- fields for Account object -->
<%= f.fields_for :account do |account_fields| %>
<%= account_fields.text_field :acct_type %>
<% end %>
<!-- fields for Address object -->
<%= f.fields_for :address do |address_fields| %>
<%= address_fields.text_field :address1 %>
<%= address_fields.text_field :address2 %>
<%= address_fields.text_field :zip_code %>
<% end %>
<br />
<div class="actions">
<%= f.submit "Create My Account", :class => "btn btn-info" %>
</div>
<% end %>
</div>
</div>
Again, the above view does render, but the form only displays the fields for Devise new user and the one field (acct_type) for the account fields. How to get the rest of form to display and create all of these things on submission? Everything I've tried and everything I've read leads me to think that there's a problem with Rails 4's strong_parameters not being able to recognize the permitted attributes (see above controllers) in an array. Could that be the issue? If so, how does one go about passing the parameters necessary to build all these things?
Could be problem with the routes?
routes.rb
Rails.application.routes.draw do
devise_for :users, :controllers => { :registrations => "registrations" }
devise_scope :user do
# authentication
post "/accounts/adminview" => "devise/sessions#new"
end
root 'home#index'
resources :administrators do
member do
get :adminview
end
end
resources :users do
resource :customers
resource :accounts
resource :addresses
end
resources :account_types, :accounts, :addresses, :administrators, :customers, :transaction_types, :transactions, :users
end
I've tried every combination of ways that I could find on SO. This has taken up days worth of valuable time. I don't see any reason why it can't work. Does anyone have a better way of doing this? Is there a gem that would help? I'm willing to tear Devise apart and rebuild if necessary.
F.Y.I. It's Rails 4 and Devise 3.4.1. I've also added nested_form gem, but it doesn't make a difference.
Thank you
If you raise your params in controller you probably see accounts_attributes instead account_attributes you are setting in permit at application_controller, try replace it.

Form_for with radio_button generated from database

This would seem easy to do, basically I'm using Devise for administrative purposes, so every user signing up can create a product/figure linked to their account then put up that product for a trade with another user.
Class User
rolify
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :role_ids, :as => :admin
attr_accessible :email, :password, :password_confirmation, :remember_me
has_many :trades
has_many :figures, :through => :trades
accepts_nested_attributes_for :trades
Class Figure
attr_accessible :image_url, :name, :series, :figure_id
has_many :trades
has_many :users, :through => :trades
accepts_nested_attributes_for :trades
Class Trade
attr_accessible :figure_id, :user_id
belongs_to :user
belongs_to :figure
The Controller for Trade
def new
#trade = Trade.new
end
def create
#trade = current_user.figures.build(params[:trade])
end
The Form for trade
<%= form_for(#trade) do |f| %>
<div class="field">
<% Figure.all.each do |figure| %>
# Create a list of figurines radio buttons
<%= f.radio_button :figure_id, figure.id, :checked => (params[:figure_id] == nil ? false : params[:figure_id]) %>
# Link the figures thumbnail to the radio button
<%= f.label :figure_id, image_tag(figure.image_url, :size => "40x58", :alt => figure.name ), :value => "#{figure.id}" %>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Here's the parameters
{"utf8"=>"✓",
"authenticity_token"=>"lo+RWOLxGhPIP1pmJhk9v+vetO7cGEKGY844egaQ6A0=",
"trade"=>{"figure_id"=>"12"},
"commit"=>"Create Trade"}
Here's the problem I'm getting: unknown attribute: figure_id
Why am I receiving this error?
If I understand you correctly, this error should go away if you build a Trade instead of a Figure (you're just trying to link a figure to a user through a new trade, right?):
def create
#trade = current_user.trades.build(params[:trade])
#trade.save
end