I'm working with three tables as follows:
article.rb
class Article < ActiveRecord::Base
has_many :comments
has_many :comentarios, :through => :comments
end
comment.rb
class Comment < ActiveRecord::Base
belongs_to :article
has_many :comentarios
end
and comentario.rb
class Comentario < ActiveRecord::Base
belongs_to :article
end
Everything works fine until I attempt to add a 'comentario' and returns this error
ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection in ComentariosController#create
Cannot modify association 'Article#comentarios' because the source reflection class 'Comentario' is associated to 'Comment' via :has_many.
This is the code I use to create a new 'comentario'
comentarios_controller.rb
class ComentariosController < ApplicationController
def new
#comentario = Comentario.new
end
def create
#article = Article.find(params[:article_id])
#comentario = #article.comentarios.create(comentario_params)
redirect_to article_path(#article)
end
private
def comentario_params
params.require(:comentario).permit(:comentador, :comentario)
end
end
The output returns an error in the line where I create #comentario from calling #article but I can't see why since Ruby documentation says that once I associate comentario to article using :through, I can simply call something like #article.comentario.
Any idea of what is causing this error?
or do you have any suggestion on how to achieve this association in any other way?
Ok. The issue is that Rails is confused about which article to use here.
Your Comment model belongs_to :article but also your Commentario belongs_to :article... so if you use #article.commentarios - it's confused as to whether the article refers to the article of the comment or the article of the commentario.
You will probably need to update your form to be more explicit about what you're referring to. A form for the commentario should actually include fields for the comment it creates.
Somebody else had the same problem here. You may wish to look at the solution here: "Cannot modify association because the source reflection class is associated via :has_many"
Related
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.
class Comment < ActiveRecord::Base
belongs_to :article
end
class Article < ActiveRecord::Base
has_many :comments do
def posted_comments
#user_comment is just an attribute of comment.
collect(&:user_comment)
end
end
end
to fetch the posted comments :
Article.first.comments.posted_comments
=> ["Nice article posted", "comment 2 added", "Good article"]
Above one is fetching correct results, but I want to have a more compact version.
Something like this:
Article.first.posted_comments
#this should list the collection of comments on the article.
Can we do something like this with Rails ActiveRecord ?
For simply solution, you can define method called posted_comments that calls nested association as the following:
def posted_commments
self.comments.posted_comments
end
Or, Try the following code:
has_many :posted_comments, -> { select("user_comment") }, class_name: "Comment"
In Rails 3.2 I have been looking for a way to traverse the associations of an object within the before_add callback.
So basically my use case is:
class User < ActiveRecord::Base
has_and_belongs_to_many :meetings
end
class Meeting < ActiveRecord::Base
has_and_belongs_to_many :users
has_many :comments, :before_add => :set_owner_id
end
class Comment < ActiveRecord::Base
belongs_to :meeting
end
def set_owner_id(child)
child.owner_id = <<<THE USER ID for #user >>>
end
and I am creating a comment within the context of a user:
#user.meetings.first.comments.create
How do I traverse the associations from within the before_add callback to discover the id of #user? I want to set this at model level. I have been looking at proxy_association, but I may be missing something. Any ideas?
You should probably create the comment in the context of the meeting, no? Either way, you should handle this in the controller since you'll have no access to #user in your model.
#comment = Meeting.find(id).comments.create(owner_id: #user, ... )
But if you insist on your way, do this:
#comment = #user.meetings.first.comments.create(owner_id: #user.id)
So, I've read in some book about tip "Use model association", which encourages developers to use build methods instead of putting ids via setters.
Assume you have multiple has_many relationships in your model. What's best practise for creating model then ?
For example, let's say you have models Article, User and Group.
class Article < ActiveRecord::Base
belongs_to :user
belongs_to :subdomain
end
class User < ActiveRecord::Base
has_many :articles
end
class Subdomain < ActiveRecord::Base
has_many :articles
end
and ArticlesController:
class ArticlesController < ApplicationController
def create
# let's say we have methods current_user which returns current user and current_subdomain which gets current subdomain
# so, what I need here is a way to set subdomain_id to current_subdomain.id and user_id to current_user.id
#article = current_user.articles.build(params[:article])
#article.subdomain_id = current_subdomain.id
# or Dogbert's suggestion
#article.subdomain = current_subdomain
#article.save
end
end
Is there a cleaner way ?
Thanks!
This should be a little cleaner.
#article.subdomain = current_subdomain
The only thing I can think of is merging the subdomain with params:
#article = current_user.articles.build(params[:article].merge(:subdomain => current_subdomain))
rails 3 newbie, using Devise for auth...
I want to create the following models:
class Instance < ActiveRecord::Base
has_many :users
has_many :notes
end
class User < ActiveRecord::Base
belongs_to :instance
end
class Note < ActiveRecord::Base
belongs_to :instance
end
To create a new note in the notes_controller.rb
def create
#note = instance.notes.build(params[:note].merge(:instance_id => current_user.instance_id))
end
But I'm getting the following ERROR: "undefined local variable or method `instance' for #"
Ideas?
You haven't assigned anything to "instance" yet, so there's nothing to reference. If you know the instance record already exists in the database, you could do something like:
#instance = current_user.instance
#note = Note.create(:instace_id => #instance.id)
If not, you'd need to check and create it first if necessary, using the same kind of syntax.