I am creating employee pay roll application. In that i am having two models one is employee and another is salary. I have added foreign key to get the employee_id in salaries table and to get the employee name in _form.html.erb(salaries) using the gem 'rails3-jquery-autocomplete'. I have followed all the steps in that gem. But foreign key relationship is not working. I am getting null value in the field employee_id in salaries table.
'
Here i have attached my model,view,controller,routes and everything. Kindly check it.
My Models
** Salary.rb**
class Salary < ActiveRecord::Base
belongs_to :employee, :foreign_key => "employee_id"
attr_accessible :basic, :da, :effective_from, :effective_to, :employeesalary, :hra, :ca, :sa, :employee_id
end
Employee.rb
class Employee < ActiveRecord::Base
has_many :salaries, :foreign_key => "employee_id"
attr_accessible :address, :age, :branch, :city, :date_of_birth, :designation, :employee_end_date, :employee_start_date, :gender, :name
end
My Controllers
salaries_controller.rb
class SalariesController < ApplicationController
autocomplete :employee, :name, :display_value => :name, :full => true
def new
#salary = Salary.new
end
def create
#salary = Salary.new(params[:salary])
if #salary.save
flash[:success] = "Created a new month salary!"
redirect_to salaries_path
else
flash[:error] = "Couldn't Create salary"
render 'new'
end
end
def index
#salary = Salary.order('created_at DESC').paginate(:page => params[:page], :per_page => 10)
end
def show
#salary = Salary.find(params[:id])
#employee =Employee.all
end
end
employees_controller.rb
class EmployeesController < ApplicationController
def new
#employee = Employee.new
#salary = #employee.salaries.build
end
def create
#employee = Employee.new(params[:employee])
if #employee.save
flash[:success] = "Created Employee Successfully!"
redirect_to employee_path(#employee)
else
flash[:error] = "Couldn't Create a Employee"
render 'new'
end
end
def index
#employee = Employee.order('created_at DESC').paginate(:page => params[:page], :per_page => 10)
end
def show
#employee = Employee.find(params[:id])
end
end
routes.rb
resources :salaries do
get :autocomplete_employee_name, :on => :collection
end
resources :employees
_form.html.erb(salaries)
<%= form_for #salary do |f| %>
<%= f.hidden_field :employee_id, :id => "real_employee_id" %>
<span class="help-block">Enter the Employee name</span>
<%= autocomplete_field_tag 'throwaway_employee', '',
autocomplete_employee_name_salaries_path,
:size => 75, :id_element => '#real_employee_id' %>
application.js
//= require jquery
//= require jquery_ujs
//= require autocomplete-rails
//= require_tree .
I have checked throughly but can't find the error. Kindly help me to get out of this issue.
Thanks in advance...
I had a play with this code tonight and I think I know what might be happening.
When you start typing in an employee name a green list of names pops up. If you just hit enter straight away the form takes whatever you have typed so far and uses it to look up the name. This fails because the name is incomplete. As you have no validation on the employee id the form works and a null employee id is saved.
What you need to do is to use the down arrow key or mouse to select one of the names. When you do that the name will change to light green and the text in the text box will be replaced with the full name.
Then when you submit the form the match is found and everything works!
So your code works, you are just using the GUI incorrectly.
You should also add this line to your salary.rb file:
validates :employee_id, presence: true
That's my theory. Please try it out and let us know how you go.
Related
I want to have a simple select field that allows users to choose some users to be on a job's team.
#jobs.rb
class Job < ApplicationRecord
has_many :team_members
end
#jobs_controller.rb
class JobsController < ApplicationController
def update
#job = Job.find(params[:id])
if #job.update_attributes(job_params)
flash[:notice] = "Job Saved"
redirect_to job_path(#job)
else
flash[:alert] = "Job Not Saved"
render :edit
end
end
private
def job_params
params.require(:job).permit(
team_member_ids: []
)
end
end
# jobs/edit.html.erb
<%= f.select :team_member_ids, User.all, {:include_blank => "None"},{ :multiple => true} %>
This gives me an error
Couldn't find TeamMembers with ids 117, 23, 30.
Am I missing something simple here? Or is the wrong approach to managing the data in the form?
Update, params hash:
{"utf8"=>"✓",
"_method"=>"patch",
"authenticity_token"=>"xxxx",
"job"=>
{
"jobtype"=>"Cutting",
"status"=>"Not_Started",
"team_member_ids"=>["", "117", "23", "30"]},
"commit"=>"Update Job",
"id"=>"84"}
Oops, This was a bit silly.
The id's are user id's, but I was passing them as team_member ids. Where team_member is the join table.
Fix was:
#add to job.rb
has_many :users, through: :team_members
# update jobs_controller.rb
private
def job_params
params.require(:job).permit(
user_ids: []
)
end
# update form field:
<%= f.select :user_ids, User.all,{:include_blank => "None"},{ :multiple => true} %>
I'm trying to set up a page where there are 4 dropdown boxes, each of which have a full list of Products. A user can select any combination of 4 products, and 'create' a new print page, which has the product information list
I only have one box right now, but when I try to create a new row for Print from this page, it doesn't return anything to :p1
new.html.erb:
<%= f.collection_select :p1, Product.all, :id, :name, :prompt => 'Select One' %>
<%= f.submit "Create" %>
class PrintsController < ApplicationController
def new
#print = Print.new
end
def create
#print = Print.new(print_params)
if #print.save
redirect_to #print, alert: "Created successfully."
else
redirect_to new_print_path, alert: "Error creating print page."
end
end
def show
#print = Print.find(params[:id])
end
private
def print_params
params.require(:p1).permit(:p2, :p3, :p4)
end
end
Model
class Print < ActiveRecord::Base
belongs_to :product
end
Migrate
class CreatePrints < ActiveRecord::Migration
def change
create_table :prints do |t|
t.integer :p1
t.integer :p2
t.integer :p3
t.integer :p4
t.timestamps
end
end
end
Routes:
Rails.application.routes.draw do
resources :categories, :products, :prints
I'm a total rails newbie, so I know I'm probably making a stupid mistake somewhere, but I've been fiddling with code for hours and still haven't figured out what I did wrong.
Your print_params method is wrong :
def print_params
params.require(:print).permit(:p1, :p2, :p3, :p4)
end
This is the right format.
I have two models Hotel and Address.
Relationships are:
class Hotel
belongs_to :user
has_one :address
accepts_nested_attributes_for :address
and
class Address
belongs_to :hotel
And I need to save in hotels table and in addresses table from one form.
The input form is simple:
<%= form_for(#hotel) do |f| %>
<%= f.text_field :title %>
......other hotel fields......
<%= f.fields_for :address do |o| %>
<%= o.text_field :country %>
......other address fields......
<% end %>
<% end %>
Hotels controller:
class HotelsController < ApplicationController
def new
#hotel = Hotel.new
end
def create
#hotel = current_user.hotels.build(hotel_params)
address = #hotel.address.build
if #hotel.save
flash[:success] = "Hotel created!"
redirect_to #hotel
else
render 'new'
end
end
But this code doesn't work.
ADD 1
Hotel_params:
private
def hotel_params
params.require(:hotel).permit(:title, :stars, :room, :price)
end
ADD 2
The main problem is I don't know how to render form properly. This ^^^ form doesn't even include adress fields (country, city etc.). But if in the line
<%= f.fields_for :address do |o| %>
I change :address to :hotel, I get address fields in the form, but of course nothing saves in :address table in this case. I don't understand the principle of saving in 2 tables from 1 form, I'm VERY sorry, I'm new to Rails...
You are using wrong method for appending your child with the parent.And also it is has_one relation,so you should use build_model not model.build.Your new and create methods should be like this
class HotelsController < ApplicationController
def new
#hotel = Hotel.new
#hotel.build_address #here
end
def create
#hotel = current_user.hotels.build(hotel_params)
if #hotel.save
flash[:success] = "Hotel created!"
redirect_to #hotel
else
render 'new'
end
end
Update
Your hotel_params method should look like this
def hotel_params
params.require(:hotel).permit(:title, :stars, :room, :price,address_attributes: [:country,:state,:city,:street])
end
You should not build address again
class HotelsController < ApplicationController
def new
#hotel = Hotel.new
end
def create
#hotel = current_user.hotels.build(hotel_params)
# address = #hotel.address.build
# the previous line should not be used
if #hotel.save
flash[:success] = "Hotel created!"
redirect_to #hotel
else
render 'new'
end
end
Bottom line here is you need to use the f.fields_for method correctly.
--
Controller
There are several things you need to do to get the method to work. Firstly, you need to build the associated object, then you need to be able to pass the data in the right way to your model:
#app/models/hotel.rb
Class Hotel < ActiveRecord::Base
has_one :address
accepts_nested_attributes_for :address
end
#app/controllers/hotels_controller.rb
Class HotelsController < ApplicationController
def new
#hotel = Hotel.new
#hotel.build_address #-> build_singular for singular assoc. plural.build for plural
end
def create
#hotel = Hotel.new(hotel_params)
#hotel.save
end
private
def hotel_params
params.require(:hotel).permit(:title, :stars, :room, :price, address_attributes: [:each, :address, :attribute])
end
end
This should work for you.
--
Form
Some tips for your form - if you're loading the form & not seeing the f.fields_for block showing, it basically means you've not set your ActiveRecord Model correctly (in the new action)
What I've written above (which is very similar to that written by Pavan) should get it working for you
I feel like I have no idea what I'm doing.. I have a vague Idea. I'm hoping I did this all right so far.
Any way you can see to refactor this would be greatly appreciated.
One thing I noticed it does wrong is it won't load the proper options that were previously submitted if there is an error and it posts to the same URL. The text inputs seem to load the previous value but the select and the radio buttons reset to the default every submit.
ResourcesController#new
def new
#resource = Resource.new
#title = "Submit Resource"
#categories = Category.all
end
ResourcesController#create (notice I have #categories = Category.all in both... according to DRY im not sure where else it should go, or it only works on the first form submit.
def create
#title = "Submit Resource"
#categories = Category.all
#resource = Resource.new(params[:resource])
category_ids = #categories.map { |c| c[1] }
if #resource.valid? and category_ids.include? params[:category_id]
#resource.cost = params[:cost]
#resource.category_id = params[:category_id]
#resource.save
redirect_to root_url
else
render :action => :new
end
end
Resource.rb (model)
# == Schema Information
#
# Table name: resources
#
# id :integer not null, primary key
# upvotes :integer default(0)
# downvotes :integer default(0)
# url :string(255)
# title :string(255)
# cost :integer default(0)
# description :text
# flags :integer
# category_id :integer
# user_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
class Resource < ActiveRecord::Base
belongs_to :category
belongs_to :user
has_many :favorites
has_many :resource_tags
has_many :tags, :through => :resource_tags
attr_accessible :url, :title, :cost, :description, :category_id, :user_id
# Pseudo-Enum
COST = [:free, :paid, :both]
url_regex = /^(?:http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6}(:[0-9]{1,5})?(\/.*)?$/ix
validates :url, :presence => true,
:format => { :with => url_regex,
:message => "must be valid"},
:uniqueness => { :case_sensitive => false,
:message => "has already been submitted"}
validates :title, :presence => true,
:length => { :within => 6..75 }
validates :cost, :presence => true
validates :description, :presence => true,
:length => { :within => 25..200 }
validates :category_id, :presence => true,
:format => { :with => /\d+/ }
validates :user_id, :presence => true,
:format => { :with => /\d+/ }
def cost
COST[read_attribute(:cost)]
end
def cost=(value)
write_attribute(:cost, COST.index(value.downcase.to_sym))
end
def category_id
read_attribute(:category_id).to_i
end
def category_id=(value)
write_attribute(:category_id, value.to_i)
end
end
My view file for the Resource#new form
<div class="field">
<%= f.label :category %>
<%= select_tag(:category_id, options_for_select(#categories.map {|c|[c.name, c.id]})) %>
</div>
Last Q: i havent worked with the user_id field yet. This is going to be pulled from devise and will associate a User with a submitted resource. But how do I assign this without making some sort of input, like a hidden input. Would this go on behind the scenes in the controller?
To your last question:
devise adds a current_user method which is the logged in user. So if a user has multiple resources you could do something like:
#resource = current_user.resources.new(params[:resource])
First question:
When a form is rendered it is done so based on the #resource & #categories variables. When you post the form the create action is called which creates a new #resource. If the save fails for whatever reason the form is rerendered using the new #resource variable. The problem you have is that #resource.category is not set when you show the form again. So you'll have to do this before the is_valid? check.
def create
#title = "Submit Resource"
#categories = Category.all
#resource = Resource.new(params[:resource])
#resource.category = Category.find(params[:category_id])
if #resource.valid? # won't be valid if there is no category found.
#resource.cost = params[:cost]
#resource.save
redirect_to root_url
else
render :action => :new
end
end
But the real problem is with your form. It should nest the category_id in the resource params so that the category is set when you do Resource.new(params[:resource]).
Check the POST request body in your console or something and see if it's nested in the resource or not. I don't know the exact syntax for it but if you change this you can drop the #resource.category = Category.find line.
To piggyback on Sandip, you can dry up your actions by using a before_filter
class ResourcesController < ApplicationController
before_filter :load_categories, :only => [:show, :create]
def new
#resource = Resource.new
end
def create
#resource = Resource.new(params[:resource])
#resource.category = Category.find(params[:category_id])
if #resource.valid? # won't be valid if there is no category found.
#resource.cost = params[:cost]
#resource.save
redirect_to root_url
else
render :action => :new
end
end
private
def load_categories
#categories = Category.all
end
end
also if you plan on sticking #title inside of your application layout, I would change #title in your view to:
yield(:title) || 'My Site'
and on the appropriate pages use:
content_for(:title) do
Submit Resource
It will default to 'My Site' unless otherwise specified.
Looks like there is problem with create action
def create
#title = "Submit Resource"
#categories = Category.all
#resource = Resource.new(params[:resource])
if #categories.collect(&:id).include?(params[:category_id].to_i)
#resource.category_id = params[:category_id]
end
#resource.user = current_user
if #resource.valid?
#resource.cost = params[:cost]
#resource.save
redirect_to root_url
else
render :action => :new
end
end
view
<div class="field">
<%= f.label :category %>
<%= select_tag(:category_id, options_for_select(#categories.map {|c|[c.name, c.id]}, :selected => #resource.category_id)) %>
</div>
I've used Virtual attributes in the past but I can't seem to get past this, and I know the answer is probably staring me in the face.
I have a model like so:
model Confirmation.rb
class Confirmation < ActiveRecord::Base
#attr_accessible :confirmation, :confirmation_token
#attr_accessible :confirmation_token
def confirmation_token
confirmation.confirmation_token if confirmation
end
def confirmation_token=(token)
self.confirmation = Booking.find_by_confirmation_token(token)
end
end
Your average scaffold controller for
confirmations_controller.rb
def new
#confirmation = Confirmation.new(:confirmation_token => params[:confirmation_token])
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #confirmation }
end
end
new.html.erb
<h1>New confirmation</h1>
<% form_for(#confirmation) do |f| %>
<%= f.error_messages %>
<%= f.hidden_field :confirmation_token %>
...
routes.rb
map.confirmation "confirmation/:confirmation_token", :controller => "confirmations", :action => "new"
map.resources :confirmations
error
undefined method `confirmation=' for #
In the console Booking.find_by_confirmation_token(token) with a given token works perfectly fine.
Any ideas? suggestions?
What you really need is attr_accessor :confirmation. There's a difference between attr_accessible and attr_accessor.
attr_accessor :confirmation
is same as
def confirmation
#confirmation
end
def confirmation=(value)
#confirmation = value
end
Now since it's such a common pattern ruby introduced helper methods for that.
Attr_accesible on the other hand is rails method, which marks that certain fields can be mass updated.
I think it should be either:
def confirmation_token=(token)
#confirmation = Booking.find_by_confirmation_token(token)
end
Or you should uncomment attr_accessible :confirmation or define #confirmation and #confirmation=.
class Confirmation < ActiveRecord::Base
belongs_to :bookings
#attr_accessible :confirmation, :confirmation_token
#attr_accessible :confirmation
def confirmation_token
#confirmation.confirmation_token if #confirmation
end
def confirmation_token=(token)
#confirmation = Booking.find_by_confirmation_token(token)
end
end
this worked... however just uncovering the attr_accessible :confirmation, did not. self.confirmation still returned undefined method...