Is this polymorphic association being set up correctly? - ruby-on-rails

I have two account types with the relationship below laid out in models below. I need to build a registration form for #account that has a form select field where a user can select to register for either a student or partner account and for this account record to save according to the selection (i.e. to the students or partners table and the accounts table).
I'm running into issues on the controller Accounts#new method and I'm not sure how to set this up in a way that works.
Account:
class Account < ApplicationRecord
belongs_to :acct_holderable, :polymorphic => true
Student:
class Student < ApplicationRecord
has_one :account, :as => :acct_holderable
Partners:
class Partner < ApplicationRecord
has_one :account, :as => :acct_holderable
View for Accounts#new
<%= form_for(#account) do |f| %>
<%= render 'shared/error_messages', object: #account %>
<%= f.label :account_type %>
<%= f.select :acct_holderable, options_for_select(account_type, #account.account_holderable_type), class: 'form-control' %>
<%= f.label :first_name %>
<%= f.text_field :first_name, class: 'form-control' %>
<%= f.label :last_name %>
<%= f.text_field :last_name, class: 'form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit yield(:button_text), class: "btn btn-primary" %>
<% end %>
Account Helper (for options_select)
def account_type
input =<<-OPTIONS
Student,
Partner,
Other Account TBU,
Other Account TBU
input.split(',')
end
Accounts controller
def new
#account = Account.new
end
def create
#account = Account.new(account_params)
if #account.save
#account.send_activation_email
flash[:info] = "Please check your email to activate your account before logging in!"
redirect_to login_url
else
render 'new'
end
end

Pls check the following link for more info http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
https://6ftdan.com/allyourdev/2015/02/10/rails-polymorphic-models/
These links help you to get better understand about polymorphic association.

Related

Rails Submitting Multiple Has_Many Attributes via Form

I am running a Rails 5.1 app with the following information:
Models
class Company < ApplicationRecord
has_many :complaints
accepts_nested_attributes_for :complaints
validates :name, presence: true
end
class Complaint < ApplicationRecord
belongs_to :company
validates :username, :priority, presence: true
end
Controller
class ComplaintController < ApplicationController
def new
#company = Company.new
#company.complaints.build
end
def create
#company = Company.new(company_params)
respond_to do |format|
if #company.save
format.html { redirect_to complaint_url }
else
format.html { render :new }
end
end
end
private
def company_params
params.require(:company).permit(:name, complaints_attributes: [:username, :priority])
end
Form in view
<%= form_for #company do |f| %>
<%= f.label :name, "Company" %>
<%= f.text_field :name, type: "text" %>
<%= f.fields_for :complaints do |complaint| %>
<%= complaint.label :username, "Username" %>
<%= complaint.text_field :username %>
<%= complaint.label :priority, "Priority" %>
<%= complaint.text_field :priority %>
<% end %>
<%= f.submit 'Submit' %>
<% end %>
If I have just one input field for the complaint_attributes part of the form (in other words just one field for username and one field for priority as shown above), this works just fine.
However, if I want to have multiple fields for username/priority in the form, so that I can submit multiple username/priority combinations in a single submission, I find that submitting the form will only save the last username/priority values from the form. Example of this view would be:
<%= form_for #company do |f| %>
<%= f.label :name, "Company" %>
<%= f.text_field :name, type: "text" %>
<%= f.fields_for :complaints do |complaint| %>
<div>
<%= complaint.label :username, "Username" %>
<%= complaint.text_field :username %>
<%= complaint.label :priority, "Priority" %>
<%= complaint.text_field :priority %>
</div>
<div>
<%= complaint.label :username, "Username" %>
<%= complaint.text_field :username %>
<%= complaint.label :priority, "Priority" %>
<%= complaint.text_field :priority %>
</div>
<% end %>
<%= f.submit 'Submit' %>
<% end %>
I noticed that when submitting the form, I get a hash like this (for submitting single complaint):
{"utf8"=>"✓", "authenticity_token"=>"...", "company"=>{"name"=>"Test", "complaints_attributes"=>{"0"=>{"username"=>"test_person", "priority"=>"1"}}}, "commit"=>"Submit"}
Is there any way to modify the params to make it similar to this and have it saved to the DB?:
{"utf8"=>"✓", "authenticity_token"=>"...", "company"=>{"name"=>"Test", "complaints_attributes"=>{"0"=>{"username"=>"test_person", "priority"=>"1"}"1"=>{"username"=>"test_person", "priority"=>"2"}}}, "commit"=>"Submit"}
Or if not the above, what would be the best way to have the username/priority values saved if using multiple fields for them in a single form?
EDIT: I should point out that I can dynamically add the username/priority field groups as needed, so I don't want to be restricted to a set number.
the second block will override the first fields... you should instead build many complaints in the controller:
def new
#company = Company.new
3.times { #company.complaints.build }
end
and then with the following form it should generate to inputs according to the number of complaints you have built:
<%= form_for #company do |f| %>
<%= f.label :name, "Company" %>
<%= f.text_field :name, type: "text" %>
<%= f.fields_for :complaints do |complaint| %>
<%= complaint.label :username, "Username" %>
<%= complaint.text_field :username %>
<%= complaint.label :priority, "Priority" %>
<%= complaint.text_field :priority %>
<% end %>
<%= f.submit 'Submit' %>
<% end %>

Cannot get a nested model to save in rails 5

I have a user which is a nested resource of account, and I'm trying to create the account and the user in 1 form. The account is saving correctly, however no user record is being written.
user.rb:
class User < ApplicationRecord
belongs_to :account
validates :email, presence: true
end
account.rb:
class Account < ApplicationRecord
has_many :users, :dependent => :destroy
accepts_nested_attributes_for :users
end
accounts_controller.rb:
class AccountsController < ApplicationController
def new
#account = Account.new
#user = #account.users.build
end
def create
#account = Account.new(account_params)
#account.secret = SecureRandom.uuid
if #account.save
flash[:success] = "You've successfully created your account, now it's time to create your first team."
redirect_to dashboard_path
else
flash[:danger] = "There was a problem creating your account"
redirect_to signup_path
end
end
private
def account_params
params.require(:account).permit(:name, user_attributes: [:first_name, :last_name, :email, :password, :password_confirmation, :secret])
end
end
accounts/new.html.erb:
<h1>Signup</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#account) do |form| %>
<%= form.label "Account name" %>
<%= form.text_field :name, class: "form-control" %>
<%= form.fields_for :user do | f| %>
<%= f.label :first_name %>
<%= f.text_field :first_name, class: "form-control" %>
<%= f.label :last_name %>
<%= f.text_field :last_name, class: "form-control" %>
<%= f.label :email %>
<%= f.text_field :email, class: "form-control" %>
<%= f.label :password %>
<%= f.password_field :password, class: "form-control" %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: "form-control" %>
<% end %>
<%= form.submit "Signup", class: "btn btn-primary" %>
<% end %>
</div>
</div>
When I submit the the form and check my logs, I'm seeing:
Processing by AccountsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"p5+CRICRp3ZilIg1NtehEQG7Gh2amnFFUD5NawtqkICJ4uRDvvJxf2WTbd7+rnfG9zdblT1QWgfJ62NNxcc2RA==", "account"=>{"name"=>"Streame", "user"=>{"first_name"=>"Jeremy", "last_name"=>"Kirkham", "email"=>"jeremy#streame.com.au", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}}, "commit"=>"Signup"}
Unpermitted parameter: user
The answer was quite simple in the end - I needed to change the form from
<%= form.fields_for :user do | f| %>
to
<%= form.fields_for :users do | f| %>
(note user -> users), and the controller from
params.require(:account).permit(:name, user_attributes:
to
params.require(:account).permit(:name, users_attributes:
(again note user -> users).
Basically it came down to pluralisation!

Create two interdependent models from one form in rails

I have used a combination of answers from 21222015, 17572279, 2663141, and RailsGuide to try and get multiple interdependent models to be created from a form.
In my application Users are used for authentication and Clinicians and Patients are models for the two different types of Users. Clinicians and Patients have almost no attributes in common so creating separate models made sense.
I would like to be able to create a patient or clinician and a user at the same time on a form. Patients and clinicians are both connected to their user by a user_id integer field.
Right now patients and clinicians belongs_to a user (patients also belongs_to a clinician and a clinician has_many patients):
class Patient < ActiveRecord::Base
belongs_to :clinician
belongs_to :user
def set_user_id_from_user
patient.user_id = user.id
end
before_validation :set_user_id_from_user
end
A user has_one patient or clinician:
class User < ActiveRecord::Base
has_secure_password
has_one :patient, :dependent => :destroy
has_one :clinician, :dependent => :destroy
accepts_nested_attributes_for :patient, :allow_destroy => true
accepts_nested_attributes_for :clinician, :allow_destroy => true
validates :email, presence: true
validates_uniqueness_of :email
end
I am trying to create a user and a patient on the new.html.erb - patients page using accepts_nested_attributes_for. I followed the RailsCast Nested Model Form Part 1 as it was recommended as an answer to SO question 10058584. This is my form:
<%= form_for #user do |form| %>
<p>
<div class="form-group">
<%= form.label :email %>
<%= form.text_field :email, class: "form-control", placeholder: "email address" %>
</div>
<div class="form-group">
<%= form.label :password %>
<%= form.password_field :password, class: "form-control", placeholder: "enter password" %>
</div>
</p>
<%= form.fields_for :patient do |builder| %>
<p>
<div class="form-group">
<%= builder.label :first_name %>
<%= builder.text_field :first_name, class: "form-control", placeholder: "First name" %>
</div>
<div class="form-group">
<%= builder.label :last_name %>
<%= builder.text_field :last_name, class: "form-control", placeholder: "Last name" %>
</div>
<div class="form-group">
<%= builder.label :diagnosis %>
<%= builder.text_field :diagnosis, class: "form-control", placeholder: "Diagnosis" %>
</div>
<div class="form-group">
<%= builder.label :gender_id %>
<%= builder.collection_select :gender_id, Gender.all, :id, :gender_type, :prompt => true, class: "form-control" %>
</div>
<div class="form-group">
<%= builder.label :age %>
<%= builder.text_field :age, class: "form-control", placeholder: "Age" %>
</div>
</p>
<% end %>
<%= form.button 'Create Patient', class: "btn btn-u btn-success" %>
<% end %>
In my UsersController I have:
def new
#user = User.new
end
def create
#user = User.create(user_params)
if #user.save
redirect_to user_path(#user), notice: "User created!"
else
render "new"
end
end
private
def user_params
params.require(:user).permit(:email, :password, patient_attributes: [ :first_name,:last_name,:user_id,:diagnosis,:gender_id,:age,:address, :email, :password, :phone_number, :caregiver_name, :other_symptom, :goals_of_care, :patient_deceased, :patient_archived ])
end
All of this currently gives me a form that only shows the two user fields: email and password. When submitted I get a new user created but no patient is created.
How do I get the patient fields to appear on the form and have a patient created that has a user_id that connects it to the user created at the same time?
Eventually I need to have it possible to create either a patient or a clinician when a user is created; maybe by making it so that that a patient/clinician accepts_nested_attributes_for a user and have the form for each?
Any advice would be really appreciated, thanks
I think that in order for your patient form fields to show up, you need to say #user.build_patient in your new action in your UsersController. This initializes a patient that is associated with the User instance you created.
In case it helps anyone else, if the relationship between patient and user was a has_many instead of a has_one, then the syntax would be #user.patient.build thenew action.

How to build a devise nested resource during registration?

During registration of a new user with Devise, I need to create a new Family object link to this new user at the same time (the user being the head of the family).
My family model:
belongs_to user
My user model:
attr_accessible :name, :email, :password, :password_confirmation, :remember_me, :family
has_one :family
accepts_nested_attributes_for :family
In devise/registration/new.html.erb
<%= simple_form_for([resource, Family.new], :as => resource_name, :url => registration_path(resource_name), :html => {:class => 'form-vertical' }) do |f| %>
<%= f.error_notification %>
<%= display_base_errors resource %>
<%= f.input :name, :autofocus => true %>
<%= f.input :email, :required => true %>
<%= f.input :password, :required => true %>
<%= f.input :password_confirmation, :required => true %>
<% f.fields_for :family do |family_form| %>
<p><%= family_form.label :name %></p>
<p><%= family_form.text_field :name %></p>
<% end %>
<%= f.button :submit, 'OK', :class => 'btn-primary' %>
<% end %>
But this is not working, I find a couple of question like this but I did not manage to fix that.
Any idea ?
UPDATE 1
I got the following error:
undefined method `email' for #<Family:0x007f892a12c310>
Family is a model that do not have any email, just a name. I just need to be able to create a new Family object with a name when creating a new user (and link it to the user as well).
UPDATE 2
I have added resource.build_family in my registrations controller. The family object is correctly created and associated to the user (I can display <%= resource.family %> in new.html.erb for debugging), but still no form displayed for the family.
You need the equal sign in the <%=fields_for
<%= f.fields_for :family do |family_form| %>
<p><%= family_form.label :name %></p>
<p><%= family_form.text_field :name %></p>
<% end %>
And in your user model you need to make the :family_attribues accessible and not :family
attr_accessible :name, :email, :password, :password_confirmation, :remember_me, :family_attributes
has_one :family
accepts_nested_attributes_for :family
If you're getting undefined method 'email' for #<Model:0x007f892a12c310>:
You need to overwrite Devise::RegistrationsController as described in the docs: https://github.com/heartcombo/devise#configuring-controllers. E.g.
class Users::RegistrationsController < Devise::RegistrationsController
def new
super do |resource|
resource.build_<model>
end
end
end
And you must only specify resource in form_for: simple_form_for(resource, ... instead of simple_form_for([resource, Model.new], ...

AssociationTypeMismatch (Object expected, got HashWithIndifferentAccess) in Rails app

I'm getting a AssociationTypeMismatch Error and I'm not sure where I'm making a mistake. I'm pretty new to Rails so I'm guessing I'm making some silly mistake. I've checked my syntax and compared it against AssociationTypeMismatch Error on Ruby on Rails app
... but I still can't seem to catch the error.
Here's my models
class User < ActiveRecord::Base
attr_accessible :email, :full_name, :password, :password_confirmation, :preference
has_secure_password
validates_uniqueness_of :email
validates_presence_of :full_name
has_one :preference, :dependent => :destroy
accepts_nested_attributes_for :preference
end
class Preference < ActiveRecord::Base
attr_accessible :background_fill, :background_position, :layout
belongs_to :user
end
Here's my controller:
def new
#user = User.new
#user.build_preference
end
def create
#user = User.new(params[:user])
if #user.save
session[:user_id] = #user.id
redirect_to root_url, notice: "Thanks for signing up!"
else
render "new"
end
end
Here's my view:
<%= form_for #user do |f| %>
<% if #user.errors.any? %>
<div class="error_messages">
<h2>There's an error!</h2>
<ul>
<% #user.errors.full_message.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.label :full_name %>
<%= f.text_field :full_name, :class => "target", :placeholder => "Your full name", :maxlength => "55", :autofocus => "autofocus" %>
<%= f.label :email %>
<%= f.email_field :email, :class => "target", :placeholder => "example#gmail.com", :maxlength => "55" %>
<%= f.label :password %>
<%= f.password_field :password, :class => "target", :placeholder => "Enter a password", :maxlength => "55" %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, :class => "target", :placeholder => "Enter your password again", :maxlength => "55" %>
<%= f.fields_for :preference do |builder| %>
<%= builder.hidden_field :layout, value: params[:layout] %>
<%= builder.hidden_field :background_fill, value: params[:background_fill] %>
<%= builder.hidden_field :background_position, value: params[:background_position] %>
<% end %>
<%= f.submit "Create an Account", :class => "button cta" %>
<% end %>
And my parameters
{"utf8"=>"✓",
"authenticity_token"=>"[redacted]=",
"user"=>{"full_name"=>"test",
"email"=>"test#example.com",
"password"=>"[FILTERED]",
"password_confirmation"=>"[FILTERED]",
"preference"=>{"layout"=>"layout-3",
"background_fill"=>"restaurant-2",
"background_position"=>"position-center"}},
"commit"=>"Create an Account"}
Edit:
The error I get is Preference(#70124732528700) expected, got ActiveSupport::HashWithIndifferentAccess(#70124687315200). My understanding is that #user.build_preference and accepts_nested_attributes_for :preference would just work on its own.
Do I need to create a def preferences_attributes= as per?
http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-fields_for
Update
Okay so I think I'm getting a bit closer. Poking around in the rails console, I figured I need to create Preference.new inside the UserController before I can pass in the hash. Since I'm not sure sure what build_preference does exactly, I'm not having any luck yet.
I've tried adding #user.preference = Preference.new above build preference and changing f.field_for :preference to f.field_for #user.preference but I'm still getting the same error.
Update 2
For anyone else that's stuck on this problem, the answer is to change f.field_for :preference to f.field_for :preference_attributes. See comment below by zetetic.
it is:
attr_accessible :preference_attributes
and in your form:
<%= f.fields_for :preference_attributes do |builder| %>
...
Give a shot.
In your User model, try to add :preference_attributes to your attr_accessible line.
class User < ActiveRecord::Base
attr_accessible :email, :full_name, :password, :password_confirmation, :preference_attributes
.. # rest of your code goes here
end

Resources