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
Related
any help would be most appreciated, I am rather new to Rails.
I have two models a Shopping List and a Product. I'd like to save/update multiple products to a shopping list at a time.
The suggested changes are not updating the models. I've been googling and is "attr_accessor" or find_or_create_by the answer(s)?
Attempt 1 - Existing code
Error
> unknown attribute 'products_attributes' for Product.
Request
Parameters:
{"_method"=>"patch",
"authenticity_token"=>"3BgTQth38d5ykd3EHiuV1hkUqBZaTmedaJai3p9AR1N2bPlHraVANaxxe5lQYaVcWNoydA3Hb3ooMZxx15YnOQ==",
"list"=>
{"products_attributes"=>
{"0"=>{"title"=>"ten", "id"=>"12"},
"1"=>{"title"=>"two", "id"=>"13"},
"2"=>{"title"=>"three", "id"=>"14"},
"3"=>{"title"=>"four", "id"=>"15"},
"4"=>{"title"=>"five", "id"=>"16"},
"5"=>{"title"=>""},
"6"=>{"title"=>""},
"7"=>{"title"=>""},
"8"=>{"title"=>""},
"9"=>{"title"=>""},
"10"=>{"title"=>""}}},
"commit"=>"Save Products",
"id"=>"7"}
Attempt 2 - no errors the page reloads and none of the expected fields are updated. In earnest, I am Googling around and copying and pasting code snippets in the vain hope of unlocking the right combo.
Added to Products mode
class Product < ApplicationRecord
attr_accessor :products_attributes
belongs_to :list, optional: true
end
<%= content_tag(:h1, 'Add Products To This List') %>
<%= form_for(#list) do |f| %>
<%= f.fields_for :products do |pf| %>
<%= pf.text_field :title %><br>
<% end %>
<p>
<%= submit_tag "Save Products" %>
</p>
<% end %>
<%= link_to "Back To List", lists_path %>
list controller
def update
#render plain: params[:list].inspect
#list = List.find(params[:id])
if #list.products.update(params.require(:list).permit(:id, products_attributes: [:id, :title]))
redirect_to list_path(#list)
else
render 'show'
end
list model
class List < ApplicationRecord
has_many :products
accepts_nested_attributes_for :products
end
original do nothing - product model
class Product < ApplicationRecord
belongs_to :list, optional: true
end
If you just want a user to be able to select products and place them on a list you want a many to many association:
class List < ApplicationRecord
has_many :list_items
has_many :products, through: :list_products
end
class ListItem < ApplicationRecord
belongs_to :list
belongs_to :product
end
class Product < ApplicationRecord
has_many :list_items
has_many :lists, through: :list_products
end
This avoids creating vast numbers of duplicates on the products table and is known as normalization.
You can then select existing products by simply using a select:
<%= form_for(#list) do |f| %>
<%= f.label :product_ids %>
<%= f.collection_select(:product_ids, Product.all, :name, :id) %>
# ...
<% end %>
Note that this has nothing to with nested routes or nested attributes. Its just a select that uses the product_ids setter that's created by the association. This form will still submit to /lists or /lists/:id
You can whitelist an array of ids by:
def list_params
params.require(:list)
.permit(:foo, :bar, product_ids: [])
end
To add create/update/delete a bunch of nested records in one form you can use accepts_nested_attributes_for together with fields_for:
class List < ApplicationRecord
has_many :list_items
has_many :products, through: :list_products
accepts_nested_attributes_for :products
end
<%= form_for(#list) do |f| %>
<%= form.fields_for :products do |pf| %>
<%= pf.label :title %><br>
<%= pf.text_field :title %>
<% end %>
# ...
<% end %>
Of course fields_for won't show anything if you don't seed the association with records. That's where that loop that you completely misplaced comes in.
class ListsController < ApplicationController
# ...
def new
#list = List.new
5.times { #list.products.new } # seeds the form
end
def edit
#list = List.find(params[:id])
5.times { #list.products.new } # seeds the form
end
# ...
def update
#list = List.find(params[:id])
if #list.update(list_params)
redirect_to #list
else
render :new
end
end
private
def list_params
params.require(:list)
.permit(
:foo, :bar,
product_ids: [],
products_attrbutes: [ :title ]
)
end
end
Required reading:
Rails Guides: Nested forms
ActiveRecord::NestedAttributes
fields_for
I'm trying to associate a relation betwen 1 patient with a consultation, but I'm getting a error:
Association :patient not found
In the model I have:
class Patient < ActiveRecord::Base
belongs_to :user
has_many :consultums, through: :patients
end
class Consultum < ActiveRecord::Base
belongs_to :patients
belongs_to :user
has_one :recetum
end
in the controller I have:
class ConsultationController < ApplicationController
before_action :authenticate_user!
before_action :set_consultum, only: [:show, :edit, :update, :destroy]
respond_to :html
def index
#consultation = Consultum.all
respond_with(#consultation)
end
def show
respond_with(#consultum)
end
## when I try to create a new consultation, throw me the error ##
def new
#consultum = Consultum.new
# patient_id
respond_with(#consultum)
end
def edit
end
def create
#consultum = Consultum.new(consultum_params)
#consultum.save
respond_with(#consultum)
end
def update
#consultum.update(consultum_params)
respond_with(#consultum)
end
def destroy
#consultum.destroy
respond_with(#consultum)
end
# def patient_id
# patient = Patient.find(params[:id])
# # patient = #patient.id
# #consultum.patient_id = patient
# end
private
def set_consultum
#consultum = Consultum.find(params[:id])
end
def consultum_params
params.require(:consultum).permit(:Reason_Consultation, :Diagnostic, :TX, :Labs, :Observations, :patient_id)
end
end
so, as you can see I create the function patient_id, and I'm trying to retrieve the id from the 'patient' and put into patient_id in 'consultum' table, but seems not work...
if I uncomment patient_id function throw me this error:
Couldn't find Patient without an ID
I'm stuck, any idea?
Thanks in advance
EDIT
in my view I have:
consultation/new.html.erb
<div class="page-header">
<%= link_to consultation_path, class: 'btn btn-default' do %>
<span class="glyphicon glyphicon-list-alt"></span>
Back
<% end %>
<h1>New consultum</h1>
</div>
<%= render 'form' %>
_form.html.erb
<%= simple_form_for(#consultum) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :Reason_Consultation %>
<%= f.input :Diagnostic %>
<%= f.input :TX, placeholder: "optional" %>
<%= f.input :Labs, placeholder: "optional" %>
<%= f.input :Observations, placeholder: "optional" %>
<%= f.association :patient %>
</div>
<div class="form-actions">
<%= f.button :submit, "Save Consultation" %>
</div>
<% end %>
When creating associations in rails pay super close attention to the pluralization!
class Patient < ActiveRecord::Base
belongs_to :user
has_many :consultums, through: :patients
end
class Consultum < ActiveRecord::Base
belongs_to :patients
belongs_to :user
has_one :recetum
end
You will notice that your Consultum class does not have a patient relation. It has belongs_to :patients.
class Consultum < ActiveRecord::Base
belongs_to :patient
belongs_to :user
has_one :recetum
end
Also your attribute naming should follow the Ruby conventions!
Ruby uses snake_case for naming attributes and methods and will automatically treat any identifier which begins with an UPPERCASE letter as a constant!
This is not just a stylistic issue - MRI Ruby will let you reassign constants with a warning. Other versions or future versions may be less lenient.
Bad Good
.Reason_Consultation .reason_consultation
.Diagnostic .diagnostic
.TX .tx
.Lab .lab
.Observations .observations
https://github.com/bbatsov/ruby-style-guide
To call an action you can pass parameters as follow:
link_to "New Consultum", new_consultum_path(:id => here, yo have to put the paciente id)
Hope this helps!
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'm fairly new to Rails and I've been trying to extend Michael Hartl's tutorial in various ways. One of which is to model user interests using a has_many :through association. I have the following models set up:
class Interest < ActiveRecord::Base
has_many :user_interests, dependent: :destroy
has_many :users, through: :user_interests
end
class User < ActiveRecord::Base
has_many :user_interests, dependent: :destroy
has_many :interests, through: :user_interests
end
class UserInterest < ActiveRecord::Base
belongs_to :user
belongs_to :interest
end
My user_interests controller:
def index
end
def create
#user_interest = current_user.user_interests.build(params[:interest_ids])
if #user_interest.save
redirect_to current_user
flash[:success] = "Interests updated!"
else
render 'index'
end
end
def destroy
#user_interest = UserInterest.find(params[:user_id][:interest_ids])
current_user.user_interests(#user_interest).destroy
end
The view:
<%= form_for(current_user.user_interests.build(params[:interest_ids])) do |f| %>
<%= hidden_field_tag "user[interest_ids][]", nil %>
<% Interest.all.each do |interest| %>
<%= check_box_tag "user[interest_ids][]", interest.id, current_user.interest_ids.include?(interest.id), id: dom_id(interest) %>
<%= label_tag dom_id(interest), interest.activity %><br>
<% end %>
<%= f.submit "Update interests", class: "btn btn-large btn-primary" %>
<% end %>
When I run the app I can select a check box and click the submit button but only the user id is saved in the user_interests table. So it will look like:
id integer user_id interest_id created_at_timestamp updated_at_timestamp
1 2155 2014-04-06 ect... 2014-04-06 ect...
At first I was trying to use the users controller to create the association, but that was causing issues because I didn't have the interests check boxes displayed on the users#edit action, I wanted them to have their own page. What do I need to do to get the interest ids to save to the user_interests table along with the user id?
Please have a try with the following code.
def create
interests = Interest.where(id: params[:user][:interest_ids])
current_user.interests.push(interests)
flash[:success] = "Interests updated!"
redirect_to current_user
end
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 %>