I have two models: User and Role.
The User attributes are:
name:string
email:string
admin:boolean
role_id:integer
The Role attributes are:
designer:boolean
developer:boolean
The associations that I've set is that user belongs_to role and role has_many users.
When the user signs up, I want him to choose his position (either designer or developer). However, I get the role_id as an Integer field when I want to display the positions (designer and developer) to choose from. Can anyone help me with that?
What you want to do is called "Nested Models".
First you have to tell the model to allow the other model like this:
# app/model/user.rb
class User < ActiveRecord::Base
belongs_to :role
accepts_nested_attributes_for :role
end
the next thing is in your view
#app/views/users/new.html.ham
= simple_form_for #user do |f|
= f.input :name
= f.input :email
%br
= f.simple_fields_for :role do |role|
= role.input :designer
= role.input :developer
= f.button :submit, "Send Message", :class => 'btn btn-primary btn-block'
Now last but bot least you to be able to accept the new params in the controller
class UsersController < ApplicationController
expose(:users){User.all.order(:id)}
expose(:user, attributes: :user_params)
def new
#user = User.new
#user.role.build
end
def create
if user.save
flash[:notice] = t(:user_was_successfully_created)
redirect_to root_path
else
render :new
end
end
private
def user_params
params.require(:user).permit(
[
:email ,
:name ,
role_attributes: [
:designer,
:developer,
]
]
)
end
end
you can look at a sample app https://github.com/mzaragoza/sample_nestes_forms
I hope that this helps
Happy Hacking
Related
class User < ApplicationRecord
has_one :address
accepts_nested_attributes_for :address
end
class Address < ApplicationRecord
belongs_to :user
end
<%= form_for #user do |f| %>
.... // some filed here everything fine
<%= f.fields_for :address do |a| %>
<%= a.text_field :city %> // this field is not appear
<% end %>
<% end %>
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.valid?
#user.save
else
redirect_to root_path
end
end
private
def user_params
params.require(:user).permit(:id, :name, :email, :password, :password_confirmation, :status, :image, :address_attributes => [:id, :city, :street, :home_number, :post_code, :country])
end
end
So like you can see above I have two classes and one form, when I am trying display fields for Address class I can not do it in that way. I took this example from https://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for
I was trying different combination like for example using User.new and Address.new in form definition it not working as well, I was able display all fields in that situation but I wasn't able to save Address data to table, because of "unpermited address".
Can someone explain what I am doing wrong? Or at least give me please some hints.
[SOLVED]
I should learn how to read documentations properly. Excalty like #Srack said I needed just use build_address method. I checked documentation rails api again and on the end of page there was examples says to create User class like this:
class User < ApplicationRecord
has_one :address
accepts_nested_attributes_for :address
def address
super || build_address
end
end
and that solved my issue.
Thank you.
You'll have to make sure there's an address instantiated for the user in the new view. You could do something like:
def new
#user = User.new
#user.build_address
end
You should then see the address fields on the form.
The nested_fields_for show the fields for a record that's been initialised and belong to the parent. I think the latter is why your previous attempts haven't worked.
FYI build_address is an method generated by the belongs_to association: http://guides.rubyonrails.org/association_basics.html#methods-added-by-belongs-to
Can't save params selected on select box.
Table users:
1id| |name|
1 CR7
2 Messi
Table ejecutives:
1id| |name|
1 Mourinho
2 Guardiola
Table user_ejecutives:
|id| |user_id| |ejecutive_id|
1 1 1
2 2 2
Controller users_controller.rb:
def new
#obj_user = User.new
end
def create
#user = User.new user_params
#user.save
end
def show
#user = User.find(params[:id])
end
private
def user_params
params.require(:user).permit(:name, user_ejecutive_ids: [])
end
Models:
#User.rb
has_many :ejecutives, :through => :user_ejecutives
has_many :user_ejecutives
has_and_belongs_to_many :user_ejecutives, class_name: "User", join_table: "user_ejecutives"#, foreign_key: :user_id, association_foreign_key: :ejecutive_id
#Ejecutive.rb
has_many :user_ejecutives
has_many :users, :through => :user_ejecutives
#UserEjecutive.rb
belongs_to :user
belongs_to :ejecutive
View new.html.erb:
<%= form_for #user do |f| %>
<%= form.text_field :name %>
<%= f.collection_select :user_ejecutive_ids, Ejecutive.all, :id, :name, multiple: true %>
<% end %>
View show.html.erb
<% #user.ejecutives.each do |ejecutive| %>
<%= ejecutive.name %></label>
<% end %>
I'm not getting results on the view show and it show on logs:
SystemStackError (stack level too deep):
If you're just trying to populate the join table (user_ejecutives), you'll want to populate the singular_colletion_ids method:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new user_params
#user.save
end
private
def user_params
params.require(:user).permit(:name, user_ejecutive_ids: [])
end
end
#app/views/users/new.html.erb
<%= form_for #user do |f| %>
<%= f.collection_select :user_ejecutive_ids, User.all, :id, :name, multiple: true %>
<%= f.submit %>
<% end %>
This will assign new user_ejecutives for each new #user you create.
PS User.all is valid in this instance as you're dealing with a new (uncreated) #user record, hence it won't appear in the db.
If you wanted to create new user_ejecutives with each new #user, you'll want to use accepts_nested_attributes_for, which I can explain if required.
Update
So your error is as follows:
Unpermitted parameter: user_ejecutive_ids
... you also have another error...
NoMethodError (undefined method `each' for nil:NilClass):
This is exactly why I don't like your code. Because it doesn't fit to convention, you've go to evaluate whether the params are present etc.
You'll need to use the controller code I posted - it will populate the other table for you, and fix this NilClass error.
--
Join Table
Your user_ejecutives table is a join table.
Your User model should have the following:
#app/models/user.rb
class User < ActiveRecord::Base
has_and_belongs_to_many :user_ejecutives, class_name: "User", join_table: "user_ejecutives", foreign_key: :user_id, association_foreign_key: :ejecutive_id
end
You'll have to remove the id column from your user_ejecutives table (as per the definition here). The importance of this is that it gives you the ability to populate the singular_collection_ids method (in your case user_ejective_ids), as per my recommended code.
Try the following code.
params.require(:user).permit(:name, :user_ejecutives => [])
Hey, I think you have "ejecutive_id" column declared as integer but when loop through "user_ejecutives" you are getting each value as string, May be this is causing the issue, Kindly update your create action to below.
def create
obj_user = User.new(user_params)
if obj_user.save
params[:user_ejecutives].each do |ejecutive|
user_ejecutive = UserEjecutive.create(user_id: obj_user.id, ejecutive_id: ejecutive.to_i)
user_ejecutive.save
end
end
end
I have what I thought is a simple event signup application. A user registers for the site and then can select an event, apply to participate in that event by updating some fields that are on the user model (At this point, just a first_name). A User can attend many Events, but must register (Participation) for each one. An Event can have many Users through Participations. Any help is greatly appreciated!
There are currently three models:
# user.rb
class User < ActiveRecord::Base
has_many :participations
has_many :events, through: :participations
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
# event.rb
class Event < ActiveRecord::Base
has_many :participations
has_many :users, through: :participations
end
# And a join table: participation.rb
class Participation < ActiveRecord::Base
belongs_to :user
belongs_to :event
accepts_nested_attributes_for :user
end
Here's my routes file:
routes.rb
Rails.application.routes.draw do
mount RailsAdmin::Engine => '/admin', as: 'rails_admin'
devise_for :users
root 'events#index'
resources :events do
resources :participations
end
resources :users
end
And I think the only applicable controller:
participations_controller.rb
class ParticipationsController < ApplicationController
def index
end
def new
#participation = Participation.new
#user = current_user
#event = Event.find(params[:event_id])
end
def create
#participation = Participation.new(participation_params)
if #participation.save
redirect_to events_path, notice: "You are registered!"
else
render action: 'new'
end
end
private
def participation_params
params.require(:participation).permit(:status, :user_attributes => [:id, :first_name])
end
end
The form should simply create a new participation based on the event_id, set its status, and update the user_attributes.
views/participations/new.html.erb
<%= form_for #participation, url: {action: "create"} do |f| %>
<%= f.label :status %><br />
<%= f.text_field :status %>
<%= f.fields_for :user, current_user do |builder| %>
<fieldset>
<%= builder.label :first_name, "First Name" %><br />
<%= builder.text_field :first_name %><br />
</fieldset>
<% end %>
<%= f.submit "Register" %>
<% end %>
Unfortunately, completing the form returns a 404 error with a missing participation_id.
Started POST "/events/1/participations" for ::1 at 2015-11-24 21:26:35 -0600
Processing by ParticipationsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"0vVTyeGwGUheZPbOoMeyUvr2ciJG2OpXwqToc2pYLr2HXDMhogX8llESiG8Z4Cc5Pq5sBmiHi43rvjHka7K3yA==", "participation"=>{"status"=>"done", "user_attributes"=>{"first_name"=>"sone", "id"=>"1"}}, "commit"=>"Register", "event_id"=>"1"}
Completed 404 Not Found in 3ms (ActiveRecord: 0.0ms)
ActiveRecord::RecordNotFound - Couldn't find User with ID=1 for Participation with ID=:
Well as described here, what is happening is that when you pass an id to the nested model, accepts_nested_attributes will look and try to update the model you are looking for.
So at that moment there is not such association between current user and the Participation you want to create, that's why you get the error:
Couldn't find User with ID=1 for Participation with ID=:
That means there is not such user with ID=1 associated with your participation
My suggestion:
Instead of add nested attributes for user, why not just add the fields you need to the Participation model?
Add the first_name attribute to your participation model and in your controller do the following:
class ParticipationsController < ApplicationController
def index
end
def new
#participation = Participation.new
#participation.user = current_user
#participation.event = Event.find(params[:event_id])
end
def create
#participation = Participation.new(participation_params)
#participation.user = current_user
#participation.event = Event.find(params[:event_id])
if #participation.save
redirect_to events_path, notice: "You are registered!"
else
render action: 'new'
end
end
private
def participation_params
params.require(:participation).permit(:status, :first_name)
end
end
Then in your form you can just make a normal first_name input:
<fieldset>
<%= f.label :first_name, "First Name" %><br />
<%= f.text_field :first_name %><br />
</fieldset>
Try that and let me know, by the way do not forget to remove the accepts_nested_attributes from Participation model, and make sure your migrations are correctly set up for match the associations you have. I hope have helped you.
Update
If you do not want to persist user information in the Participation then you can just add attribute accessors to your Participation model, and store information in your current_user in your create action:
#app/models/participation.rb
class Participation < ActiveRecord::Base
belongs_to :user
belongs_to :event
attr_accessor :first_name, :whatever_other_attribute # You can add as many attributes you need.
end
Then just update information of your current_user in your create action:
#app/controllers/participations_controller
def create
#participation = Participation.new(participation_params)
#participation.user = current_user
#participation.event = Event.find(params[:event_id])
current_user.update_attributes first_name: #participation.first_name
if #participation.save
redirect_to events_path, notice: "You are registered!"
else
render action: 'new'
end
end
This way you store the information in the current_user instead of Participation, also this way you can easily customize the different information you will ask in the participation form.
Thanks to #ssoulless for pointing me in to the final solution.
Ultimately I was able to update the controller action to associate the user and then use the participation_params to update the participation[:user].
# participations_controller.rb
...
def create
#participation = Participation.new(status: participation_params[:status])
#participation.mission_id = params[:mission_id]
current_user.update_attributes(participation_params[:user_attributes])
#participation.user = current_user
if #participation.save
redirect_to missions_path, notice: "You are registered!"
else
render action: 'new'
end
end
private
def participation_params
params.require(:participation)
.permit(:id, :status, user_attributes: [:id, :first_name])
end
I like this approach a bit more since it hides the user attributes in a private method. Also for anyone that this might help, when using accepts_nested_attributes_for in your model, you need to add it to the strong_params #permit parameters (Don't forget the ID!).
I have a User who has many Accounts through a User_Accounts model. The User_Accounts model also tracks other information such as admin and billing access. Via the user edit form, I want to be able to edit the admin and billing boolean fields for the users current account.
user.rb
class User < ActiveRecord::Base
has_one :owned_account, class_name: 'Account', foreign_key: 'owner_id'
has_many :user_accounts
has_many :accounts, through: :user_accounts
accepts_nested_attributes_for :user_accounts
end
user_account.rb
class UserAccount < ActiveRecord::Base
belongs_to :account
belongs_to :user
end
In the users controller, I specified which user_account I wanted to edit via the nested form and assigned to the #user_account instance variable.
users_controller.rb
def edit
#user = User.find(params[:id])
#user_account = #user.user_accounts.find_by_account_id(current_account)
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
redirect_to #user, notice: 'User was successfully updated.'
else
render action: "edit"
end
end
private
def user_params
params.require(:user).permit(:name, :email, user_accounts_attributes: [:admin, :billing] )
end
user/edit.html.erb
<%= f.fields_for :user_accounts, #user_account do |o| %>
<%= o.check_box :admin, class: 'checkbox' %>
<% end %>
When I submit the change, it successfully saves the user record, but doesn't update the User_account record. It appears to be passing the following:
{"name"=>"Colin 21", "email"=>"mike21#example.com", "user_accounts_attributes"=>{"0"=>{"admin"=>"1"}}}
Id is required to edit an object via accepts_nested_attributes_for. Seems that the id attribute is not allowed through strong parameters.
Try changing the user_params method in 'users_controller.rb'
def user_params
params.require(:user).permit(:name, :email, user_accounts_attributes: [:id, :admin, :billing] )
end
You need to add a hidden field for user account's id field within fields_for part of the form too.
I'm unable to associate the foreign key before saving the data (the field 'user_id' for the Network model is blank when stored in the database). I'm new to RoR so please excuse me if my code is sloppy :-)
Models:
class Network < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_one :network, :foreign_key => "user_id",
:dependent => :destroy
Controller:
class UsersController < ApplicationController
def new
#user = User.new
#network = #user.build_network
#title = "Sign Up"
end
def create
#user = User.new(params[:user])
if (#user.save)
#network = Network.new(params[:network])
if (#network.save)
sign_in #user
flash[:success] = "Welcome!"
redirect_to #user
else
#title = "Sign up"
render 'new'
end
else
#title = "Sign up"
render 'new'
end
end
I'm able to get all of the user input from the view without issues (using form_for and fields_for). Do I have to explicitly define the has_one association in addition to using #user.build_network ?
Per the suggestion of using accepts_nested_attributes_for, I cleaned up my controller to be
def new
#user = User.new
#user.build_network
#title = "Sign Up"
end
def create
#user = User.new(params[:user])
if (#user.save)
sign_in #user
flash[:success] = "Welcome!"
redirect_to #user
else
#title = "Sign up"
render 'new'
end
end
The updated association in the User Model:
has_one :network, :class_name => 'Network',
:foreign_key => "user_id",
:dependent => :destroy
accepts_nested_attributes_for :network,
:reject_if => :all_blank,
:allow_destroy => true
However, the network_attributes are all blank when I submit the form. I have followed the directions in the links provided as well as http://ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes (and many more)
I don't think it's an issue with the view/form since I'm able to access the attributes using Network.new(params[:network]
Any thoughts/suggestions ?
This is a perfect situation for using accepts_nested_attributes_for
Have a look at http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html as well as http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-fields_for to see how you can easily implement it using form_for
(post-question update)
If you can access the attributes for Network through params[:network], then you have (unfortunately!) missed one of the more subtle parts of nested attributes, which is the invocation of fields_for
I'm assuming you have something along the lines of the following (using HAML syntax for speed):
= form_for #user do |f|
= f.text_field :name
= f.text_field :email_address
- fields_for #network do |n|
= n.text_field :name
The problem with this is that Rails isn't seeing any explicit connection in the form between your User and Network fields. (It's smart, but not that smart)
The way you explicitly state that Network is nested within User (for the sake of updating nested attributes is to make sure you call the fields_for #network function on the User form builder, not by itself:
= form_for #user do |f|
= f.text_field :name
= f.text_field :email_address
- f.fields_for #network do |n|
= n.text_field :name
That way, when you submit your form and inspect params, you'll notice that you've got both params[:user] and params[:user][:network] - which Rails will recognise as nested attributtes and should then save them and link them together.
It's possible you need to set network as attr_accessible, along with other attributes of User model, like this:
attr_accessible :name, :age, :network_attributes, ...
Please note you'll also need to put all attributes you want to 'mass assign' in the list, just like :name and :age in the above example.