Rails: can`t get data in callback - ruby-on-rails

I try to modify data before validation, but can't: by using self.param_name will be returned nil. My code:
class Category < ActiveRecord::Base
before_validation do
logger.info ":category_parameters before: #{self.category_parameters}"
self.category_parameters = self.category_parameters.split(/,/)
logger.info ":category_parameters after: #{self.category_parameters}"
end
end
Log file:
Started PATCH "/categories/9" for 127.0.0.1 at 2014-08-16 15:13:15 +0300
Processing by CategoriesController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"7RswlNAVs26W72IY6eL56xmACbVPCZQvl7MqDMueUbg=", "category"=>{"name"=>"Computers", "url"=>"computers", "category_parameters"=>"gergewrg,rewgwerg,wergwerg"}, "commit"=>"Save", "id"=>"9"}
Category Load (0.5ms) SELECT "categories".* FROM "categories" WHERE "categories"."id" = $1 LIMIT 1 [["id", 9]]
(0.3ms) BEGIN
:category_parameters before: []
:category_parameters after: [[]]
I use PostgreSQL and :category_parameters have type array in my schema:
create_table "categories", force: true do |t|
...
t.string "category_parameters", array: true
...
end
UPD:
Controller code:
class CategoriesController < ApplicationController
before_action :set_category, only: [:show, :edit, :update, :destroy]
...
def edit
end
...
def update
respond_to do |format|
if #category.update(category_params)
format.html { redirect_to #category, notice: 'Category was successfully updated.' }
format.json { render :show, status: :ok, location: #category }
else
format.html { render :edit }
format.json { render json: #category.errors, status: :unprocessable_entity }
end
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, :url, :category_parameters)
end
end

Reserved
After talking on chat, it appears the original attribute name of category_parameters was reserved by Rails for some reason (hence why the OP couldn't populate it).
The fix was to change the attribute name in the db to nested_params, the strong_params reference, and finally the attributes in the view. Here is the result:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"7RswlNAVs26W72IY6eL56xmACbVPCZQvl7MqDMueUbg=", "category"=>{"name"=>"Computers", "url"=>"computers", "nested_params"=>"{123,123,123},{123,gwehrewrh,235235}"}, "commit"=>"Save", "id"=>"9"}
:nested_params before: ["123", "123", "123", "123", "gwehrewrh", "235235"]
:nested_params after: [["123", "123", "123", "123", "gwehrewrh", "235235"]]

Your category_parameters are not being passed as an array, they're being passed as a string.
You could try this...
params[:category][:category_parameters] = params[:category][:category_parameters].split(/,/)
params.require(:category).permit(:name, :url, category_parameters: [])
Do NOT do a before_validation record, as it will mess up your record every time you re-save it for any reason.

Changing your strong parameters to this format should solve your problem.
def category_params
params[:category][:category_parameters] = params[:category][:category_parameters].split!(',')
params.require(:category).permit(:name, :url, :category_parameters => [])
end

Related

Rails collection_select not working for many to one relationship

I have 3 models in my rails project
Patient
class Patient < ActiveRecord::Base
has_many :temperatures
belongs_to :bloodgroup
def fullname
"#{firstName} #{surname}"
end
end
Temperature
class Temperature < ActiveRecord::Base
belongs_to :patient
end
BloodGroup
class BloodGroup < ActiveRecord::Base
has_many :patients
end
collection_select works for adding a patient to a temperature using the below snippet
<div class="field">
<%= f.label :patient_id %><br>
<%= collection_select(:temperature, :patient_id, Patient.all, :id, :fullname, prompt: true) %>
</div>
however the below snippet - to add BloodGroup to a patient doesnt work
<div class="field">
<%= f.label :bloodgroup_id %><br>
<%= collection_select(:Patient, :bloodgroup_id, BloodGroup.all, :id, :id, prompt: true) %>
</div>
I'm not sure if I've not set up the collection_select properly or if I've not set up the association properly as I'm new to rails so any help would be greatly appreciated.
Update
create_table "blood_groups", force: :cascade do |t|
t.string "bloodgroup"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "patients", force: :cascade do |t|
t.string "firstName"
t.string "surname"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "bloodgroup_id"
end
create_table "temperatures", force: :cascade do |t|
t.integer "patient_id"
t.decimal "temperature"
t.datetime "dt"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Controllers
Patient
class PatientsController < ApplicationController
before_action :set_patient, only: [:show, :edit, :update, :destroy]
# GET /patients
# GET /patients.json
def index
#patients = Patient.all
end
# GET /patients/1
# GET /patients/1.json
def show
end
# GET /patients/new
def new
#patient = Patient.new
end
# GET /patients/1/edit
def edit
end
# POST /patients
# POST /patients.json
def create
#patient = Patient.new(patient_params)
respond_to do |format|
if #patient.save
format.html { redirect_to #patient, notice: 'Patient was successfully created.' }
format.json { render :show, status: :created, location: #patient }
else
format.html { render :new }
format.json { render json: #patient.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /patients/1
# PATCH/PUT /patients/1.json
def update
respond_to do |format|
if #patient.update(patient_params)
format.html { redirect_to #patient, notice: 'Patient was successfully updated.' }
format.json { render :show, status: :ok, location: #patient }
else
format.html { render :edit }
format.json { render json: #patient.errors, status: :unprocessable_entity }
end
end
end
# DELETE /patients/1
# DELETE /patients/1.json
def destroy
#patient.destroy
respond_to do |format|
format.html { redirect_to patients_url, notice: 'Patient was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_patient
#patient = Patient.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def patient_params
params.require(:patient).permit(:firstName, :surname)
end
end
Temperature
class TemperaturesController < ApplicationController
before_action :set_temperature, only: [:show, :edit, :update, :destroy]
# GET /temperatures
# GET /temperatures.json
def index
#temperatures = Temperature.all
end
# GET /temperatures/1
# GET /temperatures/1.json
def show
end
# GET /temperatures/new
def new
#temperature = Temperature.new
end
# GET /temperatures/1/edit
def edit
end
# POST /temperatures
# POST /temperatures.json
def create
#temperature = Temperature.new(temperature_params)
respond_to do |format|
if #temperature.save
format.html { redirect_to #temperature, notice: 'Temperature was successfully created.' }
format.json { render :show, status: :created, location: #temperature }
else
format.html { render :new }
format.json { render json: #temperature.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /temperatures/1
# PATCH/PUT /temperatures/1.json
def update
respond_to do |format|
if #temperature.update(temperature_params)
format.html { redirect_to #temperature, notice: 'Temperature was successfully updated.' }
format.json { render :show, status: :ok, location: #temperature }
else
format.html { render :edit }
format.json { render json: #temperature.errors, status: :unprocessable_entity }
end
end
end
# DELETE /temperatures/1
# DELETE /temperatures/1.json
def destroy
#temperature.destroy
respond_to do |format|
format.html { redirect_to temperatures_url, notice: 'Temperature was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_temperature
#temperature = Temperature.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def temperature_params
params.require(:temperature).permit(:patient_id, :temperature, :dt)
end
end
Blood Group
class BloodGroupsController < ApplicationController
before_action :set_blood_group, only: [:show, :edit, :update, :destroy]
# GET /blood_groups
# GET /blood_groups.json
def index
#blood_groups = BloodGroup.all
end
# GET /blood_groups/1
# GET /blood_groups/1.json
def show
end
# GET /blood_groups/new
def new
#blood_group = BloodGroup.new
end
# GET /blood_groups/1/edit
def edit
end
# POST /blood_groups
# POST /blood_groups.json
def create
#blood_group = BloodGroup.new(blood_group_params)
respond_to do |format|
if #blood_group.save
format.html { redirect_to #blood_group, notice: 'Blood group was successfully created.' }
format.json { render :show, status: :created, location: #blood_group }
else
format.html { render :new }
format.json { render json: #blood_group.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /blood_groups/1
# PATCH/PUT /blood_groups/1.json
def update
respond_to do |format|
if #blood_group.update(blood_group_params)
format.html { redirect_to #blood_group, notice: 'Blood group was successfully updated.' }
format.json { render :show, status: :ok, location: #blood_group }
else
format.html { render :edit }
format.json { render json: #blood_group.errors, status: :unprocessable_entity }
end
end
end
# DELETE /blood_groups/1
# DELETE /blood_groups/1.json
def destroy
#blood_group.destroy
respond_to do |format|
format.html { redirect_to blood_groups_url, notice: 'Blood group was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_blood_group
#blood_group = BloodGroup.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def blood_group_params
params.require(:blood_group).permit(:bloodgroup)
end
end
Log
Started POST "/patients" for 81.144.132.166 at 2017-08-25 12:10:08 +0000
Cannot render console from 81.144.132.166! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by PatientsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"KnDRbEAL/f1DK6Gc9TcFNu0rRH5tahnGZiwLcx6FqH5iP7L5BsMOjH0yKR3630g/4ejCLUzilloj1rkO86wkAA==", "patient"=>{"firstName"=>"Test", "surname"=>"test", "bloodgroup_id"=>"7"}, "commit"=>"Create Patient"}
Unpermitted parameter: bloodgroup_id
(0.1ms) begin transaction
SQL (41.7ms) INSERT INTO "patients" ("firstName", "surname", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["firstName", "Test"], ["surname", "test"], ["created_at", "2017-08-25 12:10:08.979026"], ["updated_at", "2017-08-25 12:10:08.979026"]]
(10.1ms) commit transaction
Redirected to https://healthy-rails-conorsmalley.c9users.io/patients/6
Completed 302 Found in 125ms (ActiveRecord: 51.8ms)
Started GET "/patients/6" for 81.144.132.166 at 2017-08-25 12:10:09 +0000
Cannot render console from 81.144.132.166! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by PatientsController#show as HTML
Parameters: {"id"=>"6"}
Patient Load (0.4ms) SELECT "patients".* FROM "patients" WHERE "patients"."id" = ? LIMIT 1 [["id", 6]]
Rendered patients/show.html.erb within layouts/application (0.4ms)
Completed 200 OK in 32ms (Views: 29.2ms | ActiveRecord: 0.4ms)
When I press submit to add bloodgroup to a patient i get the following logs
Started POST "/patients" for 81.144.132.166 at 2017-08-25 12:40:03 +0000
Cannot render console from 81.144.132.166! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by PatientsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Mkw0f3Yu6jXqzc+wBgbLxDDbW8GJY41ARBme0St/JIV6A1fqMOYZRNTURzEJ7obNPBjdkqjrAtwB4yysxlao+w==", "patient"=>{"firstName"=>"asdf", "surname"=>"ghjk"}, "Patient"=>{"bloodgroup_id"=>"3"}, "commit"=>"Create Patient"}
(0.1ms) begin transaction
SQL (0.4ms) INSERT INTO "patients" ("firstName", "surname", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["firstName", "asdf"], ["surname", "ghjk"], ["created_at", "2017-08-25 12:40:03.623102"], ["updated_at", "2017-08-25 12:40:03.623102"]]
(9.6ms) commit transaction
Redirected to https://healthy-rails-conorsmalley.c9users.io/patients/8
Completed 302 Found in 15ms (ActiveRecord: 10.2ms)
Started GET "/patients/8" for 81.144.132.166 at 2017-08-25 12:40:03 +0000
Cannot render console from 81.144.132.166! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by PatientsController#show as HTML
Parameters: {"id"=>"8"}
Patient Load (0.2ms) SELECT "patients".* FROM "patients" WHERE "patients"."id" = ? LIMIT 1 [["id", 8]]
Rendered patients/show.html.erb within layouts/application (0.5ms)
Completed 200 OK in 23ms (Views: 21.4ms | ActiveRecord: 0.2ms)
You have a few mistakes in your code which would be causing the problem
1)
belongs_to :bloodgroup
should be
belongs_to :blood_group
as the model name is BloodGroup and the snake_case string of BloodGroup is blood_group not bloodgroup
2)
<%= collection_select(:Patient, :bloodgroup_id, BloodGroup.all, :id, :id, prompt: true) %>
should be
<%= collection_select(:patient, :blood_group_id, BloodGroup.all, :id, :id, prompt: true) %>
Update:
My db schema has the column bloodgroup_id not blood_group_id
Then you need to specify your association with a custom foreign key else by default Rails will look for blood_group_id
class Patient < ActiveRecord::Base
has_many :temperatures
belongs_to :blood_group, foreign_key: "bloodgroup_id"
def fullname
"#{firstName} #{surname}"
end
end
Unpermitted parameter: bloodgroup_id
You need to permit bloodgroup_id in the patient_params in order to save it to the DB
def patient_params
params.require(:patient).permit(:firstName, :surname, bloodgroup_id)
end

Creating multiple model objects if they do not exist?

Models:
Store - id, name
Source - id, name
User - id, name
StoreUser - store_id, user_id
StoreSourceDeduction - store_id, source_id
StoreSourceRevenue - store_id, source_id
Transaction - store_id, source_id, deduction:boolean, user_id, amount, submitted_at, created_at, updated_at
(StoreSource split into Deduction and Revenue to make use of the Nested Form Fields gem)
I have routes set up as such: /transactions/:store_id/:date
When navigating to /transactions/1/08112017, if there are no records in the Transaction model for that date, I need to create a row in the Transaction table for each of the StoreSourceDeduction and StoreSourceRevenue models for store 1 and display a form for each record's amount field.
From the Transaction controller, how do I create multiple new Transaction objects while automatically populating the store_id, user_id, deduction, source_id fields, and how do I recall them to allow edits?
EDIT
I believe I solved the creation of objects:
class TransactionsController < ApplicationController
before_action :set_transactions, only: [:show, :edit, :update, :destroy]
before_action :set_store, only: [:store]
before_action :get_sources, only: [:show]
before_action :check_date, only: [:show]
# GET /transactions
# GET /transactions.json
def index
#transactions = Transaction.all
end
def store
end
# GET /transactions/1
# GET /transactions/1.json
def show
if is_today?
if #transactions.count == 0
#deductions.each do |d|
row = Transaction.new(:store_id => params[:store_id],
:source_id => d.source_id,
:user_id => current_user.id,
:deduction => true)
row.save
end
#revenues.each do |r|
row = Transaction.new(:store_id => params[:store_id],
:source_id => r.source_id,
:user_id => current_user.id,
:deduction => false)
row.save
end
end
end
end
# GET /transactions/1/edit
def edit
end
# POST /transactions
# POST /transactions.json
def create
end
# PATCH/PUT /transactions/1
# PATCH/PUT /transactions/1.json
def update
respond_to do |format|
if #transaction.update(transaction_params)
format.html { redirect_to #transaction, notice: 'Transaction was successfully updated.' }
format.json { render :show, status: :ok, location: #transaction }
else
format.html { render :edit }
format.json { render json: #transaction.errors, status: :unprocessable_entity }
end
end
end
# DELETE /transactions/1
# DELETE /transactions/1.json
def destroy
#transaction.destroy
respond_to do |format|
format.html { redirect_to transactions_url, notice: 'Transaction was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_transactions
#transactions = Transaction.where("store_id = :store_id AND date = :date",
{store_id: params[:store_id], date: params[:transaction_date]})
end
def set_store
#transactions = Transaction.where("store_id = :store_id",
{store_id: params[:store_id]})
end
def is_today?
date = params[:transaction_date]
today = Date.today().strftime('%m%d%Y')
date == today
end
def check_date
#date = Transaction.where("date = :date", {date: params[:date]})
end
def get_sources
#deductions = StoreSourceDeduction.where("store_id = :store_id", {store_id: params[:store_id]})
#revenues = StoreSourceRevenue.where("store_id = :store_id", {store_id: params[:store_id]})
end
# Never trust parameters from the scary internet, only allow the white list through.
def transaction_params
params.require(:transaction).permit(:store_id, :user_id, :source_id, :amount, :submitted_at)
end
end

Rails 5 - has_many through and nested attributes forms

I have two models Request and TableLocation with both having a has_many through relationship joined by RequestLocation table.
I am trying to create a nested form and the table_location data is not being saved to the database.
As you can see the table_locations"=>["1"] parameter is being passed to the create action but not being saved.
Appreciate any help.
Console Output
Started POST "/requests" for 127.0.0.1 at 2017-06-07 10:35:26 -0400
Processing by RequestsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"mHV/xbmdfHmCAsi16KXlW+0bWVSkEo9SRVchdyPpL60o3m3SuKEt4nuUT4PJNEyCsWq3Nj4IWiCMlDbhiPewdA==", "request"=>{"concierge_name"=>"Alex", "concierge_number"=>"954-123-4567", "concierge_email"=>"alex#email.com", "client_name"=>"Adam", "client_number"=>"954-765-4321", "client_email"=>"adam#email.com", "hotel_employee"=>"0", "concierge_service"=>"0", "vip_promoter"=>"0", "arriving_with_client"=>"1", "client_alone"=>"0", "males"=>"", "females"=>"1", "table_minimum"=>"1000", "arrival_time(1i)"=>"2017", "arrival_time(2i)"=>"6", "arrival_time(3i)"=>"7", "arrival_time(4i)"=>"14", "arrival_time(5i)"=>"35", "table_locations"=>["1"], "comments"=>""}, "commit"=>"Submit"}
(0.1ms) BEGIN
SQL (0.4ms) INSERT INTO "requests" ("concierge_name", "concierge_number", "concierge_email", "client_name", "client_number", "client_email", "arriving_with_client", "people", "females", "table_minimum", "arrival_time", "comments", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) RETURNING "id" [["concierge_name", "Alex"], ["concierge_number", "954-123-4567"], ["concierge_email", "alex#email.com"], ["client_name", "Adam"], ["client_number", "954-765-4321"], ["client_email", "adam#email.com"], ["arriving_with_client", "t"], ["people", 1], ["females", 1], ["table_minimum", 1000], ["arrival_time", "2017-06-07 14:35:00"], ["comments", ""], ["created_at", "2017-06-07 14:35:26.658718"], ["updated_at", "2017-06-07 14:35:26.658718"]]
(0.3ms) COMMIT
Redirected to http://localhost:3000/thanks
Completed 302 Found in 8ms (ActiveRecord: 0.8ms)
app/models/request.rb
class Request < ApplicationRecord
has_many :request_locations
has_many :table_locations, through: :request_locations
end
app/models/table_locations.rb
class TableLocation < ApplicationRecord
has_many :request_locations
has_many :requests, through: :request_locations
end
app/models/request_location.rb
class RequestLocation < ApplicationRecord
belongs_to :request
belongs_to :table_location
end
app/controllers/requests_controller.rb
class RequestsController < ApplicationController
before_action :set_request, only: [:show,
:edit,
:update,
:destroy]
before_action :authenticate_admin!, except: [:index,
:new,
:create]
def index
redirect_to root_path unless admin_signed_in?
#requests = Request.search(params[:term], params[:filter], params[:page])
end
def show
end
def new
#request = Request.new
end
def edit
end
def create
#request = Request.new(request_params)
#request.people = (#request.males || 0) + (#request.females || 0)
respond_to do |format|
if #request.save
format.html { redirect_to thanks_path, notice: 'Request was successfully created.' }
format.json { render :show, status: :created, location: #request }
else
format.html { render :new }
format.json { render json: #request.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #request.update(request_params)
format.html { redirect_to #request, notice: 'Request was successfully updated.' }
format.json { render :show, status: :ok, location: #request }
else
format.html { render :edit }
format.json { render json: #request.errors, status: :unprocessable_entity }
end
end
end
def destroy
#request.destroy
respond_to do |format|
format.html { redirect_to requests_url, notice: 'Request was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_request
#request = Request.find(params[:id])
end
def request_params
params.require(:request).permit(:concierge_name,
:concierge_number,
:concierge_email,
:client_name,
:client_number,
:client_email,
:hotel_employee,
:concierge_service,
:vip_promoter,
:arriving_with_client,
:client_alone,
:people,
:males,
:females,
:table_minimum,
:arrival_time,
:comments,
table_locations: [:id]
)
end
end
app/views/requests/_form.html.erb
...
<% TableLocation.all.each do |t| %>
<%= check_box_tag "request[table_locations][]", t.id, #request.table_locations.include?(t.id) %>
<%= t.location %>
<br />
<% end %>
...
Explanations:
Your request_params permits table_locations: [:id], but this only permits the following format:
Parameters: {"utf8"=>"✓", ... "request"=>{"table_locations"=>{"id"=>"1"}, "comments"=>""}, "commit"=>"Submit"}
but yours is showing to be:
Parameters: {"utf8"=>"✓", ... "request"=>{"table_locations"=>["1"], "comments"=>""}, "commit"=>"Submit"}
therefore, try this: puts request_params inside the create method, and you'll notice that it doesn't have table_locations values (even though you thought it's there, but it's not), because it is not "properly" whitelisted in your strong params request_params.
To be able to associate multiple TableLocations objects to a newly built Request object, the format should be something like below
request = Request.new(table_location_ids: [1,2,3,4,5])
but from your implementation, you're doing it like this (which won't work):
request = Request.new(table_locations: [1,2,3,4,5])
# => this will raise an error:
# ActiveRecord::AssociationTypeMismatch: TableLocation expected, got Fixnum
# however yours didn't raise an error, because it was not whitelisted in the request_params in the first place
Solution:
requests_controller.rb
def request_params
params.require(:request).permit(..., table_location_ids: [])
end
_form.html.erb
<% TableLocation.all.each do |t| %>
<%= check_box_tag "request[table_location_ids][]", t.id %>
<%= t.location %>
<br />
<% end %>
Recommendation:
just in case you don't know yet that you can do the following in this way, I'll be refactoring your code to look something like this:
requests_controller.rb
def create
#table_locations = TableLocation.all
end
_form.html.erb
<%= form_for #request do |f| %>
<% #table_locations.each do |table_location| %>
<%= f.check_box :table_location_ids, multiple: true, table_location.id %>
<% end %>
<% end %>

HABTM avoid dupe in join table

I've read this question : has_and_belongs_to_many, avoiding dupes in the join table but I wasn't able to fix my issue;
I've got a class TypeProduit which has many ExtensionFichier :
class TypeProduit < ActiveRecord::Base
has_and_belongs_to_many :extension_fichiers
end
and:
class ExtensionFichier < ActiveRecord::Base
has_and_belongs_to_many :type_produits
end
They are related thanks to
class LinkExtensionFichiers < ActiveRecord::Migration
def change
create_join_table :extension_fichiers, :type_produits
end
end
Edit:
Here's the [correct] Controler:
class TypeProduitsController < ApplicationController
before_action :set_type_produit, only: [:show, :edit, :update, :destroy]
# GET /type_produits
# GET /type_produits.json
def index
#type_produits = TypeProduit.all
end
# GET /type_produits/1
# GET /type_produits/1.json
def show
end
# GET /type_produits/new
def new
#type_produit = TypeProduit.new
end
# GET /type_produits/1/edit
def edit
end
# POST /type_produits
# POST /type_produits.json
def create
#type_produit = TypeProduit.new(type_produit_params)
puts "------extension_fichier_params------"
puts extension_fichier_params
##type_produit.extension_fichier_ids = extension_fichier_params
#type_produit.extension_fichiers << ExtensionFichier.find_by(:id => extension_fichier_params)
respond_to do |format|
if #type_produit.save
format.html { redirect_to #type_produit, notice: 'Type produit was successfully created.' }
format.json { render :show, status: :created, location: #type_produit }
else
format.html { render :new }
format.json { render json: #type_produit.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /type_produits/1
# PATCH/PUT /type_produits/1.json
def update
respond_to do |format|
if #type_produit.update(type_produit_params)
format.html { redirect_to #type_produit, notice: 'Type produit was successfully updated.' }
format.json { render :show, status: :ok, location: #type_produit }
else
format.html { render :edit }
format.json { render json: #type_produit.errors, status: :unprocessable_entity }
end
end
end
# DELETE /type_produits/1
# DELETE /type_produits/1.json
def destroy
#type_produit.destroy
respond_to do |format|
format.html { redirect_to type_produits_url, notice: 'Type produit was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_type_produit
#type_produit = TypeProduit.find(params[:id])
end
def extension_fichier_params
#type_produit.extension_fichier_ids
end
# Never trust parameters from the scary internet, only allow the white list through.
def type_produit_params
params.require(:type_produit).permit(:nom, :extension_fichier_ids => [])
end
end
And here's the Rails server output:
Started POST "/type_produits" for 127.0.0.1 at 2015-01-27 17:55:15 +0100
Processing by TypeProduitsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"4LBhwTvGjXOq+3qzO+loGUV/oBCYjW66aVDeE1Zj/AM=", "type_produit"=>{"nom"=>"qsdfqsdf", "extension_fichier_ids"=>["1", "2", ""]}, "commit"=>"Create Type produit"}
ExtensionFichier Load (0.4ms) SELECT "extension_fichiers".* FROM "extension_fichiers" WHERE "extension_fichiers"."id" IN (1, 2)
------extension_fichier_params------
1
2
ExtensionFichier Load (0.2ms) SELECT "extension_fichiers".* FROM "extension_fichiers" WHERE "extension_fichiers"."id" IN (1, 2) LIMIT 1
(0.1ms) begin transaction
TypeProduit Exists (0.1ms) SELECT 1 AS one FROM "type_produits" WHERE "type_produits"."nom" = 'qsdfqsdf' LIMIT 1
SQL (0.4ms) INSERT INTO "type_produits" ("created_at", "nom", "updated_at") VALUES (?, ?, ?) [["created_at", "2015-01-27 16:55:15.923132"], ["nom", "qsdfqsdf"], ["updated_at", "2015-01-27 16:55:15.923132"]]
SQL (0.1ms) INSERT INTO "extension_fichiers_type_produits" ("extension_fichier_id", "type_produit_id") VALUES (?, ?) [["extension_fichier_id", 1], ["type_produit_id", 8]]
SQL (0.1ms) INSERT INTO "extension_fichiers_type_produits" ("extension_fichier_id", "type_produit_id") VALUES (?, ?) [["extension_fichier_id", 2], ["type_produit_id", 8]]
SQL (0.0ms) INSERT INTO "extension_fichiers_type_produits" ("extension_fichier_id", "type_produit_id") VALUES (?, ?) [["extension_fichier_id", 1], ["type_produit_id", 8]]
(0.5ms) commit transaction
Why is rails making 3 insert while it just has 2 objects
There's a very obvious error in your controller code. Let's go step by step through your create action.
You create a new TypeProduit from type_produit_params which includes :nom and :extension_fichier_ids. You then output the value of extension_fichier_params which itself is #type_produit.extension_fichier_ids and you get "1\n2\n" so your #type_produit already has both of those extension_fichiers attached to it.
You then append ExtensionFichier.find_by(:id => extension_fichier_params) to #type_produit.extension_fichier_ids. Now a find_by is basically a where().first so it gets only the first one, ie. id=1. So now in your #type_produit.extension_fichiers array you have ids 1, 2 and 1 again. Ie. three objects.
Then you save the TypeProduit and all three of those associations are saved with the three INSERTs.
And yeah, the overall solution to this (other than fixing your unnecessary << code or your model-or-view that's adding 1 and 2) is to add the -> { uniq } scope to your habtm association(s) as per https://stackoverflow.com/a/20226630/152786
Have you tried the solution suggested by using-uniq-in-a-has-and-belongs-to-many-relationship-in-rails-4 (and maybe also adding an index as suggested in has-and-belongs-to-many-avoiding-dupes-in-the-join-table ?)

Rails 4: issues with strong parameters and passing data

Good day, community.
First of all, I'm a newbie in Rails. I did some thing with it in College 4 years ago, and now I decided to get back on it. Lots of things changed in version 4.
Anyway, I am experiencing issues with strong parameters. Here's what I have:
I'm using Ruby 2.1, Rails 4.1.
I am trying to create a form for a hockey match with parameters (id, team_a, team_b, arena, date, score_a, score_b). team is a table (id, name) and arena is a table (id, name).
When I pass the parameters from form to the controller, the json parameters seem to be okay. But, when it is converted into match_params it is missing some values from parameters from other table. For example, I am passing arena_id: 12, but it shows arena_id: as blank.
I've spent over 5 days on this thing. Any help appreciated.
Some of the code is bellow. Let me know if you need me to provide more information...
migration data
class CreateMatches < ActiveRecord::Migration
def change
create_table :matches do |t|
t.references :team_a, default: 1 # unknown
t.references :team_b, default: 1 # unknown
t.references :arena, default: 1 # unknown
t.datetime :date
t.integer :score_a
t.integer :score_b
t.timestamps
end
add_index :matches, :team_a_id
add_index :matches, :team_b_id
add_index :matches, :arena_id
end
end
class CreateTeams < ActiveRecord::Migration
def change
create_table :teams do |t|
t.string :name, null: false
t.timestamps
end
end
end
class CreateArena < ActiveRecord::Migration
def change
create_table :arena do |t|
t.string :name, null: false
t.timestamps
end
end
end
match.rb (model)
class Match < ActiveRecord::Base
belongs_to :team_a, :class_name => 'Team'
belongs_to :team_b, :class_name => 'Team'
belongs_to :arena
end
team.rb (model)
class Team < ActiveRecord::Base
has_many :matches
accepts_nested_attributes_for :matches
end
arena.rb (model)
class Arena < ActiveRecord::Base
has_many :matches
accepts_nested_attributes_for :matches
end
matches_controller.rb
class MatchesController < ApplicationController
before_action :set_match, only: [:show, :edit, :score, :update, :destroy]
include ActionView::Helpers::DateHelper
def index
# some code
end
def show
# some code
end
def new
#match = Match.new
#teams = Team.all.order("name ASC")
#arenas = Arena.all.order("name ASC")
end
# GET /matches/1/edit
def edit
# some code
end
def create
puts YAML::dump(match_params) # Checking passed params. Output is bellow
#match = Match.new(match_params)
respond_to do |format|
if #match.save
format.html { redirect_to #match, notice: 'Match was successfully created.' }
format.json { render action: 'show', status: :created, location: #match }
else
format.html { render action: 'new' }
format.json { render json: #match.errors, status: :unprocessable_entity }
end
end
end
def update
end
def destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_match
#match = Match.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def match_params
params.require(:match).permit(:date, :score_a, :score_b, team_a_id: [:id, :name], team_b_id: [:id, :name], arena_id: [:id, :name])
end
public
end
teams_controller.rb
class TeamsController < ApplicationController
before_action :set_team, only: [:show, :edit, :update, :destroy]
layout :false
def index
#teams = Team.all
end
def show
end
def new
#team = Team.new
end
def edit
end
def create
#team = Team.new(team_params)
respond_to do |format|
if #team.save
format.json { render action: 'show', status: :created, location: #team }
format.html { redirect_to #team, notice: 'Team was successfully created.' }
else
format.html { render action: 'new' }
format.json { render json: #team.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #team.update(team_params)
format.json { head :no_content }
format.html { redirect_to #team, notice: 'Team was successfully updated.' }
else
format.html { render action: 'edit' }
format.json { render json: #team.errors, status: :unprocessable_entity }
end
end
end
def destroy
#team.destroy
respond_to do |format|
format.html { redirect_to teams_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_team
#team = Team.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def team_params
params.require(:team).permit(:name)
end
end
arenas_controller.rb
class ArenasController < ApplicationController
before_action :set_arena, only: [:show, :edit, :update, :destroy]
layout false
def index
#arena = Arena.all
end
def show
end
def new
#arena = Arena.new
end
def edit
end
def create
#arena = Arena.new(arena_params)
respond_to do |format|
if #arena.save
format.json { render action: 'show', status: :created, location: #arena }
format.html { redirect_to #arena, notice: 'Arena was successfully created.' }
else
format.html { render action: 'new' }
format.json { render json: #arena.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #arena.update(arena_params)
format.json { head :no_content }
format.html { redirect_to #arena, notice: 'Arena was successfully updated.' }
else
format.html { render action: 'edit' }
format.json { render json: #arena.errors, status: :unprocessable_entity }
end
end
end
def destroy
#arena.destroy
respond_to do |format|
format.html { redirect_to arenas_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_arena
#arena = Arena.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def arena_params
params.require(:arena).permit(:name)
end
end
matches/_match.html.erb
<%= form_for(#match, html: {role: 'form', class: 'form-horizontal'}) do |f| %>
<% if #match.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#match.errors.count, "error") %> prohibited this match from being saved:</h2>
<ul>
<% #match.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.label 'Home Team' %>
<%= f.collection_select :team_a_id, #teams, :id, :name, {prompt: true}, {class: ''} %>
<%= f.label 'Visitor Team' %>
<%= f.collection_select :team_b_id, #teams, :id, :name, {prompt: true}, {class: ''} %>
<%= f.label 'Arena' %>
<%= f.collection_select :arena_id, #arenas, :id, :name, {prompt: true}, {class: ''} %>
<%= f.label 'Date' %>
<%= f.datetime_select :date, class: 'form-control' %>
<%= f.submit value: 'Submit' %>
<% end %>
And here's what I am getting in console after dumping data:
Started POST "/matches" for 127.0.0.1 at 2014-05-06 18:24:20 -0700
Processing by MatchesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"0RJjnpczVkp2unG9VITyHYC89ThgELn5kVE2wYRymBU=", "match"=>{"team_a_id"=>"24", "team_b_id"=>"27", "arena_id"=>"21", "date(1i)"=>"2014", "date(2i)"=>"5", "date(3i)"=>"6", "date(4i)"=>"18", "date(5i)"=>"24"}, "commit"=>"Update"}
User Load (0.5ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 ORDER BY `users`.`id` ASC LIMIT 1
--- !ruby/hash:ActionController::Parameters
date(1i): '2014'
date(2i): '5'
date(3i): '6'
date(4i): '18'
date(5i): '24'
team_a_id:
team_b_id:
arena_id:
(0.2ms) BEGIN
SQL (1.5ms) INSERT INTO `matches` (`created_at`, `date`, `arena_id`, `team_a_id`, `team_b_id`, `updated_at`) VALUES ('2014-05-07 01:24:20', '2014-05-07 01:24:00', NULL, NULL, NULL, '2014-05-07 01:24:20')
(0.2ms) COMMIT
Redirected to http://localhost:3000/matches/90
Completed 302 Found in 13ms (ActiveRecord: 2.4ms)
Take a look at your match_params, and compare it to what parameters are being passed to your controller from your form.
def match_params
params.require(:match).permit(:date, :score_a, :score_b, team_a_id: [:id, :name], team_b_id: [:id, :name], area_id: [:id, :name])
end
Parameters: {"utf8"=>"✓", "authenticity_token"=>"0RJjnpczVkp2unG9VITyHYC89ThgELn5kVE2wYRymBU=", "match"=>{"team_a_id"=>"24", "team_b_id"=>"27", "arena_id"=>"21", "date(1i)"=>"2014", "date(2i)"=>"5", "date(3i)"=>"6", "date(4i)"=>"18", "date(5i)"=>"24"}, "commit"=>"Update"}
You're permitting your arena_id in match_params as an array called area_id, with elements :id and :name. However, it's being passed from your form as just arena_id. You should change your match_params function to:
def match_params
params.require(:match).permit(:date, :score_a, :score_b, :team_a_id, :team_b_id, :arena_id)
end
Note that I've also changed :team_a_id and :team_b_id to be consistent with what's being passed in your parameters too, although it doesn't look like you're passing :score_a or :score_b. You should check out strong parameters in the rails guides for more information.
Okay, I found my mistake. (Thanks to JKen13579)
I have put params in the wrong place.
It should be something like that:
def match_params
params.require(:match).permit(:date, :score_a, :score_b, :team_a_id, :team_b_id , :arena_id)
end
def team_params
params.require(:team).permit(:name, matches_params:[:id, :match_id, :name])
end
def arena_params
params.require(:arena).permit(:name, matches_params:[:id, :match_id, :name])
end
It fixed the issue.
everyting but name will be removed when you call this:
params.require(:arena).permit(:name)

Resources