Unable to save Join Table details to database in Rails 4 - ruby-on-rails-4

I've always had trouble with has_many :through relationships on join tables and am stuck again on where I'm going wrong. I think it might be in my controller as I'm still getting to grips with how it all works.
I have three models:
class Role < ActiveRecord::Base
has_many :assignments, inverse_of: :role
has_many :employees, :through => :assignments
accepts_nested_attributes_for :assignments
end
class Employee < ActiveRecord::Base
has_many :assignments, inverse_of: :employee
has_many :roles, :through => :assignments
accepts_nested_attributes_for :assignments
end
class Assignment < ActiveRecord::Base
belongs_to :employee, inverse_of: :assignment
belongs_to :role, inverse_of: :assignment
accepts_nested_attributes_for :employee
accepts_nested_attributes_for :role
end
I'd like to be able to set the pre-created role for an employee when creating or editing an employee. My New Employee form is as follows:
<%= semantic_form_for #employee do |f| %>
<%= render 'shared/error_messages' %>
<%= f.inputs do %>
<%= f.input :first_name %>
<%= f.input :last_name %>
<%= f.input :email %>
<%= f.input :password %>
<%= f.input :password_confirmation %>
<%= semantic_fields_for :roles do |role| %>
<%= role.input :role, :as => :select, :collection => Role.all %>
<%= role.semantic_fields_for :assignments do |assignment| %>
<%= assignment.input :start_date, :as => :date_select %>
<%= assignment.input :end_date, :as => :date_select %>
<%= assignment.input :assignment_no %>
<%= assignment.input :assignment_id %>
<% end %>
<% end %>
<% end %>
<%= f.actions do %>
<%= f.action :submit, :as => :button %>
<%= f.action :cancel, :as => :link %>
<% end %>
<% end %>
and finally my Employee Controller is:
class EmployeesController < ApplicationController
before_action :set_employee, only: [:show, :edit, :update, :destroy]
def index
#employees = Employee.paginate(page: params[:page])
end
def show
end
def new
#employee = Employee.new
role = #employee.roles.build
end
def edit
#employee = Employee.find(params[:id])
end
def create
#employee = Employee.new(employee_params)
if #employee.save
#employee.send_activation_email
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
end
def update
respond_to do |format|
if #employee.update(employee_params)
flash[:success] = "Profile updated"
format.html { redirect_to #employee, notice: 'Employee was successfully updated.' }
format.json { render :show, status: :ok, location: #employee }
else
format.html { render :edit }
format.json { render json: #employee.errors, status: :unprocessable_entity }
end
end
end
def destroy
Employee.find(params[:id]).destroy
flash[:success] = "Employee deleted"
respond_to do |format|
format.html { redirect_to employees_url, notice: 'Employee was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_employee
#employee = Employee.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def employee_params
params.require(:employee).permit(:avatar, :email, :first_name, :last_name, :password, :assignments_attributes => [:employee_id, :role_id, :roles_attributes => [:id, :employee_id, :PID]])
end
end
I appreciate that this subject is always on Stackoverflow, but I can't seem to translate any of the problems into where I am. Any help would be greatly appreciated!
The console output is as follows:
Started POST "/employees" for 127.0.0.1 at 2015-05-16 18:05:34 +0200
source=rack-timeout id=d5933405c94d9c2e8bca3332564528ec timeout=60000ms service=25ms state=active
Processing by EmployeesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"TgHHELkt2zXUtS474WwGeHcmfKKw/broHxRdhlsF1P4JyGoa+03rchb6mfxbOSXKdrPgcJMeBCyIlCMHqPlQBA==", "employee"=>{"first_name"=>"John", "last_name"=>"Smith", "email"=>"john#smith.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "roles"=>{"role"=>"2", "assignments"=>{"start_date(1i)"=>"2012", "start_date(2i)"=>"7", "start_date(3i)"=>"5", "end_date(1i)"=>"2016", "end_date(2i)"=>"3", "end_date(3i)"=>"1", "assignment_no"=>"4", "assignment_id"=>"A3392822"}}, "button"=>""}
Unpermitted parameter: password_confirmation
(0.2ms) BEGIN
source=rack-timeout id=d5933405c94d9c2e8bca3332564528ec timeout=60000ms service=1113ms state=active
SQL (70.3ms) INSERT INTO "employees" ("email", "first_name", "last_name", "password_digest", "created_at", "updated_at", "activation_digest") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["email", "john#smith.com"], ["first_name", "John"], ["last_name", "Smith"], ["password_digest", "$2a$10$g3PNppheVZ8AFnnKuWg6secBdGev0NlCXjUx.RXsky03Xl9L3CubO"], ["created_at", "2015-05-16 16:05:35.422312"], ["updated_at", "2015-05-16 16:05:35.422312"], ["activation_digest", "$2a$10$L0yFuvmlJon7fWod6lHj..O7yDRaqwqTlTkJgD7Evqx.dA4pDTlBC"]]
(133.0ms) COMMIT
(I'm working on a very slow computer!)

Related

Data not saved in nested forms

I am trying to implement nested forms .
For reference https://www.driftingruby.com/episodes/nested-forms-from-scratch
However in this video they have used simple_form_for
but i have used fields_for
Now my problem is that data in main form is saved but data in nested form is not saved.
I have checked properly ,data is passed from view to controller of nested form
My CONTROLLER
class TShyainTouyouJoshinshosController < ApplicationController
def new
#t_shyain_touyou_joshinsho = TShyainTouyouJoshinsho.new
end
def create
#t_shyain_touyou_joshinsho = TShyainTouyouJoshinsho.new(t_shyain_touyou_joshinsho_params)
#t_shyain_touyou_joshinsho.assign_attributes(:created_by => current_m_user_login.user_id)
respond_to do |format|
if #t_shyain_touyou_joshinsho.save
format.html { redirect_to dashboard_path, notice: '登録は完了しました。' }
format.json { render :show, status: :created, location: #t_shyain_touyou_joshinsho }
else
format.html { render :new }
format.json { render json: #t_shyain_touyou_joshinsho.errors, status: :unprocessable_entity }
end
end
end
private
def t_shyain_touyou_joshinsho_params
params.require(:t_shyain_touyou_joshinsho).permit(:wfs_id,:joshinsho_bi,shyain_rirekis_attributes: [:id,:emp_type,:company_name,:_destroy])
end
end
MY MODEL
class TShyainTouyouJoshinsho < ActiveRecord::Base
self.primary_key='wfs_id'
has_many :shyain_rirekis, foreign_key: 't_shyain_touyou_joshinsho_id', dependent: :destroy
accepts_nested_attributes_for :shyain_rirekis, allow_destroy: true , reject_if: :all_blank
end
class ShyainRireki < ActiveRecord::Base
belongs_to :t_shyain_touyou_joshinsho, :foreign_key => 't_shyain_touyou_joshinsho_id'
end
VIEW MAIN FORM
<%= link_to_add_nested_form_shyain('✙ 社員履歴', f, :shyain_rirekis, class: 'btn btn-success') %>
<div class="append_nested_form_fields">
</div>
VIEW PARTIAL
<%= f.fields_for :shyain_rirekis do |ff| %>
<tr>
<td><%= ff.select :emp_type,TShyainTouyouJoshinsho::EMP_TYPE,{}, class: 'form-control' , :disabled => #disabled_field %></td>
<td><%= ff.text_field :company_name, class: 'form-control' , :disabled => #disabled_field %></td>
<td><%= ff.hidden_field :_destroy %>
<%= link_to '削除' ,'#' , class: " btn btn-xs btn-danger remove_record" %>
</td>
</tr>
<% end %>
MY HELPER
def link_to_add_nested_form_shyain(name, f, association, **args)
new_object = f.object.send(association).klass.new
id = new_object.object_id
fields = f.fields_for(association, new_object, child_index: id) do |builder|
render(association.to_s.singularize, f: builder, commute_object: new_object)
end
link_to(name, '#', class: "add_nested_shyain_rireki " + args[:class], data: {id: id, fields: fields.gsub("\n", "")})
end
MY JAVASCRIPT
$(document).ready(function(){
$('form').on('click', '.remove_record', function(event) {
$(this).prev('input[type=hidden]').val('1');
$(this).closest('tr').hide();
return event.preventDefault();
});
$('form').on('click', '.add_nested_shyain_rireki', function(event) {
var regexp, time;
time = new Date().getTime();
regexp = new RegExp($(this).data('id'), 'g');
$('.append_nested_form_fields').append($(this).data('fields').replace(regexp, time));
return event.preventDefault();
});
});
MIGRATION FILE
def change
create_table :t_shyain_touyou_joshinshos do |t|
t.integer :wfs_id
t.date :joshinsho_bi
end
end
def change
create_table :shyain_rirekis do |t|
t.references :t_shyain_touyou_joshinsho, index: true, foreign_key: true
t.string :emp_type
t.string :company_name
end
end
I am using wfs_id as primary key of my main form
Thankyou in advance
It seems that the association given was wrong
It works fine now
I changed my migrations as follows
def change
create_table :shyain_rirekis do |t|
t.integer :wfs_id
t.string :emp_type
t.string :company_name
end
end
MODEL CHANGES
class TShyainTouyouJoshinsho < ActiveRecord::Base
self.primary_key='wfs_id'
has_many :shyain_rirekis, foreign_key: 'wfs_id', dependent: :destroy
accepts_nested_attributes_for :shyain_rirekis, allow_destroy: true , reject_if: :all_blank
end
class ShyainRireki < ActiveRecord::Base
belongs_to :t_shyain_touyou_joshinsho, :foreign_key => 'wfs_id'
end
VIEW CHANGES
<%= link_to_add_nested_form_shyain('✙ 社員履歴', f, :shyain_rirekis, class: 'btn btn-success') %>
<div class="append_nested_form_fields">
<%= f.fields_for :shyain_rirekis do |builder| %>
<%= render 'shyain_rireki', f: builder %>
<% end %>
</div>
PARTIAL CHANGES
<tr>
<td><%= f.select :emp_type,TShyainTouyouJoshinsho::EMP_TYPE,{}, class: 'form-control' , :disabled => #disabled_field %></td>
<td><%= f.text_field :company_name, class: 'form-control' , :disabled => #disabled_field %></td>
<td><%= f.hidden_field :_destroy %>
<%= link_to '削除' ,'#' , class: " btn btn-xs btn-danger remove_record" %>
</td>
</tr>

Editing an entry with two unique columns

The problem:
When I'm editing the contents of a nested model, it saves new entries instead of editing the ones already there.
The models:
# job.rb
class Job < ActiveRecord::Base
# Relations
has_many :logs, :dependent => :destroy
accepts_nested_attributes_for :logs, allow_destroy: true, reject_if: proc { |a| a['user_id'].blank? }
end
# log.rb
class Log < ActiveRecord::Base
belongs_to :job
belongs_to :user
# a single user should not be logged more than once per job
validates :user_id, uniqueness: { scope: :job_id }
end
# user.rb
class User < ActiveRecord::Base
has_many :logs
validates_presence_of :name
def self.all_active
User.where( active: 1 )
end
end
The Job controller:
class JobsController < ApplicationController
before_action :set_job, only: [:show, :edit, :update, :destroy]
def index
#jobs = Job.all
end
def show
end
def new
#job = Job.new
10.times { #job.logs.build }
end
def edit
( #job.people - #job.logs.count ).times { #job.logs.build }
end
def create
#job = Job.new(job_params)
# Set the admin id
#job.logs.each do |log|
log.admin_id = current_admin.id
end
respond_to do |format|
if #job.save
format.html { redirect_to #job, notice: 'Job was successfully created.' }
format.json { render :show, status: :created, location: #job }
else
format.html { render :new }
format.json { render json: #job.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #job.update(job_params)
format.html { redirect_to #job, notice: 'Job was successfully updated.' }
format.json { render :show, status: :ok, location: #job }
else
format.html { render :edit }
format.json { render json: #job.errors, status: :unprocessable_entity }
end
end
end
def destroy
#job.destroy
respond_to do |format|
format.html { redirect_to jobs_url, notice: 'Job was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_job
#job = Job.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def job_params
params.require(:job).permit(:people, :logs_attributes => [:user_id])
end
end
The Job Form
<%= form_for(#job) do |f| %>
<% if #job.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#job.errors.count, "error") %> prohibited this job from being saved:</h2>
<ul>
<% #job.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="row">
<div class="small-3 columns"><%= f.label :people %></div>
<div class="small-9 columns"><%= f.integer :people %></div>
</div>
<table>
<%= f.fields_for :logs do |builder| %>
<%= render 'logs/form', { f: builder } %>
<% end %>
</table>
<div class="actions">
<%= f.submit %>
</div>
The Logs Partial:
<tr>
<td>
<%= f.label :user_id %><br>
<%= f.collection_select(:user_id, User.all_active, :id, :name, { :prompt => 'Select User' } ) %>
</td>
<td>
<%= f.label :performance %><br>
<%= f.number_field :performance %>
</td>
</tr>
Creating the job works fine. I then go to edit the entry. On the job edit screen, I see all of the logs included along with a few remaining blank log entry lines (due to the 10.times #logs.build in the Job controller).
Now, without editing anything, I click submit. I get the following error:
1 error prohibited this war from being saved:
Logs user has already been taken
Additionally, the original entries are shown, and then below them are the same exact entries duplicated, but "red" due to the error. The list of blank entries (from 10.times) is no longer showing.
However, from the editing screen, if I change ALL of the logs' users to something else, I will not get an error. It will instead create new entries with those newly-selected users instead of modifying the current entries.
I hope I've provided enough information to get this resolved! Thanks
So, it turns out that the problem was caused by not whitelisting the :id parameter of the log.
I altered my job_controller:
From:
def job_params
params.require(:job).permit(:people, :logs_attributes => [:user_id])
end
To:
def job_params
params.require(:job).permit(:people, :logs_attributes => [:id, :user_id])
end
That was what was causing the problem with validation! This may be too complex of q/a to help anyone, but hey, it just might!

collection check boxes in controllers

This is part of my app in rails:
How to get values in the controller of posts from the check boxes?
I want to get the array of ids got from the check boxes.
The post is created, but I can not access the array of ids got from the check boxes.
class Categoryofpost < ActiveRecord::Base
belongs_to :post
belongs_to :category
end
............................
In views/posts/new.html.erb
<%= form_for #post do |p| %>
<%= p.label :title %>
<%= p.text_field :title %>
<br>
<%= p.label :body %>
<%= p.text_area :body %>
<br>
<p>Related to : </p>
<br>
<%= p.collection_check_boxes :category_ids, Category.all, :id, :name %>
<br>
<%= p.submit "Create" %>
<% end %>
class Category < ActiveRecord::Base
belongs_to :user
validates :name, uniqueness: true
has_many :categoryofposts
has_many :related_posts, class_name: "Post", through: :categoryofposts , source: :post
end
....................
In the posts controler
def create
#post_params = post_params
##post_params[:user_id] = 1 #session[:user_id]
#post = Post.new(#post_params)
if #post.save
# #cat = get_categories
# #cat.each do |c|
# Categoryofpost.create(post_id: #post.id, category_id: c)
# end
redirect_to #post, notice: "The post has been posted successfully."
else
render action: :new, alert: "The post has not been posted."
end
end
protected
def post_params
params.require(:post).permit(:title, :body, categoy_ids: [])
end
def get_categories
params.require(:post).permit(categoy_ids: [])
end
class Post < ActiveRecord::Base
belongs_to :user, dependent: :destroy
has_many :categoryofposts
has_many :categories, through: :categoryofposts
end
You have a typo in your post_params method.It should be
def post_params
params.require(:post).permit(:title, :body, category_ids: [])
end
Notice category_ids.

Rails nested_form Invalid association. Make sure that accepts_nested_attributes_for is used for assoctiation

I am using rails 4.0.4 with nested_form 0.3.2 to build an app that allows users to organize movies in lists.
I have these main models, a List model (I've excluded things such as validations):
class List < ActiveRecord::Base
belongs_to :user
has_many :list_movie_pairs
has_many :movies, :through => :list_movie_pairs
accepts_nested_attributes_for :list_movie_pairs, :allow_destroy => true
accepts_nested_attributes_for :movies, :allow_destroy => true
end
A Movie model:
class Movie < ActiveRecord::Base
has_many :list_movie_pairs
has_many :lists, :through => :list_movie_pairs
has_many :reviews
end
A ListMoviePair model for the many-to-many relationship:
class ListMoviePair < ActiveRecord::Base
belongs_to :list
belongs_to :movie
validates_presence_of :list_id, :movie_id
validates_uniqueness_of :movie_id, scope: :list_id
end
I am trying to build an interface for the user to add movies to a created list. These routes serve my purpose:
get "/users/:username/lists/:id/add" => "lists#add_movies", :as => :user_list_list_movie_pairs
post "/users/:username/lists/:id/add" => "lists#submit_movies"
These are the classes in my ListsController that should make this possible:
def add_movies
#pair = list.list_movie_pairs.new # "list" is a helper that returns the current list
end
def submit_movies
#list = current_user.lists.find(params[:id])
#pair = #list.list_movie_pairs.new(pair_params)
if #pair.save
redirect_to user_list_path(current_user.username, #list)
else
render :add_movies
end
end
def list_params
params.require(:list).permit(:name, :description, :private, \
list_movie_pairs_attributes: [:id, :list_id, :movie_id, :_destroy], \
movies_attributes: [:id, :title, :_destroy])
end
And this is the form in my view
<%= nested_form_for [current_user, list, #pair] do |f| %>
<%= f.fields_for :movies do |movie_form| %>
<%= movie_form.text_field :title %>
<%= movie_form.link_to_remove "Remove movie" %>
<% end %>
<%= f.link_to_add "Add movie", :movies %>
<% end %>
I get this error when trying to access the view:
Invalid association. Make sure that accepts_nested_attributes_for is used for :movies association.
Which pops at this line:
<%= f.link_to_add "Add movie", :movies %>
Note 1: I am using the Devise gem for users, hence the "current_user" helper;
Note 2: I have tried using both "movies" and "list_movie_pairs", i.e.:
f.fields for :list_movie_pairs
and
f.link_to_add "Add movie", :list_movie_pairs
in my view, neither association seems to work
Your code in the view should be like this
<%= nested_form_for [current_user, list, #pair] do |f| %>
<%= f.fields_for :movies do |movie_form| %>
<%= movie_form.text_field :title %>
<%= movie_form.link_to_remove "Remove movie" %>
<%= movie_form.link_to_add "Add movie", :movies %> #note the change here
<% end %>
<% end %>
Update
There are several issues in your code
1.In your List model,this line is not required
accepts_nested_attributes_for :movies, :allow_destroy => true #not requied
2.In your ListsController,you have this line
#pair = #list.list_movie_pairs.new(pair_params)
It should be
#pair = #list.list_movie_pairs.new(list_params) because you have list_params method not pair_params

Rails 4 nested form not creating the objects which it accepts_nested_attributes_for

I'm trying to get categories and subcategories working. So far, I can create categories and add subcategories to them. When I submit the form, the subcategories_attributes send, but no Subcategory records are created. Please help, I'm pulling my hair out over here, and none of the tutorials seem to be helping.
category.rb
class Category < ActiveRecord::Base
has_many :subcategories, :dependent => :destroy
accepts_nested_attributes_for :subcategories, :reject_if => lambda {|a|
a[:content].blank?}
end
subcategory.rb
class Subcategory < ActiveRecord::Base
belongs_to :category
end
categories_controller.rb
class CategoriesController < ApplicationController
before_action :set_category, only: [:show, :edit, :update, :destroy]
def index
#categories = Category.all
end
def show
end
def new
#category = Category.new
#category.subcategories.build
end
def edit
end
def create
#category = Category.new(category_params)
respond_to do |format|
if #category.save
format.html { redirect_to #category, notice: 'Category was successfully
created.' }
format.json { render action: 'show', status: :created, location: #category }
else
format.html { render action: 'new' }
format.json { render json: #category.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #category.update(category_params)
format.html { redirect_to #category, notice: 'Category was successfully
updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #category.errors, status: :unprocessable_entity }
end
end
end
def destroy
#category.destroy
respond_to do |format|
format.html { redirect_to categories_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_category
#category = Category.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def category_params
params.require(:category).permit(:name, subcategories_attributes: [:id, :name,
:_destroy])
end
end
_form.html.erb
<%= nested_form_for(#category) do |f| %>
<% if #category.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#category.errors.count, "error") %> prohibited this category
from being saved:</h2>
<ul>
<% #category.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<%f.fields_for :subcategories do |builder|%>
<p>
<b>Subcategory</b>
<%=builder.text_field :name%>
<%=builder.link_to_remove "Remove"%>
<p>
<%end%>
<p>
<%=f.link_to_add "Add a Subcategory", :subcategories%>
</p>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Params Hash (on submit):
Parameters: {"utf8"=>"✓",
"authenticity_token"=>"19FxRnWF1F3BD4QZIkkce4arkOPt/BMkWFw6Z+vpV+8=", "category"=>
{"name"=>"Test", "subcategories_attributes"=>{"1398706662184"=>{"name"=>"TS1",
"_destroy"=>"false"}, "1398706664804"=>{"name"=>"TS2", "_destroy"=>"false"}}},
"commit"=>"Create Category"}
Thanks in advance!
subcategories_attributes are sent correctly within your params hash BUT content attribute is missing in them.
"subcategories_attributes"=>{"1398706662184"=>{"name"=>"TS1",
"_destroy"=>"false"}, "1398706664804"=>{"name"=>"TS2", "_destroy"=>"false"}}
Notice there is no content key passed.
So, all the subcategories records passed in subcategories_attributes are REJECTED due to the condition you specified in Category model:
accepts_nested_attributes_for :subcategories, :reject_if => lambda {|a|
a[:content].blank?}
Notice :reject_if => lambda {|a| a[:content].blank?} part above, this will reject all the records for which content is missing.
UPDATE
when I go to my edit action, the fields don't show up, despite being added to the database as intended (and showing up as such in other
forms).
Set a variable in edit and new actions as below:
def new
#category = Category.new
#subcategories = #category.subcategories.build ## Added
end
def edit
#subcategories = #category.subcategories ## Added
end
Update the fields_for in edit form as below:
<%= f.fields_for :subcategories, #subcategories do |builder|%>