Many-to-Many Nested Attributes in Rails 4 (with strong parameters) - many-to-many

I have been trying to figure this one out for a few days now. I am using Rails 4 (with the updated mass assignment technique) and trying to use nested attributes with a many-to-many relationship. My record is saving to the DB but everything is nil and I'm getting an "Unpermitted parameters: school, alumnis, prospects" error in my logs.
Here's what I have:
referral.rb
class Referral < ActiveRecord::Base
belongs_to :school
belongs_to :alumni
belongs_to :prospect
end
alumni.rb
class Alumni < ActiveRecord::Base
has_many :referrals
has_many :prospects, through: :referrals
accepts_nested_attributes_for :referrals
end
school.rb
class School < ActiveRecord::Base
has_many :referrals
has_many :prospects, through: :referrals
has_many :alumnis, through: :referrals
accepts_nested_attributes_for :referrals
end
prospect.rb
class Prospect < ActiveRecord::Base
has_many :referrals
has_many :alumnis, through: :referrals
accepts_nested_attributes_for :referrals
end
referrals_controller.rb
def create
#referral = Referral.create(referral_params)
respond_to do |format|
if #referral.save
# ReferralMailer.referrer_email(#referral).deliver
# ReferralMailer.referral_email(#referral).deliver
format.html { redirect_to #referral, notice: 'Referral was successfully created.' }
format.json { render action: 'show', status: :created, location: #referral }
else
format.html { render action: 'new' }
format.json { render json: #referral.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_referral
#referral = Referral.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def referral_params
params.require(:referral).permit(prospects_attributes: [:first_name, :last_name, :email], alumnis_attributes: [:first_name, :last_name, :email], schools_attributes: [:name])
end
_form.html.erb
<%= form_for(#referral) do |f| %>
<% if #referral.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#referral.errors.count, "error") %> prohibited this referral from being saved:</h2>
<ul>
<% #referral.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.fields_for :school do |builder| %>
<%= builder.label :name, "School Name" %>
<%= builder.text_field :name %>
<% end %>
<%= f.fields_for :alumnis do |builder| %>
<%= builder.label :first_name, "First Name" %>
<%= builder.text_field :first_name %>
<%= builder.label :last_name, "Last Name" %>
<%= builder.text_field :last_name %>
<%= builder.label :email, "Email" %>
<%= builder.text_field :email %>
<% end %>
<%= f.fields_for :prospects do |builder| %>
<%= builder.label :first_name, "First Name" %>
<%= builder.text_field :first_name %>
<%= builder.label :last_name, "Last Name" %>
<%= builder.text_field :last_name %>
<%= builder.label :email, "Email" %>
<%= builder.text_field :email %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
server log output
Processing by ReferralsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Ee+rREUU/0wGzNFTEaMxr8oRStaA53X9fmDrlVRyrD8=", "referral"=>{"school"=>{"name"=>"asdf"}, "alumnis"=>{"first_name"=>"asdf", "last_name"=>"asfd", "email"=>"asdf"}, "prospects"=>{"first_name"=>"asdf", "last_name"=>"asdf", "email"=>"asdf"}}, "commit"=>"Create Referral"}
Unpermitted parameters: school, alumnis, prospects
(0.1ms) begin transaction
SQL (0.5ms) INSERT INTO "referrals" ("created_at", "updated_at") VALUES (?, ?) [["created_at", Fri, 12 Jul 2013 03:49:06 UTC +00:00], ["updated_at", Fri, 12 Jul 2013 03:49:06 UTC +00:00]]
(0.6ms) commit transaction
(0.1ms) begin transaction
(0.1ms) commit transaction
Redirected to http://localhost:3000/referrals/68
Referral Record
=> #<Referral id: 68, created_at: "2013-07-12 03:49:06", updated_at: "2013-07-12 03:49:06", school_id: nil, alumni_id: nil, prospect_id: nil>

You should pass also 'id' in each of your nested model params try :
def referral_params
params.require(:referral).permit(prospects_attributes: [:id,:first_name, :last_name, :email], alumnis_attributes: [:id,:first_name, :last_name, :email], schools_attributes: [:id,:name])
end
Have swing
Cheers

You parameters are not being past to the controller as strong parameters is expecting.
From you server log:
"referral" => {
"school" => {
"name" => "asdf" },
"alumnis" => {
"first_name" => "asdf",
"last_name" => "asfd",
"email" => "asdf"
},
"prospects" => {
"first_name" => "asdf",
"last_name" => "asdf",
"email" => "asdf"
}
}
Strong parameters is expecting prospects_attributes, alumnis_attributes and schools_attributes so prospects, alumnis and school are getting blocked and the objects are getting created without any attributes.

Related

Validating then using virtual attributes to create a devise user

So I have two models, User and Employee. User has one employee and Employee belongs to User. I want to create an employee but first I have to create a new User. My Employee model does not have the attributes :email, :password, :password_confirmation so I created virtual attributes. This is the error that pops up Validation failed: Email is invalid, Password confirmation doesn't match Password
here is my employee model
class Employee < ApplicationRecord
belongs_to :user
attr_accessor :email, :password, :password_confirmation
validates :email, format: { with: /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, on: :create }
validates :password, confirmation: true
end
my employee controller
class EmployeesController < ApplicationController
def create
#newuser=User.create!(
email: :email,
password: :password,
password_confirmation: :password_confirmation
)
#employee = Employee.new(employee_params)
respond_to do |format|
if #employee.save
format.html { redirect_to #employee, notice: 'Employee was successfully created.' }
format.json { render :show, status: :created, location: #employee }
else
format.html { render :new }
format.json { render json: #employee.errors, status: :unprocessable_entity }
end
end
end
private
def employee_params
params.require(:employee).permit(:name, :contact_no, :role_id, #newuser.id)
end
end
And my form
<%= form_for(employee) do |f| %>
<% if employee.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(employee.errors.count, "error") %> prohibited this employee from being saved:</h2>
<ul>
<% employee.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :email %>
<%= f.email_field :email %>
</div>
<div class="field">
<%= f.password_field :password %>
<%= f.password_field :password_confirmation %>
</div>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :contact_no %>
<%= f.text_field :contact_no %>
</div>
<div class="field">
<%= f.label :role_id %>
<%= f.number_field :role_id %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I'm still learning rails and would greatly appreciate your help
if you don't have attributes :email, :password, :password_confirmation then remove the following validation:
validates :email, format: { with: /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, on: :create }
validates :password, confirmation: true
from Employee Model.
I have found the solution to my problem, it seems that my user parameters weren't following rails' strong parameters rule. so my controller has this now
def employee_params
params.require(:employee).permit(:name, :contact_no, :role_id)
end
def user_params
params.require(:employee).permit(:email, :password, :password_confirmation)
end
I was then able to make the user using the parameters without a problem.

ruby form edit does not save a nested form

I have a nested form that i use to create new records in the database. However, the edit functionality doesn't update those records once edited. I seem to have everything set up correctly, as i didn't find any abnormalities when comparing it to code i found on the internet or scaffolded code. What am i doing wrong?
This is part of enquiries_controller:
def edit
#nog op kunnen slaan!!!! 1-01-17 Marco
#enquiry = Enquiry.find(params[:id])
#enquiry.enquirymeasures.build
#enquiry.tools.build
#enquiry.build_applicant
#enquiry.signatures.build
#enquiry.gasmeters.build
end
# POST /enquiries
# POST /enquiries.json
def create
#enquiry.user_id = current_user.id
#enquiry = Enquiry.new(enquiry_params)
##enquiry.enquirymeasures.build
respond_to do |format|
if #enquiry.save
format.html { redirect_to #enquiry, notice: 'Enquiry was successfully created.' }
format.json { render :show, status: :created, location: #enquiry }
else
format.html { render :new }
format.json { render json: #enquiry.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /enquiries/1
# PATCH/PUT /enquiries/1.json
def update
respond_to do |format|
if #enquiry.update(enquiry_params)
format.html { redirect_to #enquiry, notice: 'Enquiry was successfully updated.' }
format.json { render :show, status: :ok, location: #enquiry }
else
format.html { render :edit }
format.json { render json: #enquiry.errors, status: :unprocessable_entity }
end
end
end
THe enquiry model
class Enquiry < ActiveRecord::Base
has_many :enquirymeasures, dependent: :destroy
accepts_nested_attributes_for :enquirymeasures, :reject_if => lambda { |a| a[:responsible].blank? }, :allow_destroy => true
has_many :tools, dependent: :destroy
accepts_nested_attributes_for :tools
has_many :controls, dependent: :destroy
accepts_nested_attributes_for :controls
has_one :applicant, dependent: :destroy
accepts_nested_attributes_for :applicant
has_one :contractor, through: :applicant
has_many :signatures, dependent: :destroy
accepts_nested_attributes_for :signatures
has_many :representatives , through: :signatures, :source => :representatives
has_many :gasmeters, dependent: :destroy
accepts_nested_attributes_for :gasmeters
belongs_to :user
end
and part of the _form partial(over 200 lines in total):
<%= form_for(#enquiry) do |f| %>
<form role="form">
<% if #enquiry.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#enquiry.errors.count, "error") %> prohibited this enquiry from being saved:</h2>
<ul>
<% #enquiry.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<h1 align="center">Vul de onderstaande velden in om een werkvergunning aan te vragen.</h1>
<p align="center">Algemene informatie</p>
..........
<p align="center">De laatste stap bestaat uit het ondertekenen van het formulier met uw handtekening.</p>
<div class="form-group">
<%= f.fields_for :signatures do |s| %>
<%= f.fields_for :representatives do |sr| %>
<%= s.label :Gemachtigde %><br>
<%= s.collection_select(:representative_id, Representative.all, :id, :name, prompt: true) %>
<br>
<%= s.label :datum %><br>
<%= s.datetime_select :date %>
<br>
<%= s.label :Handtekening %><br>
<%= s.text_field :signature %>
<%# 17-1-2017 baseren van 1 dropdown op de andere %>
<% end %>
<% end %>
</div>
<div class="form-group">
<p> Goedkeuring van het formulier door JPB Groep</p>
<% if can? :manage, Enquiry %>
<%= f.label :Goedgekeurd %>
<%= f.check_box :approved %>
<%end%>
</div>
<div class="actions">
<%= f.submit %>
</div>
</form>
<% end %>
After some fiddling, i did manage to get the data entered in the record visible when editing a record, but saving doesnt work. I can see that the console uses push, but it doesn't fire a update query.
I get no error so the page just goes back to the index page when hitting submit.
Added route
Rails.application.routes.draw do
devise_for :users
scope "/Admin" do
resources :users, :enquiries, :roles
end
scope "/Aanvrager" do
resources :users, :enquiries
end
mount RailsAdmin::Engine => '/admin', :as => 'rails_admin'
root 'enquiries#index'
get 'welcome/Index'
enquiry_params
def enquiry_params
params.require(:enquiry).permit(:id, :reference, :location, :description, :date, :amount, :approved, enquirymeasures_attributes: [:id, :responsible, :done, :needed, :measurement_id, :user],
tools_attributes: [:id, :handtool, :other, :motorvehicle, :compressor, :ramp, :scaffold, :crane, :ladder, :generator, :tankladder],
applicant_attributes: [:id, :name, :email, :contractor_id],
signatures_attributes: [:id, :date, :signature, :representative_id],
gasmeters_attributes: [:id, :date, :tester, :signature, :oxigen, :o_needed, :o_continu, :explosives, :e_needed, :e_continu, :mat1, :mat1_needed, :mat1_continu, :mat2, :mat2_needed, :mat2_continu, :mat3, :mat3_needed, :mat3_continu]).merge(user_id: current_user.id)
end

Hash password not saved in the password column

I am trying to store hash password in my users table while registration. Please see my code:
users_controller.rb
def login
#title = 'Login'
#render layout: 'login'
end
def create_login
user = User.authenticate(params[:user][:username], params[:user][:password])
if user
log_in user
redirect_to '/admin'
else
flash[:danger] = 'Invalid email/password combination' # Not quite right!
redirect_to :back
end
end
def register
#user = User.new
#title = 'Register'
end
def create_register
params[:user][:uniq_id] = generate_uniq
#user = User.new(create_user_params)
#raise #user.inspect
respond_to do |format|
if #user.save
format.html { redirect_to :success, success: 'Registration was successfully created.' }
format.json { redirect_to :register, status: :created, location: #users }
else
format.html { render :register }
format.json { render json: #users.errors, status: :unprocessable_entity }
end
end
end
private
def create_user_params
params.require(:user).permit(:uniq_id, :name, :username, :email, :password, :password_confirmation, :password_salt, :dob, :address)
end
register.html.erb
<%= form_tag("/register", method: "post") do %>
<%#= form_tag(#user) do |f| %>
<% if #user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% #user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= text_field :user, :name, placeholder:'NAME', required: true %>
<div style="position: relative;">
<span id="chk-username" style="position: absolute;font-size: 12px;right: 2%; bottom: 5%; z-index: 9; display: block;"></span>
<%= text_field :user, :username, placeholder:'USERNAME', 'data-validate':"/users/check_username", required: true %>
</div>
<div style="position: relative;">
<span id="chk-email" style="position: absolute;font-size: 12px;right: 2%; bottom: 5%; z-index: 9; display: block;"></span>
<%= text_field :user, :email, placeholder:'EMAIL', 'data-validate':"/users/check_email", required: true %>
</div>
<%= password_field :user, :password, placeholder:'PASSWORD', required: true %>
<%= password_field :user, :password_confirmation, placeholder:'CONFIRM PASSWORD', required: true %>
<div class="submit">
<input type="submit" value="REGISTER" >
<input type="button" onclick="location.href = '<%= request.base_url %>/login'" value="LOGIN" >
</div>
<p>Forgot Password ?</p>
<% end %>
user.rb
class User < ActiveRecord::Base
#has_secure_password
attr_accessor :password
before_save :encrypt_password
validates :name, presence: true
validates :name, length: { minumum:2, maximum: 30 }
validates :password, :presence =>true,
:length => { :minimum => 6, :maximum => 40 },
:confirmation =>true
validates :username, :presence => true,
:uniqueness => { :case_sensitive => false }
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, :presence => true,
:format => { :with => email_regex },
:uniqueness => { :case_sensitive => false }
def self.authenticate(input_username, input_password)
user = find_by_username(input_username)
if user && user.password == BCrypt::Engine.hash_secret(input_password, user.password_salt)
user
else
nil
end
end
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password = BCrypt::Engine.hash_secret(password, password_salt)
end
end
end
routes.rb
get 'register' => 'users#register'
post 'register' => 'users#create_register'
Here is my database table.
users.sql (customize table)
+----+----------+------------+-----------+----------------+
| id | name | username | password | password_salt |
+----+----------+------------+-----------+----------------+
| 1 | chinmay | chinu | NULL |$2a$10$15fWDt.. |
| 2 | sanjib | sanjib | NULL |$2a$10$85DyMr.. |
+----+----------+------------+-----------+----------------+
I get NULL value in my password column. Please help me and let me know where the error is in my code.
Your main error is that your are using attr_accessor :password to create a getter/setter for the password attribute that overrides the getter and setter that ActiveRecord creates from the database schema.
However your whole approach to password encryption is flawed - you should have password as a purely virtual attribute and name your database column password_digest or encrypted_password.
Unless its for pure learning purposes should use the has_secure_password macro that Rails provides instead of reinventing the password encryption wheel and getting hacked.
1. Add a password_digest column to user:
rails g migration AddPassWordDigestToUser password_digest:string:index
You might want to remove the password_salt column as well as it is not used by ActiveModel::SecurePassword.
class AddPassWordDigestToUser < ActiveRecord::Migration
def change
add_column :users, :password_digest, :string
add_index :users, :password_digest
remove_column :users, :password_salt
remove_column :users, :password
end
end
2. Add has_secure_password to the User model:
class User < ActiveRecord::Base
has_secure_password
end
3. RESTful routes
You may want to correct your routes so they are resource oriented and not action oriented and follow the rails conventions:
GET /registrations/new registations#new - sign up form
POST /registrations registations#create - create user
GET /sessions/new sessions#new - sign in form
POST /sessions sessions#create - sign in user
You can setup the routes with just:
resources :registrations, only: [:new, :create]
resources :sessions, only: [:new, :create]
See Rails Routing from the Outside In.
4. Binding forms and controllers.
You are setting up the controller properly however your form is not bound to the #user model instance you are creating in your controller.
This means that the values the user enters disappear after a unsuccessful form submission.
Also pay attention to the pluralization and naming of your variables! You are inconsistently using #user and #users. In this case #users will always be nil and cause an error.
app/controllers/registrations_controller.rb:
class RegistationsController < ApplicationController
def new
#user = User.new
end
def create
# Use a block instead of messing with the incoming params.
#user = User.new(user_params) do |u|
u.uniq_id = generate_uniq
end
if #user.save
respond_to do |format|
format.html { redirect_to root_path, success: "Welcome #{#user.email}" }
format.json { status: :created, location: #user }
end
else
respond_to do |format|
format.html { redirect_to :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
app/views/registrations/new.html.erb:
<%= form_for(#user, url: registrations_path) do |f| %>
<div class="row">
<%= f.label :email %>
<%= f.text_field :email %>
</div>
<div class="row">
<%= f.label :password %>
<%= f.password_field :password %>
</div>
<div class="row">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %>
</div>
<% end %>

Ruby on rails form data not not getting saved

So i am getting trouble in saving form data,.Any help will b appreciable
form is submitted without getting any error, but in database, nothing is stored
i am new on rails
users_controller
class UsersController < ApplicationController
def new
#user= User.new
end
def create
#user = User.new(params[:User])
if #user.save
flash[:notice]= "you signed up successfully"
flash[:color]= "valid"
else
flash[:notice]= "failed"
flash[:color]="invalid"
end
render "new"
end
end
new.html.erb
<% page_title="Signup" %>
<div class="Sign_Form">
<h1>Sign up</h1>
<%= form_for(:user, :url => {:controller => 'users', :action => 'create'}) do |f| %>
<p> Username:</br> <%= f.text_field :username %> </p>
<p> Email:</br> <%= f.text_field :email %> </p>
<p> Password:</br> <%= f.password_field :password %></p>
<p> Password Confirmation:</br> <%= f.password_field :password_confirmation %> </p>
<%= f.submit :Signup %>
<% end %>
<% if #user.errors.any? %>
<ul class="Signup_Errors">
<% for message_error in #user.errors.full_messages %>
<li>* <%= message_error %></li>
<% end %>
</ul>
<% end %>
</div>
in app/models/user.rb
class User < ActiveRecord::Base
attr_accessor :password
EMAIL_REGEX = /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i
validates :username, :presence => true, :uniqueness => true, :length => { :in => 3..20 }
validates :email, :presence => true, :uniqueness => true #:format => EMAIL_REGEX
validates :password, :presence =>true #:confirmation => true #password_confirmation attr
validates_length_of :password, :in => 6..20, :on => :create
end
In users_controller, create method, you are using
#user = User.new(params[:User])
replace it with following code, hope it will work fine.
#user = User.new(params[:user])
And,also use strong params if you are using rails 4. Like follwoing.
def create
#user = User.new(user_params)
if #user.save
flash[:notice]= "you signed up successfully"
flash[:color]= "valid"
else
flash[:notice]= "failed"
flash[:color]="invalid"
end
render "new"
end
private
def user_params
params.require(:user).permit(:username, :account, :email, :password, :password_confirmation)
end
If, it still not works, then, please display your log.
If you are using Rails 4 you need to use strong parameters to whitelist the parameters you want to assign to your models.
This became non-optional in Rails 4 to prevent mass-assignment vulnerabilities where a malicious user can assign any property to a model after Egor Homakovs much publicised Github attack.
Also note that Ruby is case sensitive. This applies to hash keys as well:
irb(main):003:0> hash = { a: 1 }
=> {:a=>1}
irb(main):004:0> hash[:A]
=> nil
Which is why why you do User.new(params[:User]) you are actually doing User.new(nil)
This is a corrected version of your controller
class UsersController < ApplicationController
def new
#user= User.new
end
def create
#user = User.new(params[:User])
if #user.save
flash[:notice]= "you signed up successfully"
flash[:color]= "valid"
# You should redirect instead of rendering the form again
redirect_to #user # or redirect_to root_path
else
flash[:notice]= "failed"
flash[:color]="invalid"
render "new" # Needs to be inside the "else" statement
# Otherwise you will get a double render error
end
end
def user_params
params.require(:user)
.allow(:username, :email, :password, :password_confirmation)
end
end
Added:
You can also simplify your form_for to
<%= form_for(:user) do |f| %>
Rails will by convention route the form to UserController#create.
Also you should use <label> tags for accessibility, as they help people who use assistive technology such as screen readers to find the correct inputs.
By using the built in label helper rails will set up the for attribute and you can translate the label texts with Rails built in I18n functionality.
<% page_title="Signup" %>
<div class="Sign_Form">
<h1>Sign up</h1>
<%= form_for(:user) do |f| %>
<div class="row">
<%= f.label :username %>:</br>
<%= f.text_field :username %>
</div>
<div class="row">
<%= f.label :email %>:</br>
<%= f.text_field :email %>
</div>
<div class="row">
<%= f.label :password %>:</br>
<%= f.password_field :password %>
</div>
<%= f.submit :Signup %>
<% end %>
<% if #user.errors.any? %>
<ul class="Signup_Errors">
<%# for loops are almost never used in ruby. %>
<%# .each is the idiomatically correct way %>
<% #user.errors.full_messages.each do |message_error| %>
<li>* <%= message_error %></li>
<% end %>
</ul>
<% end %>
</div>

Rails: Issue with recieving nested forms with has many through join

I seem to be having a problem with receiving products through my join table, it's giving me a strange error as it seems to be receiving no ID for my order. I can only assume that this is because the order has not been created yet, but I am creating the order during this step anyway, so the order doesn't have an ID yet. So this is my problem.
Here is the error I recieve:
ActiveRecord::RecordNotFound in OrdersController#create
Couldn't find Product with ID=1 for Order with ID=
Rails.root: /BillingSystem
Application Trace | Framework Trace | Full Trace
app/controllers/orders_controller.rb:10:in `new'
app/controllers/orders_controller.rb:10:in `create'
Request
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"jE2wdERoxE7PKwBhN60KAfguxwAq8qdW4wbru51SMFg=",
"order"=>{"client_id"=>"1",
"products_attributes"=>{"1368396234677"=>{"id"=>"1",
"_destroy"=>"false"}}},
"commit"=>"Create Order"}
Show session dump
Show env dump
Response
Headers:
None
New Order View:
<% if current_user %>
<div id="dashboard">
<div id="logo"></div>
<table id="go_back_link_container">
<tr>
<td>
<div class="go_back_link">
<%= link_to "<- Go Back", "/orders/view" %>
</div>
</td>
<td>
<div id="user_display">
Logged in as <%= current_user.email %>.
<%= link_to "Log out", log_out_path %>
</div>
</td>
</tr>
</table>
<%= form_for #order, method: :post do |f| %>
<% if #order.errors.any? %>
<div class="error_messages">
<% for message in #order.errors.full_messages %>
* <%= message %> <br>
<% end %>
</div>
<% end %>
<p>
<%= f.label 'Select The Client' %><br />
<%= select :order, :client_id, Client.all().collect { |c| [ (c.firstname + " " + c.surname), c.id ] } %>
</p>
<%= f.fields_for :products do |pf| %>
<% #render 'product_fields', f: builder %>
<% end %>
<%= link_to_add_fields "Add Product", f, :products %>
<p class="button"><%= f.submit %></p>
<% end %>
<% flash.each do |name, msg| %>
<%= content_tag :div, "* " + msg, :id => "flash_#{name}" %><br />
<% end %>
<div id="copyright-notice"><div id="copyright_border">Copyright © Conner McCabe, all rights reserved.</div></div>
</div>
<% else %>
<script type="text/javascript">
window.location="<%= root_url %>"
</script>
<% end %>
Order Model:
class Order < ActiveRecord::Base
has_many :orderedproducts
has_many :products, through: :orderedproducts
has_one :client
attr_accessible :client_id, :order_total, :delivery_date, :products, :products_attributes
accepts_nested_attributes_for :products, :allow_destroy => true
before_save :generate_total
def generate_total
self.order_total = self.products.map(&:product_price).sum
end
end
Orders Controller:
class OrdersController < ApplicationController
def view
#orders = Order.all
end
def new
#order = Order.new
end
def create
#order = Order.new(params[:order])
if #order.save
redirect_to '/orders/view', :notice => "Order Created!"
else
render "new"
end
end
end
Product Fields Partial:
<fieldset>
<%= f.select :id, Product.all().collect {|p| [ p.product_name, p.id ] } %>
<%= f.hidden_field :_destroy %>
<%= link_to "remove", '#', class: "remove_fields" %>
</fieldset>
Products Model:
class Product < ActiveRecord::Base
#This line makes these elements accessible outside of the class.
attr_accessible :product_name, :product_price, :product_quantity, :product_supplier
has_many :orderedproducts
has_many :orders, through: :orderedproducts
#These attributes ensure that the data entered for each element is valid and present.
validates_presence_of :product_name
validates_presence_of :product_price
validates_numericality_of :product_price
validates_presence_of :product_quantity
validates_numericality_of :product_quantity
validates_presence_of :product_supplier
end
Application Helper:
module ApplicationHelper
def link_to_add_fields(name, f, association)
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 + "_fields", f: builder)
end
link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
end
end
Ordered Products Model:
class Orderedproduct < ActiveRecord::Base
attr_accessible :order_id, :product_id, :quantity_ordered
belongs_to :order
belongs_to :product
end
I have listed every possible file that could contain an error, I know it's a bit excessive, but it's everything that is to do with it and better I include it than not at all.
I also followed this railscast guide: http://railscasts.com/episodes/196-nested-model-form-revised
To get to where I am, I edited it slightly so that it was suitable for my application.
Thanks in advance.
We had a similar issue on a project, except the relation was singular. The problem is that ActiveRecord is looking for an existing association; something like order.products.find(1). Since order is as new record this doesn't work.
You could create your own products_attributes= method and define the correct behaviour. But I think that you could just use nested attributes for the join model (Orderedproduct) instead of Product.
class Order
accepts_nested_attributes_for :orderedproducts
end
Then adjust the form fields appropriately. In the new form
f.fields_for :products do |pf| becomes f.fields_for :orderedproducts do |pf|
In the fields partial
<%= f.select :id, Product.all().collect {|p| [ p.product_name, p.id ] } %> becomes <%= f.select :product_id, Product.all().collect {|p| [ p.product_name, p.id ] } %>