I have polymorphic associations with different models that I want to save upon submitting the registration form using devise. i.e:
User
Company + ContactInfo
Employee + ContactInfo
I understand that nesting forms is not recommended but What would be the best way to achieve this?
Thanks
models:
class User < ActiveRecord::Base
belongs_to :company
accepts_nested_attributes_for :company
end
class Company < ActiveRecord::Base
has_many :employees
has_one :contact_info, as: :contactable
accepts_nested_attributes_for :contact_info
end
class Employee < ActiveRecord::Base
belongs_to :company
has_one :contact_info, as: :contactable
accepts_nested_attributes_for :contact_info
end
class ContactInfo < ActiveRecord::Base
belongs_to :imageable, polymorphic: true
end
migrations:
class CreateCompanies < ActiveRecord::Migration
def change
create_table :companies do |t|
t.string :name
t.references :contact_info, index: true
t.string :website
t.timestamps null: false
end
add_foreign_key :companies, :contact_infos
end
end
class CreateEmployees < ActiveRecord::Migration
def change
create_table :employees do |t|
t.string :first_name
t.string :last_name
t.references :company, index: true
t.references :contact_info, index: true
t.string :job_title
t.timestamps null: false
end
add_foreign_key :employees, :contact_infos
end
end
class CreateContactInfos < ActiveRecord::Migration
def change
create_table :contact_infos do |t|
t.string :email
t.string :phone
t.string :mobile
t.string :contactable_type
t.integer :contactable_id
t.timestamps null: false
end
end
end
registration controller:
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up).push(:name, :email, :password, company_attributes: [ :id, :name, :website, :company_type, :number_of_employees, contact_info_attributes: [ :id, :email, :phone, :mobile]])
end
devise's new registration:
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :role => 'form'}) do |f| %>
<%= devise_error_messages! %>
<%= hidden_field_tag 'plan', params[:plan] %>
<% resource.build_company %>
<%= f.fields_for :company do |f| %>
<%= render "companies/fields", f: f %>
<% end %>
<% resource.company.build_contact_info %>
<%= f.fields_for :contact_info do |f| %>
<%= render "contact_infos/fields", f: f %>
<% end %>
<div class="form-group">
<%= f.label :name %>
<%= f.text_field :name, :autofocus => true, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
</div>
<%= f.submit 'Sign up', :class => 'button right' %>
<% end %>
Console:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Nb6Na8P93s1dYvlDIQuiG11IoDeSzSylH4BCN8Tm7ipxCsbsdiWjDx5tJpijwldkjK4pPfjuwROnEvybYS7UIQ==", "plan"=>"free", "user"=>{"company_attributes"=>{"name"=>"Company Name", "website"=>"company website", "company_type"=>"company type", "number_of_employees"=>"121"}, "contact_info"=>{"email"=>"company#email.com", "phone"=>"1234", "mobile"=>"1234"}, "name"=>"user_name", "email"=>"user_email#email.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Sign up"}
Unpermitted parameter: contact_info
only User and company params are saving well. I was trying to get the devise_parameter_sanitizer to work with nesting contact_info in company first without trying the same with employee just yet, any idea what I'm doing wrong or any tips if im on the right track?
Thanks!
Update:
However contact_info params are permitted if I nest the contact_info form fields within the company form fields like so:
<% resource.build_company %>
<%= f.fields_for :company do |f| %>
<%= render "companies/fields", f: f %>
<% resource.company.build_contact_info %>
<%= f.fields_for :contact_info do |cf| %>
<%= render "contact_infos/fields", f: cf %>
<% end %>
<% end %>
the question is, is that good practice?
This is a guess - but your contact_info for the company isn't nested inside the company section. Try something like this (note, not tested, probably buggy)
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :role => 'form'}) do |f| %>
<%= devise_error_messages! %>
<%= hidden_field_tag 'plan', params[:plan] %>
<% resource.build_company %>
<%= f.fields_for :company do |f| %>
<%= render "companies/fields", f: f %>
<%# this is now nested inside the company-fields %>
<% resource.company.build_contact_info %>
<%= f.fields_for :contact_info do |f| %>
<%= render "contact_infos/fields", f: f %>
<% end %>
<% end %>
<div class="form-group">
<%= f.label :name %>
<%= f.text_field :name, :autofocus => true, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
</div>
<%= f.submit 'Sign up', :class => 'button right' %>
Related
I added a nested attribute to my form, the fields for the nested attribute which is Education fields do not render, the other fields do. The relationships appear to be in order and the controller does too.
Here is the code.
Controller
def new
#profile = current_user.build_student_profile
end
def profile_params
params.require(:student_profile).permit(:first_name, :last_name, :gender, student_profiles_attributes: [:degree, :university_id, :major, :major2, :start_date, :end_date, :grade, :grade_scale] )
end
Models
class Education < ActiveRecord::Base
belongs_to :student_profile
belongs_to :university
validates :grade_scale, inclusion: { in: %w(GPA4 GPA7 WAM100) }
validates :degree, :university_id, :major, :start_date, :end_date, :grade, :grade_scale, presence: true
end
class StudentProfile < ActiveRecord::Base
belongs_to :user
has_many :educations
validates :gender, inclusion: { in: %w(male female) }
validates :first_name, :last_name, :gender, presence: true
accepts_nested_attributes_for :educations
end
Form
<%= form_for (#profile) do |f| %>
<%= f.label :first_name %>
<%= f.text_field :first_name %>
<%= f.label :last_name %>
<%= f.text_field :last_name %>
<%= f.label :gender %>
<%= f.text_field :gender %>
<%= f.fields_for :educations do |education_fields| %>
<%= education_fields.label :Degree %>
<%= education_fields.text_field :degree %>
<%= education_fields.label :University %>
<%= education_fields.collection_select(:university_id, University.all, :id, :name) %>
<%= education_fields.label :Major %>
<%= education_fields.text_field :major %>
<%= education_fields.label :Additional_Major %>
<%= education_fields.text_field :major2 %>
<%= education_fields.label :Start_Date %>
<%= education_fields.date_field :start_date %>
<%= education_fields.label :End_Date %>
<%= education_fields.date_field :end_date %>
<%= education_fields.label :Grade %>
<%= education_fields.number_field :grade %>
<%= education_fields.label :Grade_Scale %>
<%= education_fields.select :grade_scale, [["GPA / 4","GPA4"], ["GPA / 7","GPA7"], ["WAM / 100","WAM100"]] %>
<% end %>
<%= f.submit :submit %>
<% end %>
I have tried to add the following to the controller new action #profile.educations.build but I get an error unknown attribute student_profile_id
Can anyone help ?
Make sure you have student_profile_id attribute/column present in educations table.
After that, as you have mentioned, you need to build educations object on student_profile as:
def new
#profile = current_user.build_student_profile
#profile.educations.build
end
Try this
<%= f.fields_for(:educations,#profile.educations.build) do |education_fields| %>
<% end %>
or
def new
#profile = current_user.build_student_profile
#educations = #profile.educations.build
end
<%= f.fields_for(#educations) do |education_fields| %>
<% end %>
There is a User model and Company model.
User.rb
class User < ActiveRecord::Base
has_and_belongs_to_many :companies
end
Company.rb
class Company < ActiveRecord::Base
has_and_belongs_to_many :users
end
New.html.erb
<%= simple_form_for(#user) do |f| %>
<% if policy_scope(Company).count <= 1 %>
<%= f.label :company, "Company: "%><br />
<%= f.text_field :company, :value => policy_scope(Company).first %><br /><br />
<% else %>
<%= f.association :company, collection: policy_scope(Company).all, prompt: "Choose a Company", :required => true %>
<% end %>
<%= f.input :username, :required => true %>
<%= f.input :display_name, :required => true %>
<div class="form-actions">
<%= f.button :submit, :class => 'btn-primary' %>
</div>
<% end %>
f.association is failing saying :company not found. If policy_scope(Company).count <= 1 then code in that condition works fine.
Please say if you need more code. Thank you
I have a model Contact which has a many-to-many relationship with Company:
class Contact < ActiveRecord::Base has_many :contactcompanies
has_many :companies, through: :contactcompanies
Company model:
class Company < ActiveRecord::Base
has_many :contactcompanies
has_many :contacts, through: :contactcompanies
ContactCompany:
class ContactCompany < ActiveRecord::Base
belongs_to :company
belongs_to :contact
contacts_controller.rb:
def new
#contact = Contact.new
#all_companies = Company.all
#contact_company = #contact.contactcompanies.build
end
contact create form where I want to have a multiple select for companies:
<%= form_for #contact do |f| %>
<%= f.label :first_name %>
<%= f.text_field :first_name %>
<%= f.label :last_name %>
<%= f.text_field :last_name %>
<%= f.label :image_url %>
<%= f.text_field :image_url %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= fields_for(#contact_company) do |cc| %>
<%= cc.label "All Companies" %>
<%= collection_select(:companies, :id, #all_companies, :id, :name, {}, { :multiple => true }) %>
<% end %>
<div class="form-action">
<%= f.submit nil, :class => 'btn btn-primary' %>
<%= link_to t('.cancel', :default => t("helpers.links.cancel")),
contacts_path, :class => 'btn' %>
</div>
<% end %>
My issue is when I go to /contacts/new I get this error:
Circular dependency detected while autoloading constant Contactcompany
Any ideas? I'm searching for hours without success. Thanks
You have declared your class as "ContactCompany"
This implies:
has_many :contact_companies
has_many :contacts, through: :contact_companies
Without the underscore, it is looking for a class named Contactcompany, which does not exist.
according to http://guides.rubyonrails.org/getting_started.html, I have below relationship models,
class Tag < ActiveRecord::Base
belongs_to :post
attr_accessible :name
end
class Post < ActiveRecord::Base
attr_accessible :context, :title, :tags_attributes
validates :title, :presence => true
validates :context, :presence => true, :length => {:minimum => 5}
has_many :comments
has_many :tags
accepts_nested_attributes_for :tags, :allow_destroy => :true,
:reject_if => proc {|attrs| attrs.all? {|k,v| v.blank?} }
end
normally below code could work well when I sent a edit request,these existing tags are listed as editable elements on the page.
<%= form_for(#post) do |post_form| %>
<%= post_form.fields_for :tags do |tag_form|%>
<div class="field">
<%= tag_form.label :name, 'Tag:' %>
<%= tag_form.text_field :name %>
</div>
<% unless tag_form.object.nil? || tag_form.object.new_record? %>
<div class="field">
<%= tag_form.label :_destroy, 'Remove:' %>
<%= tag_form.check_box :_destroy %>
</div>
<% end %>
<% end %>
but now,refer to below instance code on http://guides.rubyonrails.org/form_helpers.html
<%= form_for #person, :url => { :action => "create" } do |person_form| %>
<%= person_form.text_field :name %>
<%= fields_for #person.contact_detail do |contact_details_form| %>
<%= contact_details_form.text_field :phone_number %>
<% end %>
<% end %>
then I change the statement with fields_for to below format, why it always always prompt
undefined method `model_name' for Array:Class
<%= fields_for #post.tags do |tag_form|%>
at last, I make it work with below update
<% #post.tags.each do |tag| %>
<%= post_form.fields_for tags,tag do |tag_form|%>
<div class="field">
<%= tag_form.label :name, 'Tag:' %>
<%= tag_form.text_field :name %>
</div>
<% unless tag_form.object.nil? || tag_form.object.new_record? %>
<div class="field">
<%= tag_form.label :_destroy, 'Remove:' %>
<%= tag_form.check_box :_destroy %>
</div>
<% end %>
<% end %>
<% end %>
I have this _form file.
<%= form_for(#company) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :company_type %>
<%= f.select :company_type_id, CompanyType.order(:name).map{|x| [x.name, x.id]} %>
<%= f.label :description %>
<%= f.text_area :description %>
<%= f.submit %>
<% end %>
My question is how do I change select
<%= f.select :company_type_id, CompanyType.order(:name).map{|x| [x.name, x.id]} %>
in this form into checkbox list?
CompanyType model:
class CompanyType < ActiveRecord::Base
attr_accessible :name
has_many :companies
end
Company model:
class Company < ActiveRecord::Base
attr_accessible :description, :name, :company_type_id, :website
belongs_to :type, :class_name => "CompanyType", :foreign_key => :company_type_id
end
Try this
<%= f.check_box :company_type_id, CompanyType.order(:name).map{|x| [x.name, x.id]} %>
Try the checkboxes_select method
http://edgeapi.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-collection_check_boxes