I have 3 objects : article, comment and user
I want, when a comment is published with a form, that the comment is associated with article AND user.
When I had only article and comment, it was perfect : we could send the form, create the comment, and it was showed.
Comment's form :
<%= form_with(model: [ #article, #article.comments.build ], local: true) do |form| %>
<p>
<%= form.label :body %><br>
<%= form.text_area(:body, {:class => 'form-control'} ) %>
</p>
<p>
<%= form.submit({:class => 'btn btn-success'}) %>
</p>
<% end %>
Create method of CommentsController :
before_action :authenticate_user!
def create
#article = Article.find(params[:article_id])
#comment = #article.comments.build(comment_params)
#comment.commenter = current_user
#comment.save
redirect_to article_path(#article)
end
And models :
class Comment < ApplicationRecord
belongs_to :article
belongs_to :user
end
class User < ApplicationRecord
has_many :comments, dependent: :destroy
# Some devise things
end
class Comment < ApplicationRecord
belongs_to :article
belongs_to :user
end
I want that the comment is pushed on the database, but the actual result is nothing is done : the comment is simply ignored, and I don't know why. I already search here, I found a topic with a similar issue, but the solution was my actual code.
Related
I'm kinda new to ruby on rails, I've been reading documentation on assosiations and I've been having an easy time (and usually a quick google search solves most of my doubts) however recently I'm having problems with a seemingly easy thing to do.
What I'm trying to do is to create an Event, linked to an existing Category.
Event model
class Event < ApplicationRecord
has_many :categorizations
has_many :categories, through: :categorizations
accepts_nested_attributes_for :categorizations
.
.
.
end
Category model
class Category < ApplicationRecord
has_many :categorizations
has_many :events, through: :categorizations
end
Categorization model
class Categorization < ApplicationRecord
belongs_to :event
belongs_to :category
end
Event controller
class EventsController < ApplicationController
def new
#event = Event.new
end
def create
#user = User.find(current_user.id)
#event = #user.events.create(event_params)
if #event.save
redirect_to root_path
else
redirect_to root_path
end
end
private
def event_params
params.require(:event).permit(:name, category_ids:[])
end
Here is the form, which is where I think the problem lies:
<%= form_for #event, :html => {:multipart => true} do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.fields_for :categorizations do |categories_fields|%>
<% categories = [] %>
<% Category.all.each do |category| %>
<% categories << category.name %>
<% end %>
<%= categories_fields.label :category_id, "Category" %>
<%= categories_fields.select ( :category_id, categories) %>
<% end %>
.
.
.
<%= f.submit "Create"%>
<% end %>
I previously populate the Category db with some categories, so what's left to do is to while creating an event, also create a categorization that is linked both to the new event and the chosen Categorization. but the things I've tried don't seem to be working.
Other things seem to be working ok, whenever I try to submit the event all things are populated as expected except the categorization.
As you mentioned that you are new to rails, you'll find this cocoon gem very interesting. You can achieve what you wanted. And the code will cleaner.
I don't have the points to comment, that's why I am giving this as an answer.
I want a User to be able to answer all questions that are assigned to them, in an Answer model. Now I'm trying to create a form that allows me to loop through the questions a User have assigned to them, and answer them in an Answer model.
In the answer model I save the reply, and the question id. However this requires multiple saves in one form, which I'm unable to do.
Model associations look like this:
User
has_many :answers
has_many :questions, through: :question_participants
Answer
belongs_to :user
belongs_to :question
now I'm trying to create an Answer#new form like this:
<%= form_for #answer do |f| %>
<% #questions.each do |question| %>
<h3><%= question.name %></h3>
<%= f.hidden_field :question_id, value: question.id %>
<%= f.text_field :reply, class: 'form-control' %>
<% end %>
<%= f.submit 'Send inn', class: 'btn btn-success' %>
<% end %>
and thus hoping it will allow me to save multiple columns in one, but that doesn't work. It only saves the last column, no matter what.
My answers controller:
class AnswersController < ApplicationController
def new
#questions = current_user.questions
#answer = current_user.answers.new
end
def create
#questions = current_user.questions
#answer = current_user.answers.new(answer_params)
if #answer.save
redirect_to answers_path
else
render 'new'
end
end
private
def answer_params
params.require(:answer).permit(:reply, :question_id)
end
end
What you're looking for is accepts_nested_attributes_for:
This should work:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :answers
has_many :questions, through: :answers
accepts_nested_attributes_for :answers
#this will have to be populated on user create
before_create :build_answers
private
def build_answers
questions = Question.find [1,3,4,6]
questions.each do |question|
user.build_answer(question: question)
end
end
end
#app/models/answer.rb
class Answer < ActiveRecord::Base
#columns id | user_id | question_id | response | created_at | updated_at
belongs_to :user
belongs_to :question
end
#app/models/question.rb
class Question < ActiveRecord::Base
has_many :answers
has_many :users, through: :answers
end
This will give you the ability to do the following:
#config/routes.rb
resources :answers, only: [:edit, :update]
#app/controllers/answers_controller.rb
class AnswersController < ApplicationController
def edit
#questions = current_user.questions
end
def update
#answers = current_user.answers.update answer_params
end
private
def answer_params
params.require(:answer).permit(:response) #-> question_id and user_id set on create, don't need to be changed
end
end
This will allow you to use the following form:
#app/views/answers/edit.html.erb
<%= form_tag answers_update_path, method: :patch do |f| %>
<% #questions.each do |question| %>
<%= f.fields_for "answers[]", question do |qf| %>
<%= qf.label question.title %>
<%= qf.text_field :response %>
<% end %>
<% end %>
<%= f.submit %>
<% end %>
--
Typically, you'd use the accepts_nested_attributes_for with the nested model. However, since you just want multiple answer responses, you can use the above.
The bugs in this would likely be in the strong params, or in the form declaration (IE current_user.questions). If you reply with information, I'll write some upates
Ref: Multiple objects in a Rails form
I have microposts that belong to artists, and everything with that works perfectly.
Now I'm trying to let artists comment on microposts. However, this isn't working how I want it. The comments need to belong to both a specific artist and a specific micropost.
Right now I have a form to create a comment, but it only saves under the most recent micropost id.
### controllers/artists/comments_controller.rb ###
class Artists::CommentsController < ApplicationController
def create
#micropost = ArtistMicropost.find_by(params[:micropost_id])
#comment = #micropost.artist_micropost_comments.build(comment_params)
#comment.artist_id = current_artist.id
end
private
def comment_params
params.require(:artist_micropost_comment).permit(:artist_micropost_id, :artist_id, :content)
end
end
### controllers/artists/artists_controller.rb ###
class Artists::ArtistsController < ApplicationController
def show
#artist = Artist.find(params[:id])
#micropost = ArtistMicropost.new
#micro = ArtistMicropost.find_by(params[:micropost_id])
#comment = ArtistMicropostComment.new
end
end
### views/artists/show.html.erb ###
<% #artist.artist_microposts.each do |micropost| %>
...
<%= micropost.content %>
...
<% #micro.artist_micropost_comments.each do |comment| %>
<%= comment.content %>
<% end %>
<%= form_for(#comment) do |f| %>
<%= f.text_area :content %>
<%= f.submit "post comment" %>
<% end %>
<% end %>
### models/artist.rb ###
class Artist < ActiveRecord::Base
has_many :artist_microposts, dependent: :destroy
has_many :artist_micropost_comments, dependent: :destroy
end
### models/artist_micropost.rb ###
class ArtistMicropost < ActiveRecord::Base
belongs_to :artist
has_many :artist_micropost_comments, dependent: :destroy
end
### models/artist_micropost_comment.rb ###
class ArtistMicropostComment < ActiveRecord::Base
belongs_to :artist_micropost
belongs_to :artist
end
I want it to display each micropost by the artist and underneath each micropost to display the comments that belong to the micropost. I the want the from to display under the comments to add new comments. Basically, I want it to look something like Facebook.
Right now, all the comments are displaying under each micropost no matter what the micropost_id and the create method won't create under any micropost_id, except the most recent one.
So my two problems are:
I can't get the comments to save under the correct micropost_id
I can't get the comments to loop for their micropost.
Any ideas?
Short names are easier to read and understand so I will rename your models in my example to Artist, Micropost and Comment
class Artist < ActiveRecord::Base
has_many :microposts, dependent: :destroy
has_many :comments, through: :microposts, dependent: :destroy
end
class Micropost < ActiveRecord::Base
belongs_to :artist
has_many :comments, dependent: :destroy
end
class Comment < ActiveRecord::Base
# I renamed artist to commenter to make it clear that is not the same artist as the one that created the micropost,
# this implies that instead of author_id you will have commented_id in comments table
belongs_to :commenter, :class_name => Artist
belongs_to :micropost
end
### views/artists/show.html.erb ###
<% #artist.microposts.each do |micropost| %>
...
<%= micropost.content %>
...
<% micropost.comments.each do |comment| %>
# here you display comments for each micropost
<%= comment.content %>
# pay attention at the way I builded the comment
<%= form_for(micropost.comments.build) do |f| %>
<%= f.hidden_field :micropost_id %> # this will make the link to your micropost
<%= f.text_area :content %>
<%= f.submit "post comment" %>
<% end %>
<% end %>
<% end %>
In your comments_controller you must assign current logged in artist (the commenter) to your comment.
class CommentsController < ApplicationController
def create
#comment = Comment.new(comment_params)
#comment.commenter = current_artist
if #comment.save
...
end
end
private
def comment_params
params.require(:comment).permit(:micropost_id, :content)
end
end
To avoid N+1 when you load artists, microposts and commenters do something like this:
class ArtistsController < ApplicationController
def show
#artist = Artist.includes(:microposts, :comments => :commenter).find(params[:id])
end
end
For Your requirement. For creation of microposts.
do something like this.
artist=Artist who is logged in
artist.artist_micro_posts.build(attributes)
artist.save
For creating comments to microposts
micro_posts= Micropost Id
micro_post.artist_micropost_comments.build(:artist_id=logged in person)
micro_post.save
I'm trying to setup the following: A User has many Groups through Memberships, a Group has many Events, and an Event has many Posts.
On my view to show a group with all of its events, I want a user to be able to write a new post by selecting the correct group from a drop down, writing a comment and submit. I'm currently using a collection_select to create the post, but the event_id is not getting passed to ActiveRecord, i.e. posts are created, but they do not have event_ids (or even comments):
class User < ActiveRecord::Base
has_many :memberships
has_many :groups, through: :memberships
has_many :posts
end
class Membership < ActiveRecord::Base
belongs_to :group
belongs_to :user
end
class Group < ActiveRecord::Base
has_many :memberships
has_many :events, dependent: :destroy
has_many :users, through: :memberships
end
class Event < ActiveRecord::Base
belongs_to :group
has_many :posts
end
class Post < ActiveRecord::Base
belongs_to :event
belongs_to :user
end
class GroupsController < ApplicationController
def show
#define new post
#new_post = Post.new
end
end
class PostsController < ApplicationController
def create
if #post = Post.create(params[post_params])
flash[:success] = "Post Created!"
else
redirect_to group_url
end
end
private
def post_params
params.require(:post).permit(:event_id, :comment)
end
end
<h1>New Post:</h1>
<%=form_for([#new_post]) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class = "field">
<%= f.label :event_name %>
<%= f.collection_select(:event_id, Event.all, :id, :title) %>
</div>
<div class = "field">
<%= f.text_area :comment, placeholder: "New Post..." %>
</div>
<%=f.submit "Submit", class: "btn btn-large btn-primary" %>
<%end%>
I have a feeling that because the routes are nested, group_id never is passed to the Posts controller, and so can never be set. But I'm sure there's a lot more wrong than that...
can you try to pass Post.create(post_params) instead of Post.create(params[post_params])
post_params is actually a full hash extracted from the params so you should not pass it to params again
If you want to add user_id
you should add to your view something like this
<%= f.hidden_field :user_id, value: current_user.id %>
When i try to submit the form below i get this error WARNING: Can't mass-assign protected attributes: sub_category.I have tried to go over previous asked related questions here on stackoverflow and seems like i am in the right track ,but for some reason i am still getting the same error,what am i doing wrong?.I have included all the info below, thank you in advance.
View/form
<%= form_for #ip ,:url=>{:action =>"create"} do |f| %>
<%=f.text_field :email %>
<% f.text_field :ip_address %>
<%= f.fields_for :sub_category do |s| %>
<%=s.text_field :name%>
<%end%>
<%=f.submit "submit" %>
<%end%>
Controller
def create
#ips=Ip.new(params[:ip])
#ip=#ips.sub_categories.build
if #ip.save
redirect_to :controller=>"home" ,:action=>"index"
else
render 'index'
end
Models
class Ip < ActiveRecord::Base
has_many :sub_categories ,:through=>:ip_subs
has_many :ip_subs
accepts_nested_attributes_for :sub_categories
attr_accessible :sub_categories_attributes,:ip_address,:email,:ip_count
end
class SubCategory < ActiveRecord::Base
has_many :ip ,:through=>:ip_subs
has_many :ip_subs
end
class IpSub < ActiveRecord::Base
belongs_to :ip
belongs_to :sub_category
end
You should use f.fields_for :sub_categories (association name).
And don't forget to build association before render the form:
# in controller
def new
#ip = Ip.new
#ip.sub_categories.build
end
rubyonrails api :: fields_for