How to update #model.submodel in rails 6 - ruby

I'm unsuccessfully trying to update a submodel, somehow nested but getting unusual results.
Background (Scroll down for problem):
Update a boolean verified of a reviews model to associate whether a translation is verified or not, with translation entry and user model associated references. Details about creating a verification are here
# routes.rb
resources :entries do
resources :reviews
end
# entry.rb
belongs_to :user
has_one :review
accepts_nested_attributes_for :review
# user.rb
has_many :entries
has_many :reviews
# review.rb
belongs_to :user
belongs_to :entry
From entry index, pass the entry instance to the edit partial, works perfect
# /entries/index.html.erb
<% #entries.each do |entry| %>
...
<% if entry.review %>
<%= render 'reviews/edit', entry: entry %>
<% end %>
...
<% end %>
The _edit.html.erb form seems correct...
# reviews/_edit.html.erb
<span>
<%= form_for([entry, entry.review]) do |f| %>
<div class="form-check form-switch">
<%= f.check_box :verified, class: "form-check-input" %>
</div>
<%= f.submit class: "btn btn-primary"%>
<% end %>
</span>
In the browser console, the model entry is well assigned. And also the association entry.review is well assigned i.e
>> entry.review
=> #<Review id: 4, user_id: 1, entry_id: 19,
verified: false, created_at: "2021-02-18 03:43:27",
updated_at: "2021-02-18 14:31:15">
Even using the Ruby on Rails 6 deprecated method update_attribute works
>> entry.review.update_attribute(:verified, false)
=> true
Problem: The update method in reviews_controller.rb executes successfully when verified is true, but not when it's false
# reviews_controller.rb
def update
#entry.review.update(review_params)
end
private
def review_params
params.require(:review).permit(:verified, user: current_user, entry: #entry)
end
Works
{"_method"=>"patch", "authenticity_token"=>"...",
"review"=>{"verified"=>"1"}, "commit"=>"Update Review"}
but not
{"_method"=>"patch", "authenticity_token"=>"...",
"review"=>{"verified"=>"0"}, "commit"=>"Update Review"}

I think I found the problem after #yzalavin suggestion. Somehow validations were preventing the update. I do not know the association yet.
Simply adding on: :create allowed me o update.
validates_presence_of :entry_id, :user_id, :verified, on: :create

Related

Saving associated many to many relation in Rails

I looked at related posts but could not get my code working. I have device and project models which are connected thru projects_devices table. My models look like this:
class Device < ActiveRecord::Base
has_many :projects, :through => :projects_devices
has_many :projects_devices
accepts_nested_attributes_for :projects_devices, allow_destroy: true
end
class Project < ActiveRecord::Base
has_many :devices, :through => :projects_devices
has_many :projects_devices
accepts_nested_attributes_for :projects_devices
end
class ProjectsDevice < ActiveRecord::Base
belongs_to :device
belongs_to :project
end
my device controller:
def new
#device = Device.new
project_device = #device.projects_devices.build
#projects = Project.all
end
def create
#device = Device.create(device_params)
end
and my device_param :
def device_params
params.require(:device).permit(:name, :projects_devices_attributes => [:id , project_id: []])
end
My view:
<%= f.label :Projects_devices %><br>
<%= f.fields_for :projects_devices do |proj| %>
<%= proj.select(:project_id, [], {}, {class: "form-control projects-devices", multiple: "multiple" } ) do %>
<% if (#projects) %>
<% for #project in #projects %>
<%= content_tag(:option, #project.name, value: #project.id) %>
<% end %>
<% end %>
<% end %>
<% end %>
I have a many to many association between two models. While creating and editing devices, I should be able to list all the projects available. And select only the ones required. When I save, the projects_devices table should be updated.
I have multiple problems here:
When I save a new device,
a. the projects_devices table has only device_id but not project_id. project_id is null
b. if there are 2 projects and I select only one, even then, two association records are created with only device_id (as mentioned above, project_id is missing). It should create only one association.
When a new device is edited,
a. Project list, list of all projects is empty.
b. I see 2 select boxes are displayed instead of one for projects(there are 2 projects currently).
c. updating the device is not happening.
I checked the params:
"device"=>{"name"=>"ABC",
"projects_devices_attributes"=>{"0"=>{"project_id"=>["",
"298486374"]}}},
"commit"=>"Create Device"}
My rails log shows that my application is inserting only device_id:
SQL (0.1ms) INSERT INTO `projects_devices` (`device_id`) VALUES (980190994)
Can somebody help me figure out the problem
Im not sure, but try this params.
Maybe it helps. :)
def device_params
params.require(:device).permit(:name, :projects_devices_attributes =>[:id, :device_id, :project_id, :_destroy])
end

Unpermitted parameters in Rails 5 using has_many, through

I keep getting unpermitted parameters for my appointment model.
Here are my models
class Appointment < ApplicationRecord
belongs_to :client
belongs_to :trainer
end
class Trainer < ApplicationRecord
has_many :appointments
has_many :clients, through: :appointments
end
class Client < ApplicationRecord
has_many :appointments
has_many :trainers, through: :appointments
end
Here's my controller, I just listed my private method for sake of brevity.
def appt_params
params.require(:appointment).permit(:appointment_date, client_id: [],
trainer_id: [])
end
The error says unpermitted parameters for trainer, client.
Am I missing something in my strong parameters method?
Here is my appointments/new view
<%= form_for #appointment do |f| %>
<%= f.datetime_select :appointment_date %>
<%= f.collection_select :trainer, Trainer.all, :id, :first_name %>
<%= f.collection_select :client, Client.all, :id, :name %>
<%= f.submit %>
<% end %>
I added collection to my appt_params method and still getting the same error. I'm still getting the hang of Rails and any help would be appreciated, thanks!
Since you have used associations, only client_id and trainer_id is enough and those should of integer form not array.
So change your strong parameters method code to :
def appt_params
params.require(:appointment).permit(:appointment_date, :client_id,
:trainer_id)
end
And your applications/new view to:
<%= form_for #appointment do |f| %>
<%= f.datetime_select :appointment_date %>
<%= f.collection_select :trainer_id, Trainer.all, :id, :first_name %>
<%= f.collection_select :client_id, Client.all, :id, :name %>
<%= f.submit %>
<% end %>
Was running with the same issue as you and after hours of debugging I was able to solve it:
Was my first time trying associations in Rails with models similar to you, so most documentation online recommends to define the foreign key's as client_ids:[] assuming your ids were arrays, when definitely they are integers as my parameters where
Parameters: {"utf8"=>"✓", "authenticity_token"=>"LEcwQ56xYJGpq2zIs6Cz0YbU7B7mBKRa6rhspVIxo9vEB5/UoFUvHYiN0UC0krTiIp+d0tzhit6DZT1Z8PmYYg==", "califica"=>{"text"=>"hi", "grade"=>"5", "user_id"=>"1", "party_id"=>"1"}, "commit"=>"Create Calification"}
I think this is due to f.collection_select taking as expected one value. So after hours of using the permit of arrays such as :user_id => [] , user_id:[] I was getting always error of Unpermitted parameters.
Tried the answer of #Bharath (which is correct) but it was still not working, that's was when I realized that my old models weren't made with references (ActiveModel::UnknownAttributeError (unknown attribute 'user_id' for Calification.):) so I had to make a references migration to add a foreign key and then it was all working perfectly.

Skip has_many :through Model Creation on No Change

This could be the wrong way to go about this entirely, and I'm very open to alternatives.
I've got the following models, where Users can have many Positions:
class User < ApplicationRecord
has_many :user_positions
has_many :positions, through: :user_positions
accepts_nested_attributes_for :user_positions,
reject_if: :all_blank
end
class UserPosition < ApplicationRecord
belongs_to :user
belongs_to :position
end
class Position < ApplicationRecord
end
On my edit user form, I'd like to allow a User's current position to be updated. I do that in the following way:
<%= form_for #user do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.fields_for :user_positions, #user.user_positions.order(created_at: :desc).first do |ff| %>
<%= ff.hidden_field :user_id, value: #user.id %>
<%= ff.collection_select :position_id, Position.all, :id, :label %>
<% end %>
<%= f.submit "Update User" %>
<% end %>
The issue I'm running into is that a new instance of UserPosition is being created every time I submit the form, even if the Position that's selected hasn't changed. This leads to a bunch of duplicate entries in the join table, when I really only care about "promotions" or "demotions" when the value of position_id has changed.
I don't want to add a custom validator to disallow the creation, because I still want the form to be able to submit with an unchanged position. An example of this is when I only want to change the User's name.
Any advice on how to deal with this use case?
It turns out that you can actually use any method as a Symbol argument the to accepts_nested_attributes_for reject_if option.
I updated my User model as follows, and now the behavior is exactly what I want:
class User < ApplicationRecord
has_many :user_positions
has_many :positions, through: :user_positions
accepts_nested_attributes_for :user_positions,
reject_if: :same_as_previous_position
def same_as_previous_position(attributes)
if self.user_positions.empty?
return false
end
Position.find(attributes[:position_id]) == self.user_positions.order(created_at: :desc).first.position
end
end

has_many and belongs_ association rails 4

I am while leraning how to use association in rails 4 application
I have a user having many opinions and I want to add user opinion in the book show page
This is how i proceed:
my user.rb
class User < ActiveRecord::Base
has_one :panier
has_many :opinions
end
opinion.rb
class Opinion < ActiveRecord::Base
belongs_to :user
end
views/books/show.html.erb
<h2>Votre opinion nous intéresse:</h2>
<%= form_for([#user, #user.opinions.build]) do |f| %>
<p>
<%= f.label :body, 'votre opinion' %><br>
<%= f.text_area :body %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
opinion_controller.rb
class OpinionsController < ApplicationController
def create
#user = current_user
#opinion= #user.opinion.create(opinion_params)
end
private
def opinion_params
params.require(:opinion).permit(:body)
end
end
and in books_controllers this is my show method:
def show
#user= current_user
#books= Book.all
end
my routes:
get 'books/show' => 'books#show' , as: :books_list
resources :users do
resources :opinions
end
what I got as error:
undefined method `opinions' for nil:NilClass
in this line of code:
Most probably #user.opinions in your form causing this issue. check whether current_user returning object or not.
Also in your create method there is typo(#user.opinion), it should be #user.opinions.
Use accept nested attributes for same.

Unpermitted Parameters Error for Whitlisted Attributes

I'm working on a sort of web based game using rails. My goal is to set up a form that allows a user to create a fort on the user/show page via a user update form. I have been using nested attributes and associations to try to solve the problem but I keep running into an error.
I have set up associations in my models such as this:
class Fort < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_many :forts
accepts_nested_attributes_for :forts
... (omitted validations)
end
And I have set up my Users Controller with update and edit actions like this:
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
#user.forts.build(params[:user][:forts])
if #user.update_attributes(user_params)
render #user
else
render 'users/show'
end
end
My Form in the users/show view looks like this:
<%= form_for #user do |f| %>
<%= f.fields_for :fort do |builder| %>
<%= builder.label :name, "Name" %>
<%= builder.text_field :name %>
<%= builder.label :ownership, "Ownership" %>
<%= builder.text_field :ownership %>
<% end %>
<%= f.submit %>
<% end %>
My Routes (I wouldn't think they are important here, but just in case),
BR::Application.routes.draw do
resources :users
resources :sessions, only: [:new, :create, :destroy]
resources :forts
resources :encampments
root 'home#home'
match '/signup', to: 'users#new', via: 'get'
match '/signin', to: 'sessions#new', via: 'get'
match '/signout', to: 'sessions#destroy', via: 'delete'
And finally my parameters are written as so:
def user_params
params.require(:user).permit(:name, :email, :username, :password, :password_confirmation, forts_attributes: [ :name, :xco, :yco, :territory, :ownership ])
end
I have teetered between the above
def user_params
params.require(:user).permit(:name, :email, :username, :password, :password_confirmation, forts_ids: [ :name, :xco, :yco, :territory, :ownership ])
end
and
def user_params
params.require(:user).permit(:name, :email, :username, :password, :password_confirmation, :forts => [ :name, :xco, :yco, :territory, :ownership ])
end
based on different protocols I have seen on other Stack Overflow threads. None of the permutations worked.
They all threw this error in the logs:
Unpermitted parameters: fort
The entire process looks like this:
Started PATCH "/users/1" for 127.0.0.1 at 2014-09-04 20:35:02 -0400
Processing by UsersController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"srybKv+WDZubO4ez+kiwfTYZvrP4tZYe9QoxQaMgmPk=", "user"=>{"fort"=>{"name"=>"test2", "ownership"=>"43"}}, "commit"=>"Update User", "id"=>"1"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", "1"]]
Unpermitted parameters: fort
(0.1ms) begin transaction
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE (LOWER("users"."email") = LOWER('ascottpedersen#gmail.com') AND "users"."id" != 1) LIMIT 1
(0.1ms) rollback transaction
Rendered users/show.html.erb within layouts/application (49.2ms)
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."remember_token" = '0705e5d60cc1be62fa51d8e407683e79f730a3b2' LIMIT 1
Completed 200 OK in 173ms (Views: 66.1ms | ActiveRecord: 5.1ms)
I'm pretty sure I dotted my I's and crossed my T's but now I'm pulling my hair out trying to find my error. Any help you guys can give, would be reciprocated with my thanks forever. The only problem I can still imagine is that I am putting an update form in my users/show view, but I am almost sure that is a fine thing to do.
Thanks again,
Alex P
Your parameter name doesn't match what is in your user_params method: one has fort and the other has forts.
In addition, for nested attributes to work the name passed to fields_for must match the association name (ie :forts
This will result in the parameters for forts being under the forts_attributes key (and so that is what should go into user_params, but only once you've changed the call to fields_for)
As you have has_many :forts relation in your User model,this line
<%= f.fields_for :fort do |builder| %>
should be
<%= f.fields_for :forts do |builder| %>
I guess this is causing the problem for you.

Resources