Associating (has_one) multiple models in form - ruby-on-rails

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.

Related

Rails5. Nested attributes are not going to database

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

Displaying the attributes of another table

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

How to create an event registration rails structure

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!).

Ruby on Rails Saving in two tables from one form

I have two models Hotel and Address.
Relationships are:
class Hotel
belongs_to :user
has_one :address
accepts_nested_attributes_for :address
and
class Address
belongs_to :hotel
And I need to save in hotels table and in addresses table from one form.
The input form is simple:
<%= form_for(#hotel) do |f| %>
<%= f.text_field :title %>
......other hotel fields......
<%= f.fields_for :address do |o| %>
<%= o.text_field :country %>
......other address fields......
<% end %>
<% end %>
Hotels controller:
class HotelsController < ApplicationController
def new
#hotel = Hotel.new
end
def create
#hotel = current_user.hotels.build(hotel_params)
address = #hotel.address.build
if #hotel.save
flash[:success] = "Hotel created!"
redirect_to #hotel
else
render 'new'
end
end
But this code doesn't work.
ADD 1
Hotel_params:
private
def hotel_params
params.require(:hotel).permit(:title, :stars, :room, :price)
end
ADD 2
The main problem is I don't know how to render form properly. This ^^^ form doesn't even include adress fields (country, city etc.). But if in the line
<%= f.fields_for :address do |o| %>
I change :address to :hotel, I get address fields in the form, but of course nothing saves in :address table in this case. I don't understand the principle of saving in 2 tables from 1 form, I'm VERY sorry, I'm new to Rails...
You are using wrong method for appending your child with the parent.And also it is has_one relation,so you should use build_model not model.build.Your new and create methods should be like this
class HotelsController < ApplicationController
def new
#hotel = Hotel.new
#hotel.build_address #here
end
def create
#hotel = current_user.hotels.build(hotel_params)
if #hotel.save
flash[:success] = "Hotel created!"
redirect_to #hotel
else
render 'new'
end
end
Update
Your hotel_params method should look like this
def hotel_params
params.require(:hotel).permit(:title, :stars, :room, :price,address_attributes: [:country,:state,:city,:street])
end
You should not build address again
class HotelsController < ApplicationController
def new
#hotel = Hotel.new
end
def create
#hotel = current_user.hotels.build(hotel_params)
# address = #hotel.address.build
# the previous line should not be used
if #hotel.save
flash[:success] = "Hotel created!"
redirect_to #hotel
else
render 'new'
end
end
Bottom line here is you need to use the f.fields_for method correctly.
--
Controller
There are several things you need to do to get the method to work. Firstly, you need to build the associated object, then you need to be able to pass the data in the right way to your model:
#app/models/hotel.rb
Class Hotel < ActiveRecord::Base
has_one :address
accepts_nested_attributes_for :address
end
#app/controllers/hotels_controller.rb
Class HotelsController < ApplicationController
def new
#hotel = Hotel.new
#hotel.build_address #-> build_singular for singular assoc. plural.build for plural
end
def create
#hotel = Hotel.new(hotel_params)
#hotel.save
end
private
def hotel_params
params.require(:hotel).permit(:title, :stars, :room, :price, address_attributes: [:each, :address, :attribute])
end
end
This should work for you.
--
Form
Some tips for your form - if you're loading the form & not seeing the f.fields_for block showing, it basically means you've not set your ActiveRecord Model correctly (in the new action)
What I've written above (which is very similar to that written by Pavan) should get it working for you

Rails 4 - Has_many, Using Nested Form to Edit a Specfic Record

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.

Resources