Rails turbo stream destroy action handling inexistent parameter - ruby-on-rails

The following execution upon hitting a button <%= button_to fa_icon('bomb', class: 'fa-lg'), importattribute, class: 'button alert small', method: :delete %> in Rails 7
Started DELETE "/importattributes/1" for ...
Processing by ImportattributesController#destroy as TURBO_STREAM
Parameters: {"authenticity_token"=>"[FILTERED]", "id"=>"1"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 11], ["LIMIT", 1]]
Importattribute Load (0.3ms) SELECT "importattributes".* FROM "importattributes" WHERE "importattributes"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ app/controllers/importattributes_controller.rb:53:in `set_importattribute'
TRANSACTION (0.2ms) BEGIN
↳ app/controllers/importattributes_controller.rb:47:in `destroy'
TRANSACTION (0.2ms) ROLLBACK
is hitting the error
ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column importattributes.importattribute_id does not exist
LINE 1: ...importattributes".* FROM "importattributes" WHERE "importatt...
upon
def destroy
authorize #importattribute
#supplierimport = Supplierimport.find(#importattribute.supplierimport_id)
#importattribute.destroy
redirect_to supplierimport_path(#supplierimport), notice: "Import attribute was successfully destroyed."
end
with the standard private Rails method
def set_importattribute
#importattribute = Importattribute.find(params[:id])
end
Why is Rails looking up in PG importattribute_id instead of id ?
This is also occuring in the test suite & I am quite puzzled by it.

Related

Why i can't create object with params?

I’ve been suffering from a problem for several days.
I can not create an object using the parameters from the form.
My form:
<%= form_with(model: #battle, local: true) do |form| %>
<div class="field">
<%= form.collection_select(:team_id, #teams, :id, :name) %>
</div>
<%= form.submit 'Submit'%>
<% end %>
In this form i want to choose only 1 team.
controller:
def create
#battle = Battle.new
#battle.user_id = current_user.id
#battle.team_ids = params[:team_id]
if #battle.save
redirect_to root_path, notice: "Battle has been created"
else
render :new
end
end
def battle_params
params.require(:battle).permit(:team_id)
end
And when used, this form creates an object without reference to the team.
logs:
Processing by BattlesController#create as HTML
Parameters: {"authenticity_token"=>"0wWoFrQXYkEsXsMRgGyKi5Mde7WndhI6zYWY4KvhQlgdcCAaCZqH1z1z9dK0x91iqOPw/Jsb2T6Q+EVtGz4VsA==", "battle"=>{"team_id"=>"14"}, "commit"=>"Submit"}
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ app/controllers/battles_controller.rb:14:in `create'
Team Load (0.4ms) SELECT "teams".* FROM "teams" WHERE 1=0
↳ app/controllers/battles_controller.rb:15:in `create'
(0.4ms) BEGIN
↳ app/controllers/battles_controller.rb:16:in `create'
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ app/controllers/battles_controller.rb:16:in `create'
Battle Create (3.8ms) INSERT INTO "battles" ("user_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["user_id", 1], ["created_at", "2020-06-19 10:28:28.036147"], ["updated_at", "2020-06-19 10:28:28.036147"]]
If I try to create a battle with two teams, but it will be created with only one (without using parameters):
#battle.team_ids = [params[:team_id], 14]
Although, a battle is created without problems in the console:
battle = Battle.new
battle.team_ids = [13, 14]
I don’t understand what the problem may be.
Instead of params[:team_id] try using battle_params[:team_id] instead:
#battle.team_ids = battle_params[:team_id]
otherwise you'd need to call params[:battle][:team_id] which is not the Rails way as it isn't as secure, but it would still work.

Rails. Debugging a Uniqueness validation with scope that fails when object is updated

I have a model Prediction for which its instances have references to other models Participant and Match through the attributes :participant_id :match_id.
The idea is that a #participant can have only one #prediction per #match (every #participant is allowed to give maximum one prediction per #match).
So I've included the following validation in my Prediction model
validates :match_id, uniqueness: { scope: [:participant_id] }
The validation works OK to disallow #prediction objects to be created when there's already a #prediciton for the same #match and #participant. The problem is that it does, as well, prevent updating an object even though I'm not changing those assigned ids of the original object. Plus, it's letting me update only one instance of Prediction, all other's through an error.
What's puzzling is that it was working fine but for some reason, I've been unable to track, it stopped working and I have run out of ideas on how to debug. Could you provide any ideas? Below what I've checked/tried:
The relevant params of a failed request:
"prediction"=><ActionController::Parameters {"participant_id"=>"1", "match_id"=>"2", "local_score"=>"2", "visitant_score"=>"0"}
The error I'm getting:
#details={:match_id=>[{:error=>:taken, :value=>2}]},
#messages={:match_id=>["has already been taken"]}>
There are plenty predictions in the database with match_id: 2. However, this is happening despite having no other #prediction in the database with both match_id: 2 and participant_id: 1 except, of course, for the instance that throws this error while updating.
Funnily enough, if I try the same operation in the console (as below) it successfully updates.
#prediction = Prediction.find_by(participant_id: 1, match_id: 2)
#prediction.update_attributes(local_score: 8, visitant_score: 8)
Suggesting the problem is in the controller action:
app/controllers/predicitons_controller.rb
def update
respond_to do |format|
if #prediction.update(prediction_params)
format.json { render :show, status: :ok, location: #prediction }
format.js
else
#errorMessages = []
#prediction.errors.full_messages.each do |message|
#errorMessages << message
end
format.js {render template: 'predictions/update_error'}
end
end
end
I see no problem there. Additionally, it seems that the only request sent to the controller that updates successfully is:
"prediction"=><ActionController::Parameters {"participant_id"=>"1", "match_id"=>"1", "local_score"=>"1", "visitant_score"=>"1"}
And if I do these others, for example, it doesn't:
"prediction"=><ActionController::Parameters {"participant_id"=>"2", "match_id"=>"1", "local_score"=>"0", "visitant_score"=>"0"}
"prediction"=><ActionController::Parameters {"participant_id"=>"2", "match_id"=>"2", "local_score"=>"9", "visitant_score"=>"9"}
"prediction"=><ActionController::Parameters {"participant_id"=>"4", "match_id"=>"1", "local_score"=>"1", "visitant_score"=>"1"
All of these failed requests throw the "match_id already taken" error and update just fine in the console.
The controller request is touching the correct action with the method: :put. I've tried to change the syntax of the validation switching match_id for pool_id (which of course shouldn't make a difference) with no success.
Any, help would be appreciated.
app/models/prediction.rb
class MatchPrediction < ApplicationRecord
belongs_to :participant
belongs_to :match
validates :match_id, uniqueness: { scope: [:pool_participant_id] }
def correct_score?
if match.official_outcome
local_score == match.local_score && visitant_score == match.visitant_score ? true : false
end
end
def correct_outcome?
if match.official_outcome
predicted_outcome == match.official_outcome ? true : false
end
end
end
and server output pre rollback:
Started PUT "/pools/1/predictions/19" for 127.0.0.1 at 2018-03-29 18:17:11 +1100
Processing by PredictionsController#update as JS
Parameters: {"utf8"=>"✓", "authenticity_token"=>"SagJX+a7m0eCAdH7AdA0eYz6BVL1cesXYXOUkoe2FRynta6wyiWdskrC1007V1vyrIApPtdEQnVHWlzhSeJs5Q==", "prediction"=>{"participant_id"=>"4", "match_id"=>"2", "local_score"=>"7", "visitant_score"=>"0"}, "pool_id"=>"1", "id"=>"19"}
User Load (1.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 4], ["LIMIT", 1]]
↳ /Users/andres/.rvm/gems/ruby-2.5.0#global/gems/rack-2.0.4/lib/rack/tempfile_reaper.rb:15
Prediction Load (0.4ms) SELECT "predictions".* FROM "predictions" WHERE "predictions"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ app/controllers/predictions_controller.rb:86
(0.2ms) BEGIN
↳ app/controllers/predictions_controller.rb:55
Participant Load (0.2ms) SELECT "participants".* FROM "participants" WHERE "participants"."id" = $1 LIMIT $2 [["id", 4], ["LIMIT", 1]]
↳ app/controllers/predictions_controller.rb:55
Match Load (0.3ms) SELECT "matches".* FROM "matches" WHERE "matches"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]]
↳ app/controllers/predictions_controller.rb:55
Prediction Exists (1.8ms) SELECT 1 AS one FROM "predictions" WHERE "predictions"."match_id" = $1 AND "predictions"."id" != $2 AND "predictions"."participant_id" = $3 LIMIT $4 [["match_id", 2], ["id", 1], ["participant_id", 4], ["LIMIT", 1]]
↳ app/controllers/predictions_controller.rb:55
Pool Load (0.5ms) SELECT "pools".* FROM "pools" WHERE "pools"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ app/models/prediction.rb:35
Round Load (0.6ms) SELECT "rounds".* FROM "rounds" INNER JOIN "groups" ON "rounds"."id" = "groups"."round_id" WHERE "groups"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ app/models/prediction.rb:21
(0.2ms) ROLLBACK
↳ app/controllers/predictions_controller.rb:55
Embarrassing mistake really. I missed checking how the Predictions Controller was setting #prediction. I had entred the params to find the object incorrectly as #GorillaApe suggested:
def set_match_prediction
#prediction = Prediction.find(params[:pool_id])
end
instead of:
def set_match_prediction
#prediction = Prediction.find(params[:id])
end
this in turn was always setting the same object thus raising the validation error in all but one of the instances which, by chance, its :id coincided with :pool_id.

rails validates_format_of with IP addresses

I'm allowing a user to enter IP addresses in an input field which may be of different types delimited by a comma, such as (in no particular order):
192.168.1.1,192.168.2.1-25,10.10.10.0/24,192.168.1.2
This 'string' would get saved in my DB under device.ips_to_scan.
I want to validates_format_of on these, but am finding it a little difficult to write a regex that seems to work in rails, while it does work on regex101 (https://regex101.com/r/nf2bnM/1):
validates_format_of :ips_scan, with: /\A([0-9]{1,3}\.){3}[0-9]{1,3}(\/([1-2][0-9]|[0-9]|3[0-2]))?(-([0-9]{1,3}))?,?\Z/i, on: :update
This one is expected to fail:
Started PUT "/devices/2" for 127.0.0.1 at 2018-02-19 22:03:15 -0500
Processing by DevicesController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"EQCFG6/xoJHtP6Nd3oqaYRW6mypfEoCMrnio1yj6loP+KtvjgLZ9Gmhb0oTwCjD0RGH+qQuctZFVIvF5HBJcGw==", "device"=>{"ips_scan"=>"192.168.1.1,192.168.2.1-25,a.b.c.d", "ips_exclude"=>"10.10.10.1"}, "commit"=>"Save", "id"=>"2"}
User Load (0.8ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 2], ["LIMIT", 1]]
Device Load (1.6ms) SELECT "devices".* FROM "devices" WHERE "devices"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]]
(0.5ms) BEGIN
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]]
(0.5ms) ROLLBACK
Redirected to http://localhost:3000/devices/2/edit
Completed 302 Found in 47ms (ActiveRecord: 12.1ms)
...But this one should have worked:
Processing by DevicesController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"JJfmT/0l5MEDc+gUH/WHHp3bbgyzjGa0xTzaXM3E/WHLvbi30mI5SoYXmc0xdS2LzAALj+cCU6k+ZoPy+Sw3+Q==", "device"=>{"ips_scan"=>"192.168.1.1,192.168.2.1-25,192.168.1.2", "ips_exclude"=>"10.10.10.1"}, "commit"=>"Save", "id"=>"2"}
User Load (0.9ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 2], ["LIMIT", 1]]
Device Load (0.7ms) SELECT "devices".* FROM "devices" WHERE "devices"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]]
(0.6ms) BEGIN
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]]
(0.6ms) ROLLBACK
Redirected to http://localhost:3000/devices/2/edit
Completed 302 Found in 17ms (ActiveRecord: 3.5ms)
Last thing I can think of, is that I do have strong parameters, but I'm permitting ips_scan, so that this shouldn't be an issue:
def update
if #device.update(device_params)
flash[:notice] = 'Successful update'
respond_with :edit, :device
else
flash[:warning] = 'Unable to update'
respond_with :edit, :device
end
end
private def device_params
params.require(:device).permit(:token, :ips_scan, :ips_exclude)
end
I'm hoping you rubyist's out there have a eloquent solution. The first thought that comes to mind is that I have to split the string, and check each element sequentially to ensure it matches instead.
While I'm still open to a nice eloquent one-liner within the Model itself, I was able to get this working through creating a concern:
models/concerns/ip_validator.rb
class IpValidator < ActiveModel::Validator
def validate(record)
ips = record.ips_scan.split(',')
ips.each do |ip|
/([0-9]{1,3}\.){3}[0-9]{1,3}(\/([1-2][0-9]|[0-9]|3[0-2]))?(-([0-9]{1,3}))?/ =~ ip
record.errors.add(:ips_scan, ' is not valid') unless $LAST_MATCH_INFO
end
end
end
The call in my model now looks like:
validates :ips_scan, :ips_exclude, ip: true, on: :update
You can use this method in your custom validator to check an IP address
require 'ipaddr'
def valid_ip_addr?(ip_addr)
IPAddr.new(ip_addr)
true
rescue IPAddr::InvalidAddressError => _error
false
end

POST "/admin/users/26/approve_vip" not working

I want to approve the user to be vip,but when I press the button.The page refreshed but nothing changed.The log in terminal is
Started POST "/admin/users/26/approve_vip" for ::1 at 2016-12-12 16:33:22 +0800
Processing by Admin::UsersController#approve_vip as HTML
Parameters: {"authenticity_token"=>"qYrbaVH/cssY3VBYLw6Hd4wXl42Zz8OqkdHGGoITEeeWtbJ4ZOLOmJF/Jmpx70s9aaL5Yr0vFhqNV9kGHtILpA==", "user_id"=>"26"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 4], ["LIMIT", 1]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 26], ["LIMIT", 1]]
SQL (1.4ms) UPDATE "users" SET "is_vip" = 't' WHERE "users"."id" = ? [["id", 26]]
(0.0ms) begin transaction
(0.0ms) commit transaction
DEPRECATION WARNING: `redirect_to :back` is deprecated and will be removed from Rails 5.1. Please use `redirect_back(fallback_location: fallback_location)` where `fallback_location` represents the location to use if the request has no HTTP referer information. (called from approve_vip at /Users/a1/JDDstore/app/controllers/admin/users_controller.rb:26)
Redirected to http://localhost:3000/admin/users
Completed 302 Found in 6ms (ActiveRecord: 1.7ms)
Started POST "/admin/users/26/approve_vip" for ::1 at 2016-12-12 15:41:47 +0800
Processing by Admin::UsersController#approve_vip as HTML
Parameters: {"authenticity_token"=>"uYc9hdEZaYCgfhdmYK3XnyK2lcraPpHWfuXcQ5cRtLyGuFSU5ATV0yncYVQ+TBvVxwP7Jf7eRGZiY8NfC9Cu/w==", "user_id"=>"26"}
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 4], ["LIMIT", 1]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 26], ["LIMIT", 1]]
(0.0ms) begin transaction
(0.0ms) commit transaction
DEPRECATION WARNING: `redirect_to :back` is deprecated and will be removed from Rails 5.1. Please use `redirect_back(fallback_location: fallback_location)` where `fallback_location` represents the location to use if the request has no HTTP referer information. (called from approve_vip at /Users/a1/JDDstore/app/controllers/admin/users_controller.rb:26)
Redirected to http://localhost:3000/admin/users
Completed 302 Found in 4ms (ActiveRecord: 0.3ms)
And the code in controller is
def approve_vip
#user = User.find(params[:user_id])
#user.is_vip=true
#user.save
redirect_to :back
end
Can you tell me why it not change the role?
If you want to know more informatian, please let me know. Thank you very much for helping me.
It looks you have some model callback (may be before_save) which is restricting to update the records.
You can use update_column or update_columns to bypass the callbacks/validations and directly make a update query to your db.
def approve_vip
#user = User.find(params[:user_id])
#user.update_columns(is_vip: true)
redirect_to :back
end
You need to read error's message. Probably the user's validation is failed.
def approv!
update_attributes!(is_vip: true)
end
This code give you exception with the error's message.

Nested resource failing to update

I'm trying to do what I think should be simple: do a simple edit on a single text string field with the default update action. But it just doesn't seem to work, despite many attempts and alterations.
There are no errors and the flash message responds successfully, but information isn't saved to the database at all:
routes.rb
resources :interviews do
resources :invitations do
put :accept
end
end
views/invitations/edit.html.haml
= simple_form_for [#interview, #invitation] do |f|
= f.error_notification
= f.input :testing
= f.submit 'Edit Invitstion', :class => 'button small'
controllers/invitations_controller.rb
def update
#invitation = Invitation.find(params[:id])
#interview = Interview.find(params[:interview_id])
#invitation.update_attributes(invitation_params)
if #invitation.update_attributes(invitation_params)
redirect_to edit_interview_invitation_path(#interview, #invitation), notice: "Your profile has been successfully updated."
else
render action: "edit"
end
end
private
def invitation_params
params.permit(:user_id, :interview_id, :invitation_id, :session_time, :workflow_state, :testing)
end
And here's the log:
Started PATCH "/interviews/3/invitations/7" for ::1 at 2016-05-15 19:01:52 +0800
Processing by InvitationsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"o0U5t0yPN0aE2er+DWK0uxqRGyp4ywfdSrEfvwiSQ3UUaOnr3Fd0raFs1IUqVzizKoqxRU0DDpmvysntB9fdhQ==", "invitation"=>{"interview_id"=>"3", "workflow_state"=>"invited", "session_time"=>"", "testing"=>"testtesttest"}, "commit"=>"Edit Invitstion", "interview_id"=>"3", "id"=>"7"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["id", 7]]
Invitation Load (0.2ms) SELECT "invitations".* FROM "invitations" WHERE "invitations"."id" = $1 LIMIT 1 [["id", 7]]
Role Load (0.2ms) SELECT "roles".* FROM "roles" WHERE "roles"."id" = $1 LIMIT 1 [["id", 3]]
Interview Load (0.2ms) SELECT "interviews".* FROM "interviews" WHERE "interviews"."id" = $1 ORDER BY created_at DESC LIMIT 1 [["id", 3]]
CACHE (0.0ms) SELECT "invitations".* FROM "invitations" WHERE "invitations"."id" = $1 LIMIT 1 [["id", "7"]]
CACHE (0.0ms) SELECT "interviews".* FROM "interviews" WHERE "interviews"."id" = $1 ORDER BY created_at DESC LIMIT 1 [["id", "3"]]
Unpermitted parameters: utf8, _method, authenticity_token, invitation, commit, id
(0.1ms) BEGIN
Invitation Exists (0.4ms) SELECT 1 AS one FROM "invitations" WHERE ("invitations"."user_id" = 3 AND "invitations"."id" != 7 AND "invitations"."interview_id" = 3) LIMIT 1
(0.1ms) COMMIT
Redirected to http://localhost:3000/interviews/3/invitations/7/edit
Completed 302 Found in 12ms (ActiveRecord: 1.6ms)
Started GET "/interviews/3/invitations/7/edit" for ::1 at 2016-05-15 19:01:52 +0800
Processing by InvitationsController#edit as HTML
Parameters: {"interview_id"=>"3", "id"=>"7"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["id", 7]]
Invitation Load (0.3ms) SELECT "invitations".* FROM "invitations" WHERE "invitations"."id" = $1 LIMIT 1 [["id", 7]]
Role Load (0.2ms) SELECT "roles".* FROM "roles" WHERE "roles"."id" = $1 LIMIT 1 [["id", 3]]
Interview Load (0.2ms) SELECT "interviews".* FROM "interviews" WHERE "interviews"."id" = $1 ORDER BY created_at DESC LIMIT 1 [["id", 3]]
Rendered invitations/edit.html.haml within layouts/application (6.1ms)
Completed 200 OK in 48ms (Views: 39.1ms | ActiveRecord: 1.6ms)
Check the format of your params object in your logs. Your invitation params are being passed within the params["invitation"] key, yet you're whitelisting and updating your object based on the params in the root params hash.
Also note that your logs are reporting that you're trying to update your invitation with unpermitted params:
Unpermitted parameters: utf8, _method, authenticity_token, invitation, commit, id
You can fix this by simply updating your invitation_params to use params[:invitation] rather than params like so:
def invitation_params
params.require(:invitation).permit(:user_id, :interview_id, :invitation_id, :session_time, :workflow_state, :testing)
end
Also, you might want to consider raising an error if you're trying to update a parameter that's not whitelisted to prevent these sorts of issues in the future.
In your rails config:
config.action_controller.action_on_unpermitted_parameters = :raise

Resources