Rails 4 - Polymorphic Association - Nested Attributes not showing in form - ruby-on-rails

I am building a login system that has two users, buyer and seller, in Rails 4.0.4.
For auth I am using the Devise gem: https://github.com/plataformatec/devise
To create a new buyer I use the route buyer/new. However, the fields for the user do not show in the view. I also using debug to show #buyer.user in the view and it has been created. But when I call f.fields_for #buyer.user do |u| the loop is never entered.
Any ideas of why this is? Also, the polymorphic associations seem to be working in the rails console.
Buyer Controller:
# GET /buyers/new
def new
#buyer = Buyer.new
#buyer.build_user
end
Buyer Model
class Buyer < ActiveRecord::Base
has_one :user, as: :role
accepts_nested_attributes_for :user
end
Buyer/new View
<%= form_for(#buyer) do |f| %>
....
<div class="field">
<%= debug(#buyer.user) %>
<% f.fields_for #buyer.user do |u| %>
<%= u.text_field :email %>
<% end %>
</div>
User Model
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
belongs_to :role, polymorphic: true

Shouldn't you have an = on the fields_for? http://rubydoc.info/docs/rails/ActionView/Helpers/FormHelper:fields_for
E.G.
<%= f.fields_for #buyer.user do |u| %>

Related

Rails 5 - Devise - User has_one association - nested form fields are not shown at sign up form

As I searched, this is a common issue, but none of the answers I found work for my case.
I have set up a User model with devise and it has two related models, it has one Contact Detail and many Addresses. The nested form works well with addresses, but my contact detail fields are not shown.
My User model is the following:
validates_presence_of :contact_detail, :addresses
# Include default devise modules. Others available are:
# :lockable, :timeoutable, :trackable and :omniauthable
devise :invitable, :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :confirmable
has_one :contact_detail, dependent: :destroy
has_many :addresses, dependent: :destroy
accepts_nested_attributes_for :addresses,
allow_destroy: true
accepts_nested_attributes_for :contact_detail,
allow_destroy: true
The contact details model only has belongs_to :user
I made the changes mentioned at devise gem at my application controller:
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [addresses_attributes: [:street_name, :street_number, :city, :country, :postal_code, :name],
contact_detail_attributes: [:first_name, :last_name, :description, :telephone, :mobile ]])
end
end
and my app/views/devise/registrations/new.html.erb file looks like this:
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
...
<div>
<% f.fields_for :contact_detail do |contact_detail|%>
<div class="field">
<%= contact_detail.label :first_name %>
<%= contact_detail.text_field :first_name %>
</div>
<div class="field">
<%= contact_detail.label :last_name %>
<%= contact_detail.text_field :last_name %>
</div>
<div class="field">
<%= contact_detail.label :description %>
<%= contact_detail.text_area :description %>
</div>
<div class="field">
<%= contact_detail.label :telephone %>
<%= contact_detail.number_field :telephone %>
</div>
<div class="field">
<%= contact_detail.label :mobile %>
<%= contact_detail.number_field :mobile %>
</div>
<% end %>
</div>
...
But my contact detail fields are not shown. Any ideas?
You have to "seed" the relation in order for the inputs for an association to appear. fields_for works like a loop. If the association is empty or nil the block runs 0 times.
Normally you would do this in the new action of your controller:
class UsersController < ApplicationController
def new
#user = User.new
#user.build_contact_detail
end
end
In Devise the new action is Devise::RegistrationsController#new which you can customize by subclassing the controller:
class MyRegistrationsController < Devise::RegistrationsController
def new
super do |user|
user.build_contact_detail
end
end
end
super do |user| ... end uses the fact that all the Devise controller actions take a block and yield the resource. This makes it really easy to customize them without copy-pasting the entire method.
You then have to alter the routes so that your custom controller is used:
Rails.application.routes.draw do
devise_for :users, controllers: {
registrations: 'my_registrations'
}
end

ActionView::Template::Error (undefined method `stage' for #<User:0x007f80045ca0e0>)

I'm having an error with a form in a view, can't get it why is happening. I keep getting ActionView::Template::Error (undefined method 'stage' for #<User:0x007f80045ca0e0>)
I have two models, User and Stage. User has_many stages, and stages belongs_to to user. It's as follows
The Stage Model:
class Stage < ActiveRecord::Base
belongs_to :user
end
The User Model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :user_setting
has_many :matches, dependent: :destroy
has_many :stages, dependent: :destroy
end
and in the user controller I have as follows:
class UsersController < ApplicationController
def show
#newMatch = current_user.matches.new
#newStage = current_user.drivepipes.new
end
end
and a form on the show.html.erb
<%= form_for [current_user], url: user_stages_path(current_user, #newStage) do |s| %>
<%= s.text_field :stage, placeholder: "Stage" %>
<%= s.submit "Save" %>
<% end %>
If each user has many stages and you're making a form for your user, <%= s.text_field :stage, placeholder: "Stage" %> is going to give you an error because the user has_many :stages which is an enumerable. So you're going to want something like
<%= form_for [current_user], url: user_stages_path(current_user, #newStage) do |s| %>
<% current_user.stages.each do |stage| %>
<%= s.text_field stage, placeholder: "Stage" %>
<% end %>
<%= s.submit "Save" %>
<% end %>
Assuming you want a text field for every stage the user has, that is. Perhaps that's not the goal here?
There is one-to-many relationship between User and Stage. So, foreign key will reside in Stage table i.e. user_id. You should make clear that You can't have an attribute 'stage' or stage_id in User object if it has many stages. However Rails have helpers:
#user.stages
will return array of stages which have been saved with this user's id, using query:
select * from stages where stages.user_id = #user.id
While:
#user.stage
will raise error.
If you want to get input for newly created stage object associated with current user, You view code should look like this:
<%= form_for current_user, url: user_stages_path(current_user, #newStage) do |user_form| %>
<%= user_form.fields_for #newStage do |stage| %>
<%= stage.text_field :stage, placeholder: "Stage" %>
<% end %>
<%= user_form.submit "Save" %>
<% end %>
Later, in controller; you can get the stage object in the similar way:
stage = Stage.new params[:user][:stage]

Rails 4 - Access attributes of nested model in form

I have Account model which have a has_many relationship with User model:
class Account < ActiveRecord::Base
has_many :users, -> { uniq }
accepts_nested_attributes_for :users
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :confirmable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
belongs_to :account
I added avatar attribute to User model using paperclip.
I want each user to have access to the common account settings, and inside it having the possibility to upload his/her own avatar.
I use simple_form so I tried this:
<%= simple_form_for current_account, :html => { :multipart => true } do |f| %>
<%# here come account settings %>
<%= f.input :time_zone, :label => t(".timezone"),
:
:
<%# here I need to access current user attributes %>
<%= f.simple_fields_for :user, current_account.users.first do |user_form| %>
<%= user_form.file_field :avatar, :error => false %>
<% end %>
<% end %>
First problem:
I need some logic to access current_user instead of current_account.users.first. Since there is a superadmin which can access all accounts, use current_user is not enough.
Second (and bigger) problem:
I added in my controller the avatar parameter to the whitelist:
def allowed_params
params.require(:account).permit(:time_zone, :logo, :description, user: [:avatar])
end
When I try to update my model:
if current_account.update(allowed_params)
I get this error:
unknown attribute: user
I also tried:
params.require(:account).permit(:language, :time_zone, :logo, :description, :user_attributes => [:avatar])
and:
params.require(:account).permit(:language, :time_zone, :logo, :description, :users_attributes => [:avatar])
(in plural)
but since I use ActionController::Parameters.action_on_unpermitted_parameters = :raise I get:
found unpermitted parameters: user
It must be something very easy, some help please?
Ok, got it!!
The problem is the one-to-many relationship and the way I tried to access a single instance of user. The correct way to do it is:
<% current_account.users.each_with_index do |user, index|%>
<%= f.simple_fields_for :users, user do |user_form| %>
<%= user_form.file_field :avatar, :error => false %>
<% end %>
<% end %>
As you can see, the iteration should be done over the relation, and only when having a
single instance "in hand" we can user the simple_fields_for.
Also, notice that the first parameter passed to simple_fields_for is :users and not :user, since this is a one-to-many relationship.

Devise and ActiveModel::MassAssignmentSecurity::Error

I am working on the registration process via devise
I try to save "payments" to a "user" which registers via a formular.
When User choose "bank" from a checkbox, a depending Payment should be also created to the User.
<!-- language: ruby -->
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable
attr_accessible :email, :password, :password_confirmation, :remember_me, :first_name, :last_name
attr_accessible :payments_attributes
# also tried attr_accessible :payments_attributes, :payments
has_many :payments, :autosave => true
accepts_nested_attributes_for :payments, allow_destroy: false
end
class Payment < ActiveRecord::Base
attr_accessible :method, :paid
attr_accessor :method, :paid
belongs_to :user
end
new.html.erb
<%= f.fields_for :payment do |payment| %>
<div>
<%= payment.radio_button(:method, 'bank') %>
<%= payment.label :bank_transfer %>
<%= payment.radio_button(:method, 'paypal') %>
<%= payment.label :paypal %>
</div>
<% end %>
These are the attributes I wanna set:
my_attributes = {"first_name"=>"Max", "last_name"=>"Mustermann", "password"=>"12345678", "password_confirmation"=>"12345678", "email"=>"max#mustermann.at", "company"=>"ACME INC", "industry"=>{"title"=>"Metall", "oenace"=>"123"}, "payments"=>{"method"=>"bank_transfer", "paid"=>"false"}}
User.new(my_attributes)
# ActiveModel::MassAssignmentSecurity::Error: Can't mass-assign protected attributes: payments
I also tried to add this to the user model:
def after_initialize
self.payments.build if self.payments.blank?
end
Any ideas or suggestions why these params not being saved?
You are trying to assign payments instead of payments_attributes. If this is being returned by fields_for in a view, it is most likely caused by lack of accepts_nested_attributes_for :payments inside your User model.
UPDATE:
you missed 's' in name of your association passed to fields_for. Should be
fields_for :payments do |payment|

Use both Account and User tables with Devise

I'm working with Rails 3.1.0 and Devise 1.4.8, and am new to both.
I want to allow multiple users for an account. The first user to sign up creates the account (probably for their company), then that user can add more users. A user is always linked to exactly one account.
I have Users and Accounts tables. Abbreviated models:
class User < ActiveRecord::Base
belongs_to :account
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable, :lockable, :timeoutable
attr_accessible :email, :password, :password_confirmation, :remember_me
end
class Account < ActiveRecord::Base
has_many :users, :dependent => :destroy
attr_accessible :name, :account_type
end
The question is, when the first user signs up, how do I create both the Account and User?
Do I need to modify/override the Devise registrations_controller,
something like the answer here? I couldn't figure out how to
create the Account then pass it to Devise for creating the User.
account_id is already in the User model. Should I add account_name
and account_type to the User model, and create a new the Account
record if account_id is not passed in? In this case, I'm trying to hide Account creation from Devise, but not sure that will work since I still need to prompt for account_name and account_type on the registration page.
Finally got this to work using nested attributes. As discussed in the comments on Kenton's answer, that example is reversed. If you want multiple users per account, you have to create the Account first, then the User--even if you only create one user to start with. Then you write your own Accounts controller and view, bypassing the Devise view. The Devise functionality for sending confirmation emails etc. still seems to work if you just create a user directly, i.e. that functionality must be part of automagical stuff in the Devise model; it doesn't require using the Devise controller.
Excerpts from the relevant files:
Models in app/models
class Account < ActiveRecord::Base
has_many :users, :inverse_of => :account, :dependent => :destroy
accepts_nested_attributes_for :users
attr_accessible :name, :users_attributes
end
class User < ActiveRecord::Base
belongs_to :account, :inverse_of => :users
validates :account, :presence => true
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable, :lockable, :timeoutable
attr_accessible :email, :password, :password_confirmation, :remember_me
end
spec/models/account_spec.rb RSpec model test
it "should create account AND user through accepts_nested_attributes_for" do
#AccountWithUser = { :name => "Test Account with User",
:users_attributes => [ { :email => "user#example.com",
:password => "testpass",
:password_confirmation => "testpass" } ] }
au = Account.create!(#AccountWithUser)
au.id.should_not be_nil
au.users[0].id.should_not be_nil
au.users[0].account.should == au
au.users[0].account_id.should == au.id
end
config/routes.rb
resources :accounts, :only => [:index, :new, :create, :destroy]
controllers/accounts_controller.rb
class AccountsController < ApplicationController
def new
#account = Account.new
#account.users.build # build a blank user or the child form won't display
end
def create
#account = Account.new(params[:account])
if #account.save
flash[:success] = "Account created"
redirect_to accounts_path
else
render 'new'
end
end
end
views/accounts/new.html.erb view
<h2>Create Account</h2>
<%= form_for(#account) do |f| %>
<%= render 'shared/error_messages', :object => f.object %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<%= f.fields_for :users do |user_form| %>
<div class="field"><%= user_form.label :email %><br />
<%= user_form.email_field :email %></div>
<div class="field"><%= user_form.label :password %><br />
<%= user_form.password_field :password %></div>
<div class="field"><%= user_form.label :password_confirmation %><br />
<%= user_form.password_field :password_confirmation %></div>
<% end %>
<div class="actions">
<%= f.submit "Create account" %>
</div>
<% end %>
Rails is quite picky about plural vs. singular. Since we say Account has_many Users:
it expects users_attributes (not user_attributes) in the model and tests
it expects an array of hashes for the test, even if there is only one element in the array, hence the [] around the {user attributes}.
it expects #account.users.build in the controller. I was not able to get the f.object.build_users syntax to work directly in the view.
Couldn't you use something like what's covered in these RailsCasts?:
http://railscasts.com/episodes/196-nested-model-form-part-1
http://railscasts.com/episodes/197-nested-model-form-part-2
You could setup your models as described in those screencasts, using accepts_nested_attributes_for.
Then, your views/devise/registrations/new.html.erb form would be for :user like normal, and could include a nested form for :account.
So something like this within that default form:
<%= f.fields_for :account do |account_form| %>
<div>
<p>
<%= account_form.label :name, "Account Name", :class => "label" %>
<%= account_form.text_field :name, :class => "text_field" %>
<span class="description">(e.g., enter your account name here)</span>
</p>
</div>
<div>
<p>
<%= account_form.label :company, "Company Name", :class => "label" %>
<%= account_form.text_field :company, :class => "text_field" %>
</p>
</div>
<% end %>
This is sample code from an app I'm working on and I'm using the simple_form gem, so the helpers used in your app may be different, but you'll probably get the idea.
So when a user is created (when they register), they can also fill in the info that'll be used by the Account model to create their account once they hit the "Sign Up" button.
And you may want to set an attribute on that user like "admin" too...sounds like this initial user will be the "admin" user for the company, though other users may have access too.
Hope that helps.
The best solution would be to use a gem.
Easy way: milia gem
Subdomain way: apartment gem

Resources