Can't mass-assign, when i build a has one association - ruby-on-rails

Order.rb:
class Order < ActiveRecord::Base
has_one :review
end
Review.rb:
class Review < ActiveRecord::Base
belongs_to :order
end
I need to build a review, im using this method:
class OrdersController < ApplicationController
def build_review
#review = Review.new(:order => #order)
end
but i get this error:
Can't mass-assign protected attributes: order
any ideas?

You need to white list order for mass assignment via a params hash. Read http://api.rubyonrails.org/classes/ActiveModel/MassAssignmentSecurity/ClassMethods.html
Add this to the Review model
attr_accessible :order

It would be better to use #review = #order.build_review instead of adding this to attr_accessible. Just in case ;)

Related

Rails Mass Assign has_and_belongs_to_many

I'm getting this error when I try to mass-assign a HABTM relationship in Rails 5:
*** ActiveRecord::RecordNotFound Exception: Couldn't find Job with ID=6 for Profile with ID=
class Profile < ApplicationRecord
has_and_belongs_to_many :jobs
accepts_nested_attributes_for :jobs
end
class Job < ApplicationRecord
has_and_belongs_to_many :profiles
end
class ProfilesController < ApplicationController
def create
#profile = Profile.new(profile_params)
end
private
def profile_params
params.require(:profile).permit(
:name,
:email,
:jobs_attributes: [:id]
)
end
end
=form_for #profile do |f|
=f.fields_for :jobs do |j|
=j.select :id, options_for_select([[1, "Job 1", ...]])
The problem is that accepts_nested_attributes_for is a way to update attributes on an associated object (either already linked or one that you're creating). You can think of it doing something like:
params[:jobs_attributes].each do |job_attributes|
#profile.jobs.find(job_attributes[:id]).attributes = job_attributes
end
Except that the job is then saved with the new attributes in an after-save of the parent object.
This isn't what you want. In fact, you don't need accepts_nested_attributes at all.
Instead, change the attribute in your view to job_ids as if it's an attribute of the profile, something like the following:
=form_for #profile do |f|
=j.select :job_ids, options_for_select([[1, "Job 1", ...]])
This will effectively call profile.job_ids = [1,2,3] which will have the effect that you're looking for.

Rails replace simple ActiveRecord relation in case of big amount of data

In my app controller looks pretty simple:
class ProductsController
before_action :set_company
def index
#products = company.products.includes(:shipment)
end
private
def set_company
#company = Company.find(params[:company_id])
end
end
But what I worry about is the #product inside Index action was properly declared? What if I'll have millions of products? is there more efficient solution?
Model relations below:
class Products < ApplicationRecord
belongs_to :company
has_many :shipment
end
class Company < ApplicationRecord
has_many :products
end
class Shipment < ApplicationRecord
belongs_to :products
end
There is definitely a problem if you have million of records because your query is going to be too big, in this case you can add pagination in any flavor or use any other strategy that reduce the number of records queried each time.
To do pagination in Rails you can use https://github.com/kaminari/kaminari but this is not the only strategy available to do this.

uninitialized constant ReviewsController::Reviews

I had this working very similar to another controller however i needed to change this relation to another controller called agreements_controller. I want to create a has one model. review has one and belongs to agreements.
Why isn't the row being created properly?
reviews_controller:
class ReviewsController < ApplicationController
def create
#review = Reviews.create(review_params)
end
private
def review_params
params.require(:review).permit(:comment, :star, :agreement_id, :user_id, :reviser_user_id)
end
end
_form.html.erb
<%= form_for([agreement, agreement.build_review] ) do |f| %>
<% end %>
agreement.rb
class Agreement < ActiveRecord::Base
has_one :review, :dependent => :destroy
end
review.rb
class Review < ActiveRecord::Base
belongs_to :agreement
belongs_to :reviser_user
belongs_to :user
end
I've tried to find similar examples online, but all I could find was nested forms... I don't need a nested form I just want the review to create as a has one.
Models are Singular. Use
Review.create(review_params)

How pass params and save in db

have the relationships:
(my code is in portuguese)
Order
class Pedido < ActiveRecord::Base
belongs_to :pessoa
Person
class Pessoa < ActiveRecord::Base
belongs_to :usuario
has_many :enderecos
has_many :pedidos
accepts_nested_attributes_for :enderecos
end
User
class Usuario < ActiveRecord::Base
has_many :pessoas
has_many :pedidos, through: :pessoas
end
carrinhos_controller.rb
def checkout
#pedido = current_usuario.pedidos.build
end
In migration Person have usuario_id, Order have pessoa_id and others...
When I finish an order, the pessoa_id is null and does not save on database, why??
More codes:
pedidos_controller.rb
class PedidosController < ApplicationController
before_action :authenticate_usuario!
# Criar pedido
def create
#pedido = current_usuario.pedidos.build(pedido_params)
if #pedido.save
#pedido.construir_cache_item_carrinho(carrinho_atual)
#pedido.calcular_total!(carrinho_atual)
carrinho_atual.limpar!
#OrdemDeServico.new(carrinho_atual, #pedido).encomendar_pedido!
redirect_to pedido_path(#pedido.token)
else
render "carrinho/checkout"
end
end
Use this code:
def checkout
#pedido = current_usuario.pedidos.build
#pedido.save
end
If you are using build or new, then you have to use save method after that. Otherwise you can use direct create method.
build does not save to the DB. Either save afterwards (#pedido.save) or try #pedido = current_usuario.pedidos.create
I suppose you are using something like a nested form to post the parameters. I often use the nested_form gem by Ryan Bates.
If you look careful at the usage you can find some useful insights.
Also take care of the strong_parameters: it is required to declare in the controller which params you want to permit.

remove specific user from joined table

In Ruby on Rails I have a user models and a jobs model joined through a different model called applicants. I have a button for the users when they want to "remove their application for this job" but I don't know how to remove the specific user, and for that matter I don't know if I'm doing a good job at adding them either (I know atleast it works).
user.rb
class User < ActiveRecord::Base
...
has_many :applicants
has_many:jobs, through: :applicants
end
job.rb
class Job < ActiveRecord::Base
...
has_many :applicants
has_many:users, through: :applicants
end
applicant.rb
class Applicant < ActiveRecord::Base
belongs_to :job
belongs_to :user
end
when someone applies for a job my jobs controller is called:
class JobsController < ApplicationController
...
def addapply
#job = Job.find(params[:id])
applicant = Applicant.find_or_initialize_by(job_id: #job.id)
applicant.update(user_id: current_user.id)
redirect_to #job
end
...
end
Does that .update indicate that whatever is there will be replaced? I'm not sure if I'm doing that right.
When someone wants to remove their application I want it to go to my jobs controller again but I'm not sure what def to make, maybe something like this?
def removeapply
#job = Job.find(params[:id])
applicant = Applicant.find_or_initialize_by(job_id: #job.id)
applicant.update(user_id: current_user.id).destroy
redirect_to #job
end
does it ave to sort through the list of user_ids save them all to an array but the one I want to remove, delete the table then put them all back in? I'm unsure how this has_many works, let alone has_many :through sorry for the ignorance!
thanks!
Let's assume the user will want to remove their own application. You can do something like this:
class UsersController < ApplicationController
def show
#applicants = current_user.applicants # or #user.find(params[:id]), whatever you prefer
end
end
class ApplicantsController < ApplicationController
def destroy
current_user.applications.find(params[:id]).destroy
redirect_to :back # or whereever
end
end
And in your view:
- #applicants.each do |applicant|
= form_for applicant, method: :delete do |f|
= f.submit
Don't forget to set a route:
resources :applicants, only: :destroy
Some observations, I would probably name the association application instead of applicant. So has_many :applications, class_name: 'Applicant'.

Resources