I am doing a jewel loan application using ruby 2.2.2p95 (2015-04-13 revision 50295) [i686-linux] and Rails 4.2.1 in that i am having two models:
1)jewelloan.rb
2)jltransaction.rb
I have added foreign key jewelloan_id to get the loan_amount in jltransactions table. Where as loan_amount is a field in jewelloans table.
My problem is foreign key relationship not working. I have searched many stackoverflow questions to solve this but that are not working.
I have attached my model,view,controller and everything. Please tell me where i did a mistake.
jewelloan.rb
class Jewelloan < ActiveRecord::Base
attr_accessor :transaction_amount
attr_accessible :transaction_amount
has_many :jltransactions, :dependent => :destroy
accepts_nested_attributes_for :jltransactions ,:allow_destroy => true
attr_accessible :account_number, :customer_name, :customer_address, :opened_on, :due_date, :amount, :jewel, :no_of_items, :gross_weight, :net_weight, :appraised_amount, :loan_amount, :transaction_mode, :transaction_type, :particulars, :comments, :close_date, :jltransactions_attributes
end
jltransaction.rb
class Jltransaction < ActiveRecord::Base
attr_accessor :loan_amount
attr_accessible :loan_amount
belongs_to :jewelloan
attr_accessible :transaction_date, :transaction_amount, :transaction_mode, :transaction_type, :particulars, :comments, :jewelloan_id
end
jewelloans_controller.rb
class JewelloansController < ApplicationController
def new
#jewelloan = Jewelloan.new
end
def create
#jewelloan = Jewelloan.create(jewelloan_params)
if #jewelloan.save
flash[:success] = "Special JL was successfully created"
redirect_to #jewelloan
else
flash[:danger] = "Special Jewel Loan was not created"
render 'new'
end
end
private
def jewelloan_params
params.require(:jewelloan).permit(:account_number, :customer_name, :customer_address, :amount, :interest_rate, :opened_on, :due_date, :amount_due, :jewel, :no_of_items, :gross_weight, :net_weight, :appraised_amount, :loan_amount, :transaction_mode, :transaction_type, :particulars, :comments, :close_date, :no_of_days, :jltransactions_attributes)
end
end
jltransactions_controller.rb
class JltransactionsController < ApplicationController
def new
#jltransaction = Jltransaction.new
#jewelloan = Jewelloan.new
end
def create
#jltransaction = Jltransaction.create(jltransaction_params)
#jewelloan = #jltransaction.jewelloans(jewelloan_params)
if #jltransaction.save
flash[:success] = "Transaction created"
redirect_to #jltransaction
else
flash[:danger] = "Transaction was not created"
render 'new'
end
end
private
def jltransaction_params
params.require(:jltransaction).permit(:transaction_date, :transaction_amount, :transaction_mode, :transaction_type, :particulars, :comments, :jewelloan_id)
end
end
app/views/jltransactions/_form.html.erb
<%= form_for #jltransaction do |jlt| %>
<%= jlt.hidden_field :jewelloan_id, :value => #jewelloan.id %>
<% if #jltransaction.errors.any? %>
<h2>Transaction Failed</h2>
<ul>
<% #jltransaction.errors.full_messages.each do |error| %>
<li><%= error %></li>
<% end %>
</ul>
<% end %>
<%= jlt.label :loan_amount %>
for<%= jlt.text_field :loan_amount, :disabled => true, :value => #jltransaction.jewelloan.try(:loan_amount) %>
<%= jlt.label :transaction_amount %>
<%= jlt.text_field :transaction_amount %>
<%= jlt.submit "Submit Transaction", class: "btn btn-success" %>
<% end %>
rails console
Completed 500 Internal Server Error in 34ms (ActiveRecord: 0.0ms)
ActionView::Template::Error (undefined method `loan_amount' for nil:NilClass):
Please give some ideas to work the foreign key.
Thanks...
Your relationship might be returning nil result.
Try
#jltransaction.jewelloan.try(:loan_amount)
You may try this:
<%= jlt.text_field :loan_amount, :disabled => true, :value => #jltransaction.jewelloan.try(:loan_amount) if #jltransaction && #jltransaction.jewelloan %>
Related
I am currently working on a reservation system in rails 5.0.1. I have a user, room and reservation model:
user.rb
has_many :reservations
room.rb
has_many :reservations
reservation.rb
belongs_to :user
belongs_to :room
reservation_controller.rb
class ReservationsController < ApplicationController
before_action :authenticate_user!
def create
#reservation = current_user.reservations.create(reservation_params)
redirect_to #reservation.room, notice: "Your reservation has been created"
end
private
def reservation_params
params.require(:reservation).permit(:start_date, :end_date, :price, :total, :room_id, :user_id)
end
end
_form.html.erb (view)
<%= form_for([#room, #room.reservations.new]) do |f| %>
<%= f.text_field :start_date %>
<%= f.text_field :end_date %>
<%= f.hidden_field :room_id, value: #room_id %>
<%= f.hidden_field :price, value: #room_price %>
<br>
<%= f.submit "Book Now", class: "btn btn-primary" %>
<% end %>
I'm getting this error:
Question:
Why is my #reservation.room nil?
If I type in the rails console: #reserveration = current_user.reservations.create(reservation_params)
Then I am getting:
NameError: undefined local variable or method `current_user' for main:Object
If you need further information just let me know!
Why is my instance variable (#reservation) nil?
It's not. It is #reservation.room that is nil.
Before you ask "why is that then?", inspect your params that you post to the action.
Solution:
The #room variable was missing and then I had to merge it!
reservation_controller.rb
class ReservationsController < ApplicationController
before_action :authenticate_user!
def create
#room = Room.find(params[:room_id])
#reservation = current_user.reservations.create(reservation_params.merge(room_id: #room.id))
redirect_to #reservation.room, notice: "Your reservation has been created"
end
private
def reservation_params
params.require(:reservation).permit(:start_date, :end_date, :price, :total, :room_id)
end
end
I am trying to use fields_for and create a nested form, however only one text field shows up, blank. I have 3 crewmember records.
crewmember model:
class Crewmember < ActiveRecord::Base
belongs_to :production
belongs_to :callsheet
validates :firstname, presence: true
validates :email, presence: true
def name
"#{firstname} #{lastname}"
end
end
callsheet model
class Callsheet < ActiveRecord::Base
attr_accessible :crewmembers_params
has_many :castmembers
has_many :crewmembers
accepts_nested_attributes_for :crewmembers
end
callsheets controller
class CallsheetsController < ApplicationController
def index
#callsheets = Callsheet.all
#departments = Department.where(production_id: current_user.default_working_production_id)
end
def show
#callsheet = Callsheet.find(params[:id])
end
def new
#callsheet = Callsheet.new
#departments = Department.where(production_id: current_user.default_working_production_id)
end
def edit
#callsheet = Callsheet.find(params[:id])
end
def create
#callsheet = Callsheet.new(callsheets_params)
#Callsheet.production_id = current_user.default_working_production_id
if #callsheets.save
redirect_to callsheet_path
else
render 'new'
end
end
def update
#callsheet = Callsheet.find(params[:id])
if #callsheet.update(callsheets_params)
redirect_to callsheet_path, :notice => "callsheets successfully updated."
else
render 'edit', :notice => "callsheets not updated."
end
end
def destroy
#callsheet = Callsheet.find(params[:id])
#callsheet.destroy
redirect_to callsheets_path
end
private
def callsheets_params
params.require(:callsheet).permit(:crewmembers_params [:id, :firstname])
end
end
form for new callsheet:
<%= form_for #callsheet do |f| %>
<% if #callsheet.errors.any? %>
<div id="error_explanation" class="alert alert-danger">
<strong>
<%= pluralize(#callsheet.errors.count, "error") %> prohibited
this call sheet from being saved:
</strong>
<ul>
<% #callsheet.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.fields_for :crewmember do |crewmember| %>
<fieldset>
<%= crewmember.label :firstname, "First Name" %><br />
<%= crewmember.text_field :firstname %>
</fieldset>
<% end %>
<% end %>
You don't need attr_accessible (that's only for Rails 3).
You should also rename all your models to snake_case, referencing with CamelCase:
#app/models/call_sheet.rb
class CallSheet < ActiveRecord::Base
has_many :cast_members
has_many :crew_members
accepts_nested_attributes_for :crew_members
end
As is the custom with fields_for, you also need to build the associated objects (if you're creating a new record) (you don't need to do this if editing):
#app/controllers/call_sheets_controller.rb
class CallSheetsController < ApplicationController
before_action :set_departments
def new
#callsheet = Callsheet.new
#callsheet.crew_members.build
end
def edit
#callsheet = Callsheet.find params[:id]
end
def update
#callsheet = Callsheet.find params[:id]
#callsheet.update callsheet_params
end
private
def set_departments
#departments = Department.where(production_id: current_user.default_working_production_id)
end
def callsheet_params
params.require(:callsheet).permit(crew_members_attributes: [:id, :firstname])
end
end
This will allow you to use:
<%= form_for #callsheet do |f| %>
<%= f.fields_for :crew_members do |crewmember| %>
<%= crewmember.label :firstname, "First Name" %><br />
<%= crewmember.text_field :firstname %>
<% end %>
<%= f.submit %>
<% end %>
--
When passing nested attributes through fields_for, you need several components:
The correct association in your parent model
An instantiated version of the associated model (#parent.build_child)
Correct fields_for definition
Passing correct parameters through your controller
I've outlined how to achieve the above, all of which you had incorrect.
You can also declare multiple validations in the same call:
#app/models/crew_member.rb
class CrewMember < ActiveRecord::Base
validates :firstname, :email, presence: true
end
Try changing
<%= f.fields_for :crewmember do |crewmember| %>
into
<%= f.fields_for :crewmember, #callsheet.crewmember || #callsheet.build_crewmember do |crewmember| %>
I posted an earlier question about this and was advised to read lots of relevant info. I have read it and tried implementing about 30 different solutions. None of which have worked for me.
Here's what I've got.
I have a Miniatures model.
I have a Manufacturers model.
Miniatures have many manufacturers THROUGH a Productions model.
The associations seem to be set up correctly as I can show them in my views and create them via the console. Where I have a problem is in letting the Miniatures NEW and EDIT views create and update to the Productions table.
In the console the command #miniature.productions.create(manufacturer_id: 1) works, which leads me to believe I should be able to do the same in a form.
I THINK my problem is always in the Miniatures Controller and specifically the CREATE function. I have tried out a ton of other peoples solutions there and none have done the trick. It is also possible that my field_for stuff in my form is wrong but that seems less fiddly.
I've been stuck on this for days and while there are other things I could work on, if this association isn't possible then I'd need to rethink my entire application.
The form now creates a line in the Productions table but doesn't include the all important manufacturer_id.
Any help VERY much appreciated.
My New Miniature form
<% provide(:title, 'Add miniature') %>
<h1>Add a miniature</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for(#miniature) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.fields_for :production do |production_fields| %>
<%= production_fields.label :manufacturer_id, "Manufacturer" %>
<%= production_fields.select :manufacturer_id, options_from_collection_for_select(Manufacturer.all, :id, :name) %>
<% end %>
<%= f.label :release_date %>
<%= f.date_select :release_date, :start_year => Date.current.year, :end_year => 1970, :include_blank => true %>
<%= f.submit "Add miniature", class: "btn btn-large btn-primary" %>
<% end %>
</div>
</div>
Miniatures controller
class MiniaturesController < ApplicationController
before_action :signed_in_user, only: [:new, :create, :edit, :update]
before_action :admin_user, only: :destroy
def productions
#production = #miniature.productions
end
def show
#miniature = Miniature.find(params[:id])
end
def new
#miniature = Miniature.new
end
def edit
#miniature = Miniature.find(params[:id])
end
def update
#miniature = Miniature.find(params[:id])
if #miniature.update_attributes(miniature_params)
flash[:success] = "Miniature updated"
redirect_to #miniature
else
render 'edit'
end
end
def index
#miniatures = Miniature.paginate(page: params[:page])
end
def create
#miniature = Miniature.new(miniature_params)
if #miniature.save
#production = #miniature.productions.create
redirect_to #miniature
else
render 'new'
end
end
def destroy
Miniature.find(params[:id]).destroy
flash[:success] = "Miniature destroyed."
redirect_to miniatures_url
end
private
def miniature_params
params.require(:miniature).permit(:name, :release_date, :material, :scale, :production, :production_attributes)
end
def admin_user
redirect_to(root_url) unless current_user.admin?
end
def signed_in_user
unless signed_in?
store_location
redirect_to signin_url, notice: "Please sign in."
end
end
end
Miniature model
class Miniature < ActiveRecord::Base
has_many :productions, dependent: :destroy
has_many :manufacturers, :through => :productions
accepts_nested_attributes_for :productions
validates :name, presence: true, length: { maximum: 50 }
validates :material, presence: true
validates :scale, presence: true
validates_date :release_date, :allow_blank => true
def name=(s)
super s.titleize
end
end
Production model
class Production < ActiveRecord::Base
belongs_to :miniature
belongs_to :manufacturer
end
Manufacturer model
class Manufacturer < ActiveRecord::Base
has_many :productions
has_many :miniatures, :through => :productions
validates :name, presence: true, length: { maximum: 50 }
accepts_nested_attributes_for :productions
end
Instead of calling:
#production = #miniature.productions.create
Try Rails' "build" method:
def new
#miniature = Miniature.new(miniature_params)
#miniature.productions.build
end
def create
#miniature = Miniature.new(miniature_params)
if #miniature.save
redirect_to #miniature
else
render 'new'
end
end
Using the build method uses ActiveRecord's Autosave Association functionality.
See http://api.rubyonrails.org/classes/ActiveRecord/AutosaveAssociation.html
You also need to update your params method, e.g.
def miniature_params
params.require(:miniature).permit(:name, :release_date, :material, :scale, productions_attributes: [:manufacturer_id])
end
Also your fields_for should be plural (I think)...
<%= f.fields_for :productions do |production_fields| %>
I have Users who bet on matches. A single bet is called "Tipp" and the users predict the match score in "tipp.tipp1" and "tipp.tipp2"
I have problems with my form which is supposed to save "tipps" of users.
With the code below I get "Can't mass-assign protected attributes: tipp" although i have set "accepts_nested_attributes_for :tipps" and "attr_accessible :tipps_attributes".
I hope I have provided all the necessary code. Thanks in advance for your help!
Here is the parameters output:
Parameters:
{
"utf8"=>"✓",
"_method"=>"put",
"authenticity_token"=>"mPPpCHjA3f/M2l1Bd3ffO1QUr+kdETGkNE/0CNhbJXE=",
"user" =>{
"tipp"=>{
"6"=>{"tipp1"=>"4","tipp2"=>"6"},
"7"=>{"tipp1"=>"-1","tipp2"=>"-1"},
"8"=>{"tipp1"=>"-1","tipp2"=>"-1"}
}
},
"commit"=>"Update User",
"user_id"=>"1"
}
Shortened Code:
Controllers:
1) Users
class UsersController < ApplicationController
def edit_tipps
#user = current_user
end
def update_tipps
#user = current_user
if #user.update_attributes(params[:user])
flash[:notice] = "success (maybe)"
redirect_to user_edit_tipps_path(#user)
else
flash[:error] = "errors"
redirect_to user_edit_tipps_path(#user)
end
end
Models:
1) Users
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :tipps_attributes
has_many :tipps
accepts_nested_attributes_for :tipps
end
2) Tipps
class Tipp < ActiveRecord::Base
attr_accessible :match_id, :points, :round_id, :tipp1, :tipp2, :user_id
belongs_to :user
end
My Form:
<%= form_for #user, :url => { :action => "update_tipps" } do |user_form| %>
<% #user.tipps.each do |tipp| %>
<%= user_form.fields_for tipp, :index => tipp.id do |tipp_form|%>
<%= tipp_form.text_field :tipp1 %><br/>
<%= tipp_form.text_field :tipp2 %><br/>
<% end %>
<% end %>
<%= submit_or_cancel(user_form) %>
<% end %>
Instead of doing what you did,
you could try either:
1.
Instead of:
<% #user.tipps.each do |tipp| %>
<%= user_form.fields_for tipp, :index => tipp.id do |tipp_form|%>
I would do this:
<%= user_form.fields_for :tipps do |tipp_form| %>
Or:
2.
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :tipps_attributes, :tipps
Goodluck
I'm doing what appears to be a common learning app for Ruby on Rails, the recipe app. Specifically, working on recipes and ingredients as a has_many :through relationship. Through looking at a million examples and questions, I've got my many-to-many relationship setup and my multi-model form working, but I'd like to add an additional field and can't get it working. Feels like I'm close to understanding how this stuff works. Here are the quick details:
Models:
class Ingredient < ActiveRecord::Base
has_many :recipe_ingredients
has_many :recipes, :through => :recipe_ingredients
end
class RecipeIngredient < ActiveRecord::Base
belongs_to :recipe
belongs_to :ingredient
end
class Recipe < ActiveRecord::Base
has_many :recipe_ingredients
has_many :ingredients, :through => :recipe_ingredients
accepts_nested_attributes_for :ingredients, :recipe_ingredients
def new_recipe_ingredient_attributes=(recipe_ingredient_attributes)
recipe_ingredient_attributes.each do |attributes|
recipe_ingredients.build(attributes)
end
end
def existing_recipe_ingredient_attributes=(recipe_ingredient_attributes)
recipe_ingredients.reject(&:new_record?).each do |recipe_ingredient|
attributes = recipe_ingredient_attributes[recipe_ingredient.id.to_s]
if attributes
recipe_ingredient.attributes = attributes
else
recipe_ingredient.delete(recipe_ingredient)
end
end
end
def save_recipe_ingredients
recipe_ingredients.each do |recipe_ingredient|
recipe_ingredient.save(false)
end
end
end
Controller:
def create
#recipe = Recipe.new(params[:recipe])
if #recipe.save
redirect_to :action => 'show', :id => #recipe
flash[:notice] = "Your record has been saved."
else
render :action => 'new'
end
end
def update
params[:recipe][:existing_recipe_ingredient_attributes] ||= {}
#recipe = Recipe.find(params[:id])
if #recipe.update_attributes(params[:recipe])
redirect_to :action => 'show', :id => #recipe
flash[:notice] = "Your changes have been saved."
else
render :action => 'edit'
end
end
View:
<% form_for(#recipe) do |f| %>
<%= f.label :name %><br />
<%= f.text_field :name %>
etc.....
Ingredients:
<div id="recipe_ingredients">
<div class="recipe_ingredient">
<% new_or_existing = recipe_ingredient.new_record? ? 'new' : 'existing' %>
<% prefix = "recipe[#{new_or_existing}_recipe_ingredient_attributes][]" %>
<% fields_for prefix, recipe_ingredient do |ri_form| %>
<p>
<%= ri_form.collection_select(:id, Ingredient.find(:all), :id, :name, :include_blank => true) %>
<%= ri_form.text_field :amount %>
</p>
<% end %>
</div>
</div>
</p>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>
Sorry for the wall of code, hopefully it makes sense. The thing I can't understand is why the "amount" text field doesn't work. I've tried a million different ways, but can't get it working. In this case, the error I get is "undefined method `amount' for #"
What key connection am I missing here? Thanks.
At first glance it appears you should simply replace:
<% fields_for prefix, recipe_ingredient do |ri_form| %>
with:
<%= fields_for prefix, recipe_ingredient do |ri_form| %>