Rails 5 - Nested Model Form Not Saving Child Model Attributes - ruby-on-rails

I've been at this problem for a couple weeks now on and off.
Currently I have a User model, Employee model and an Employee_Contact model. The User model was generated using Devise. After the User is logged in they can go to their User Dashboard and create an Employee using a nested form. I am having an issue where the child model Employee_Contact is not saving into the database.
Schema:
create_table "employee_contacts", force: :cascade do |t|
t.string "street"
t.string "city"
t.string "state"
t.string "zip"
t.string "phone_num"
t.string "alt_phone_num"
t.string "email"
t.string "emergency_contact_first_name"
t.string "emergency_contact_last_name"
t.string "emergency_contact_num"
t.integer "user_id"
t.integer "employee_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["employee_id"], name: "index_employee_contacts_on_employee_id"
t.index ["user_id"], name: "index_employee_contacts_on_user_id"
end
create_table "employees", force: :cascade do |t|
t.string "first_name"
t.string "middle"
t.string "last_name"
t.string "sex"
t.string "race"
t.date "birthdate"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "employee_contact_id"
t.index ["user_id"], name: "index_employees_on_user_id"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.inet "current_sign_in_ip"
t.inet "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name:
"index_users_on_reset_password_token", unique: true
end
end
These are my models:
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :employees, :dependent => :destroy
end
class Employee < ApplicationRecord
belongs_to :user
has_one :employee_contact, inverse_of: :employee, autosave: true
accepts_nested_attributes_for :employee_contact, allow_destroy: true
end
class EmployeeContact < Employee
belongs_to :employee, inverse_of: :employee_contact
end
My Employee Controller:
class EmployeesController < ApplicationController
before_action :authenticate_user!
def index
#employees = Employee.all
end
def new
#employee = Employee.new
end
def create
#employee = current_user.employees.create(employee_params)
if #employee.save!
redirect_to root_path
else
render :new, status: :unprocessable_entity
end
end
private
def employee_params
params.require(:employee).permit(:first_name, :middle, :last_name,
:sex, :race, :birthdate, employee_contact: [:id, :street, :city,
:state, :zip, :phone_num, :email, :alt_phone_num,
:emergency_contact_first_name, :emergency_contact_last_name,
:emergency_contact_num])
end
end
My View Form:
<%= form_with(model: #employee, local: true) do |form| %>
<h3>Employee Profile Information</h3>
<%= form.label :first_name %><br />
<%= form.text_field :first_name %><br />
<%= form.label :middle %><br />
<%= form.text_field :middle %><br />
<%= form.label :last_name %><br />
<%= form.text_field :last_name %><br />
<%= form.label :sex %><br />
<%= form.text_field :sex %><br />
<%= form.label :race %><br />
<%= form.text_field :race %><br />
<%= form.label :birthdate %><br />
<%= form.date_field :birthdate %><br />
<h3>Employee Contact Information</h3><br />
<%= form.fields_for :employee_contacts do |builder| %>
<%= builder.label :street %><br />
<%= builder.text_area :street %><br />
<%= builder.label :city %><br />
<%= builder.text_area :city %><br />
<%= builder.label :street %><br />
<%= builder.text_area :street %><br />
<%= builder.label :state %><br />
<%= builder.text_area :state %><br />
<%= builder.label :zip %><br />
<%= builder.text_area :zip %><br />
<%= builder.label :phone_num %><br />
<%= builder.text_area :phone_num %><br />
<%= builder.label :alt_phone_num %><br />
<%= builder.text_area :alt_phone_num %><br />
<%= builder.label :email %><br />
<%= builder.text_area :email %><br />
<%= builder.label :emergency_contact_first_name %><br />
<%= builder.text_area :emergency_contact_first_name %><br />
<%= builder.label :emergency_contact_last_name %><br />
<%= builder.text_area :emergency_contact_last_name %><br />
<%= builder.label :emergency_contact_num %><br />
<%= builder.text_area :emergency_contact_num %><br />
<% end %>
<%= form.submit "Add", class: 'btn btn-primary' %><br />
<% end %>
When I get submit the form to create a new employee this is what I get in my console parameters:
Processing by EmployeesController#create as HTML
Parameters:{"utf8"=>"✓","authenticity_token"=>"IgYzybxM8+M8jZ8NJgYKZ+Zhqr07kcV1e0QyWf> uY8hQLyBY844jfOlLNl65F/2RRr4TTW0F1MeSmDzVzBsXOjg==", "employee"=>
{"first_name"=>"Firt Nam", "middle"=>"kk", "last_name"=>"jak", "sex"=>"dfkj", "race"=>"jkadflj", "birthdate"=>"2011-01-01", "employee_contacts"=>{"street"=>"afdjkljadfkl", "city"=>"jadklfjakldfj", "state"=>"fadjlkdf", "zip"=>"32787", "phone_num"=>"567567567", "alt_phone_num"=>"57875875875", "email"=>"dajkjdlkfj#jkj.com", "emergency_contact_first_name"=>"adfjkladjf", "emergency_contact_last_name"=>"adfjkaldf", "emergency_contact_num"=>"784339793"}}, "commit"=>"Add"}
Unpermitted parameter: :employee_contacts
And so it saves the Employee but not anything associated with their contact information. If I run Employee.last in the Rails console I get a nil value for employee_contact_id. If I try and use the Rails Console to look up Employee_Contact.last or Employee_Contact.all I get an 'unable to autoload constant Employee_Contact, expected employee_contact.rb to define it'.
UPDATE
I changed a few things thanks to Anton.
My Employee Contact model now inherits from ApplicationRecord:
class EmployeeContact < ApplicationRecord
belongs_to :employee, inverse_of: :employee_contact
end
I changed the view form in the employee_contact loop from plural to singular:
<%= form.fields_for :employee_contact do |builder| %>
I added a build method in Employee#New
def new
#employee = Employee.new
#employee.build_employee_contact
end
And edited Employee#Create
def create
#employee = current_user.employees.create(employee_params)
if #employee.valid?
redirect_to root_path
else
render :new, status: :unprocessable_entity
end
end
This is now saving an Employee Contact. Here are the results from the console:
#<EmployeeContact id: 1, street: "dafjkafdjl", city: "adjfklajdk", state: "dfjkljalkdfjioad", zip: "dafjadlkfjkajdkl", phone_num: "adf", alt_phone_num: "871274892174", email: "dafnajdklj#jkklda.com", emergency_contact_first_name: "ajdfjalkj", emergency_contact_last_name: "fjadkljdfkl", emergency_contact_num: "11287244", user_id: nil, employee_id: 1, created_at: "2017-10-03 19:01:55", updated_at: "2017-10-03 19:01:55">
Employee id: 1, first_name: "dafad", middle: "dfad", last_name: "dafd", sex: "adfd", race: "dfad", birthdate: "1210-01-01", user_id: 1, created_at: "2017-10-03 19:01:55", updated_at: "2017-10-03 19:01:55", employee_contact_id: nil>
The Employee model is still getting an Employee_Contact_Id: nil value. However, the Employee Contact model is getting the correct Employee_Id attribute.
SOLVED
So I'm going to leave it at with what I have posted in the Update. There is no need for my Employee model to have an Employee_Contact_Id attribute. When I create a new Employee the Contact correctly maps to that Employee currently.

Please set the EmployeeContact to inherit from ApplicationRecord, not Employee.
In the new action build the employee contact for the nested form from the employee.
I would suggest to remove the foreign key from the has_one association.
The employee_id on the employee_contact is enough.

Related

Rails: add relationship with other table for Devise user

I want to add an input to my Devise user sign_up form in order to add a team name. I created a specific table for Teams and I created a relationship: Users belong to Team. Team has many users.
The problem is I need to add a Team_id when submiting the sign_up form. How can I save the value of the Team input in the register form to the Team table and get the Id in order to save it to the team_id entry in Devise User table?
My Sign Up form:
<div class="container_index">
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true %>
</div>
<div class="field">
<%= f.label :password %>
<% if #minimum_password_length %>
<em>(<%= #minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "off" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %>
</div>
<div class="field">
<%= f.label :team %><br />
<%= f.text_field :team %>
</div>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
</div>
My user schema:
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.integer "team_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
t.index ["team_id"], name: "index_users_on_team_id"
end
My application controller:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
skip_before_action :verify_authenticity_token
before_filter :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up) { |u| u.permit(:name, :email, :password, :team) }
devise_parameter_sanitizer.permit(:account_update) { |u| u.permit(:name, :email, :password, :current_password, :team) }
end
Thank you.

Rails many to many model with select in the view

The logic in the exercise is create a Bill where a Bill have a list of Products with an amount of them.
I have the next 3 models:
Bill
Product
BillItem (This is the intermediate model between Bill and Product for the many to many relationship)
Schema for more clarity:
create_table "bill_items", force: :cascade do |t|
t.integer "amount"
t.integer "product_id"
t.integer "bill_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["bill_id"], name: "index_bill_items_on_bill_id"
t.index ["product_id"], name: "index_bill_items_on_product_id"
end
create_table "bills", force: :cascade do |t|
t.string "user_name"
t.string "dni"
t.date "expiration"
t.float "sub_total"
t.float "grand_total"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "products", force: :cascade do |t|
t.string "name"
t.string "description"
t.float "price"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
First i have a list of created products in my database.
For my BillController i have a _form.html.erb where i want be capable to create dynamic selects for choose a product and set a amount.
My view is the next:
<%= form_for bill do |f| %>
<div>
<%= f.label :user_name %>
<%= f.text_field :user_name %>
</div>
<div>
<%= f.label :dni %>
<%= f.text_field :dni %>
</div>
<div>
<%= f.label :product %>
<%= f.collection_select :product_ids, Product.all, :id, :name %>
</div>
# Here i should add more selects if the user want to add more products.
<div><%= f.submit %></div>
<% end %>
My problem or my question is: how to "groupe" the ids of the products and the amount of them. And the another question is, i can create the other selects dynamically with JS?
You need to be adding multiple BillItem to your form to account for the amount and the product. This is done through accepts_nested_attributes_for on the model. For example:
class Bill < ActiveRecord::Base
has_many :bill_items
accepts_nested_attributes_for :bill_items
end
Initialize a BillItem on the Bill in your controller:
class BillsController < ActionController::Base
def new
#bill = Bill.new
#bill.bill_items.build
end
end
Then in your form, use the fields_for helper to create sub-forms:
<%= form_for #bill do |f| %>
<div>
<%= f.label :user_name %>
<%= f.text_field :user_name %>
</div>
<div>
<%= f.label :dni %>
<%= f.text_field :dni %>
</div>
<%= f.fields_for :bill_items do |f| %>
<div>
<%= f.label :product %>
<%= f.collection_select :product_id, Product.all, :id, :name %>
</div>
<div>
<%= f.label :amount %>
<%= f.number_field :amount %>
</div>
<% end %>
<%= f.submit %></div>
<% end %>
And yes, you can use javascript to create new nested forms.

Values of helper collection_select is being called twice

I have model Category:
class Category < ActiveRecord::Base
has_many :projects
end
Model project:
class Project < ActiveRecord::Base
belongs_to :category
validates :title, :description, :category, :skill, :payment, presence: true
validates :price, numericality: { only_integer: true }
PRICE_CATEGORIES = ['cat_price1', 'cat_price2', 'cat_price3', 'cat_price4']
end
projects/new
<%= form_for #project, url: {action: 'create'} do |f| %>
<p>
<%= f.label 'Titel' %>
<%= f.text_field :title %>
</p>
<P>
<%= f.label 'Budget' %>
<%= f.text_field :price %>
für
<%= f.select :price_category, Project::PRICE_CATEGORIES %>
</P>
<p>
<%= f.label 'Beschreibung' %>
<%= f.text_area :description %>
</p>
<p>
<%= f.label 'Kategorie' %>
<%= f.collection_select :category_id, Category.all, :id, :name %>
</p>
<P>
<%= f.submit 'erstellen' %>
</P>
<% end %>
schema.rb:
create_table "categories", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "projects", force: :cascade do |t|
t.string "title"
t.text "description"
t.integer "price"
t.string "skill"
t.string "location"
t.string "payment"
t.boolean "anonymity"
t.string "file"
t.integer "category_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "price_category"
end
When calling <%= f.collection_select :category_id, Category.all, :id, :name %>
all items in dropdown list displaying twice. Cant undestand, why.
For example:
cat1
cat2
cat3
cat1
cat2
cat3
Thank for you help.

Pass a foreign table in a "form_for"

I have two model
user.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :personal
before_create :generate_profile
end
and personal.rb
class Personal < ActiveRecord::Base
belongs_to :user
validates :user_id, presence: true
end
and in my controller i have :
def createinfo
#user = current_user
#personal = #user.personal
end
I want to do a form for like that :
<%= form_for #personal do |f| %>
Trigramme : <%= f.text_field :trigramme, :input_html => { :value => 'blabla' } %><br />
Nom : <%= f.text_field :nom %><br />
Prenom : <%= f.text_field :prenom %><br />
Poste : <%= f.text_field :poste %><br />
Bio : <%= f.text_area :bio %><br />
<%= f.submit %>
<% end %>
But i have an error and I don't know how to resove that. I think the path current_user.personal is not accepted by form_for although it print the correct thing when I only put current_user.personal.trigramme in the code.
undefined method `personal_path' for #<#<Class:0x8e15a28>:0x8e15308>
do you know how can I do this form?
EDIT : My route.rb
Rails.application.routes.draw do
devise_for :users
root 'static_pages#cvpage'
get 'home' => 'static_pages#home'
get 'help' => 'static_pages#help'
get 'about' => 'static_pages#about'
get 'contact' => 'static_pages#contact'
get 'cvpage' => 'static_pages#cvpage'
get 'createinfo' => 'static_pages#createinfo'
end
EDIT 2 schema.rb :
create_table "personals", force: :cascade do |t|
t.string "trigramme"
t.string "nom"
t.string "prenom"
t.string "poste"
t.text "bio"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "personals", ["user_id"], name: "index_personals_on_user_id"
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
YEAHH! I solve my problem (take a lot of time)
I put in my personal controller this :
def update
#user = current_user
#personal = #user.personal
if #personal.update_attributes(personal_params)
# if update is successful
else
# if update is unsuccessful
end
redirect_to home_path
end
def personal_params
params.require(:personal).permit(:trigramme, :nom, :prenom, :poste, :bio)
end
And i put in my view this form :
<%= form_for(#personal, html: { method: :put }) do |f| %>
Trigramme : <%= f.text_field :trigramme %><br />
Nom : <%= f.text_field :nom %><br />
Prenom : <%= f.text_field :prenom %><br />
Poste : <%= f.text_field :poste %><br />
Bio : <%= f.text_area :bio %><br />
<%= f.submit %>
Thank you to all of you !!

Rails 4 Create record with record.id of join table

Ruby on Rails 4
I am trying to have a form create a record in my Test table. The record needs to have the id(s) from its join table called questions_tests. Do I need to create the records in questions_tests from the form and then create the tests record? How would you do that?
The Models (not sure if I named the join table correct):
class Test < ActiveRecord::Base
has_many :questions_tests
has_many :questions, :through => :questions_tests
end
class Question < ActiveRecord::Base
has_many :questions_tests
has_many :tests, :through => :questions_tests
end
class QuestionTest < ActiveRecord::Base
belongs_to :question
belongs_to :test
end
My schema:
create_table "questions", force: true do |t|
t.string "content"
t.string "question_type"
t.string "category"
t.integer "product_id"
t.boolean "active"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_id"
end
add_index "questions", ["product_id", "created_at"], name: "index_questions_on_product_id_and_created_at", using: :btree
create_table "questions_tests", id: false, force: true do |t|
t.integer "test_id", null: false
t.integer "question_id", null: false
end
create_table "tests", force: true do |t|
t.string "name"
t.integer "user_id"
t.string "type"
t.string "category"
t.string "description"
end
The form should fill in the Test attributes and somehow have the id(s) from questions_tests. Right now I do not know how to send or create the questions_tests records.
My form, not sure how to have option to select questions and store them to the tests record. Here, :question_id is undefined but I need to store 2 to 200 of them in the test.
<h1>New Test</h1>
<%= form_for #test do |f| %>
<%= f.label :name, "Test Name" %><br>
<%= f.text_field :name, class: "input-lg" %>
<%= f.label :description, "Description" %><br>
<%= f.text_field :description, class: "input-lg" %>
<%= f.label :question_id %><br>
<%= f.select :question_id, Question.all.collect { |p| [ p.content, p.id ] }, class: "input-lg" %>
<%= f.label :category %><br>
<%= f.select :category, [ ["IP Voice Telephony", "ip_voice"], ["IP Video Surveillance", "ip_video_surveillance"], ["IP Video Telephony", "ip_video_telephony"], ["Enterprise Gateways", "enterprise_gateways"], ["Consumer ATAs", "consumer_atas"], ["IP PBX", "ip_pbx"] ], {prompt: "Select Category"}, class: "input-lg" %>
<%= f.label :type %><br>
<%= f.select :type, [ ["Beginner", "beginner"], ["Intermediate", "intermediate"], ["Advanced", "advanced"] ], {prompt: "Select Type"}, class: "input-lg" %>
<br/><br/><br/>
<%= f.submit "Create Test", class: "btn btn-lg btn-primary" %>
<% end %>
You need to look into fields_for.
You'll have a fields_for block in your form
f.fields_for :questions do |question|
question.select(...)
end`
Within this block, you'll have a way to select the questions to add to the test.
There's a gem called cocoon that will help you add a "Add question" link to add more questions.
You'll have to add accepts_nested_attributes_for :questions in your test model. Then Rails will create the QuestionTest itself, you don't need to worry about that.

Resources