Rails rendering nested comments use eager loading - ruby-on-rails

I wanted to be able to allow users to add comments to recipes. I also wanted users to be able to comment on those comments. It appears to be working, but maybe there are too many queries going on. I have researched and seen a lot of closely related articles, but none seem to be helping the issue. No matter how I change it up, it doesn't work. I can make as many comments on comments as I want, but as soon as there are multiple comments to a recipe it crashes. It's like I can have one or the other, but not both or it will crash. Here is what I have so far:
comment.rb
class Comment < ApplicationRecord
belongs_to :commentable, polymorphic: true
has_many :comments, as: :commentable
end
recipe.rb
class Recipe < ApplicationRecord
has_many :comments, as: :commentable
recipes/show.html.erb
<div class="">
<h5>Comments:</h5>
<div class="comment-form">
<hr />
<h3 class="subtitle is-3">Leave a reply</h3>
<%= render #recipe.comments %>
</div>
<%= simple_form_for([#recipe, #recipe.comments.build]) do |f| %>
<div class="field">
<div class="control">
<%= f.input :content, input_html: { class: 'input' }, wrapper: false, label_html: { class: 'label' } %>
</div>
</div>
<%= f.button :submit, 'Leave a reply', class: "button is-primary" %>
<% end %>
</div>
_comments.html.erb
<div class="box">
<article class="media">
<div class="media-content">
<div class="content">
<p>
<strong><%= comment.content %></strong>
</p>
</div>
</div>
</article>
</div>
<div>
<div class="">
<%= form_for([comment, comment.comments.build]) do |f| %>
<%= f.hidden_field :recipe_id, value: #recipe.id %>
<%= f.text_area :content, placeholder: "Add a Reply" %><br/>
<%= f.submit "Reply" %>
<% end %>
</div>
<div>
<%= render comment.comments %>
</div>
</div>
If I remove <%= render comment.comments %> It works, but obviously will not show me the comments on other comments. If I make only 1 comment on a recipe, I can comment on that comment as many times as I want with no issues. If I add just one more comment on the recipe, it crashes. If I pry in, it works and shows every comment is there until it is done going through each comment, then crashes. I know there are gems for this, but I am learning and really wanted to build from scratch and understand what is going on. Thanks in advance!

I think if you made a self referential relationship on your Comment model it would work. You'd need to add a parent_id or whatever you want to call it. You can keep the polymorphic owner in case you have other models that will be able to have comments.
# Migration
def change
add_column :comments, :parent_id, :integer, foreign_key: true
add_index :comments, :parent_id
end
And then in your Comment model:
class Comment < ApplicationRecord
belongs_to :commentable, polymorphic: true
belongs_to :parent, class_name: 'Comment', inverse_of: :replies
has_many :replies, foreign_key: :parent_id, class_name: 'Comment', inverse_of: :parent
end
If you're using Rails 5, you'd need to add optional: true to the belongs_to :parent relationship.

Related

Nested form to create a number of new associated objects in a 'has_many through' many to many relationship

I have a many to many relationship between DailyQuestionSets and Questions. I have a one to many relationship between Questions and Answers. What I am having trouble working out is how to build the right kind of nested form that could retrieve a set of Questions with a label showing the question text as well as a form field with the answer which would get posted to the Answer model.
Models:
class Question < ApplicationRecord
has_many :dailyquestions, foreign_key: 'dailyquestion_id'
has_many :dailyquestionsets, :through => :dailyquestions
end
class DailyQuestion < ApplicationRecord
belongs_to :daily_question_set
belongs_to :question
end
class DailyQuestionSet < ApplicationRecord
has_many :daily_questions, foreign_key: 'question_id'
has_many :questions, :through => :daily_questions, :source => :question
end
class Answer < ApplicationRecord
belongs_to :users
belongs_to :questions
belongs_to :dailyquestionset
end
View:
<p id="notice"><%= notice %></p>
<%= form_tag('/answers/answerquestions') do %>
<% #questions.each do |q| %>
<%= fields_for '#answers[]', Answer do |a| %>
<div class="field">
<%= q.label :content %><br>
<%= a.text_field :answer_text %>
</div>
<% end %>
<% end %>
<div class="actions">
<%= submit_tag %>
</div>
<% end %>
Controller:
def answerquestions
#dailyquestionset = DailyQuestionSet.get_today_dailyquestionset
#questions = #dailyquestionset.questions
#answers = []
#questions.count.times do
#answers << Answer.new
end
The problem is that I'm not able to get a label value out of q (Question) in the view and this is no surprise as I have taken a wild guess as to how to construct this nested form syntax.
undefined method `label' for #<Question:0x00007f54383ce748>
Extracted source (around line #8):
<div class="field">
<%= q.label :content %><br>
<%= a.text_field :answer_text %>
</div>
Any ideas on how to handle the form correctly most appreciated :)
It seems you are trying to create a text field and a related label with text taken from the Questions's content property:
<div class="field">
<%= a.label :answer_text, q.content %><br>
<%= a.text_field :answer_text %>
</div>
i.e. label is a method on the FormBuilder and the desired text (here q.content) is passed as second argument. See documentation.

Double nested association w/ Cocoon and Polymorphism gets rejected

So, I have a model Label which is polymorphic and another model Stuff which is not. A stuff can have many labels (may also called groups), and each and every one of them can also have a label. I am working with the Cocoon gem to try to add these resource on to a single edit/new form (the Stuff one). The problem is that when I try to update the stuff with a new group (with new labels in it), it says this Labels labels labelable must exist. I think that that's an error given because the first label(group) is not yet saved to the database, so it can't give his nested label an id. (not sure though)
Also, is this the best way to do that? I mean, I made the label polymorphic only because I needed to save a only a string, and it would be unpractical and it would have taken database storage for nothing...
Enough talking, here's my code:
<div class="form-group">
<label>Groups:</label>
<div id="group" class="col-md-12 p-0 pl-md-3">
<%= form.fields_for :labels do |groupForm| %>
<%= render 'group_fields', f: groupForm %>
<% end %>
<div class="text-center">
<%= link_to_add_association 'Add Group', form, :labels, partial: 'group_fields', class: 'btn btn-success',
wrap_object: Proc.new { |group| group.labels.build; group } %>
</div>
</div>
</div>
that's in my _form.html.erb
<div class="nested-fields">
<div class="field form-group row">
<%= f.label :name, class: 'col-sm-1 col-form-label' %>
<div class="col-sm-10">
<%= f.text_field :name, class: 'form-control ml-2' %>
</div>
<div class="col-sm-1 p-0">
<%= link_to_remove_association "×".html_safe, f, class: 'badge badge-pill badge-danger mt-2' %>
</div>
<div class="col-12">
<div id="label" class="col-md-12 p-0 pl-md-5 pt-2">
<%= f.fields_for :labels do |labelForm| %>
<%= render 'label_fields', f: labelForm %>
<% end %>
<div class="text-center">
<%= link_to_add_association 'Add Label', f, :labels, class: 'btn btn-success' %>
</div>
</div>
</div>
</div>
</div>
that's in _group_fields.html.erb
<div class="nested-fields">
<div class="field form-group row mb-2">
<%= f.label :name, class: 'col-sm-1 col-form-label' %>
<div class="col-sm-10">
<%= f.text_field :name, class: 'form-control ml-2' %>
</div>
<div class="col-sm-1 p-0">
<%= link_to_remove_association "×".html_safe, f, class: 'badge badge-pill badge-danger mt-2' %>
</div>
</div>
</div>
and that's in my _label_fields.html.erb
class Label < ApplicationRecord
belongs_to :labelable, polymorphic: true
has_many :labels, as: :labelable, dependent: :destroy
accepts_nested_attributes_for :labels, allow_destroy: true, reject_if: proc { |att| att[:name].blank? }
end
this is my Label model
class Stuff< ApplicationRecord
has_many :labels, as: :labelable, dependent: :destroy
accepts_nested_attributes_for :labels, allow_destroy: true, reject_if: proc { |att| att[:name].blank? }
end
and this is my Stuff model
I forgot to mention that if I add only the first "layer" of label (group) without writing anything on the labels (2nd "layer") and I submit the form (which I can do and it updates the database as well) when I come back and edit I can actually modify the 2nd "layer" without any problems.
A belongs_to relation-ship is by default "required". This means it has to be filled in upon saving. However, when saving an item with nested childs, no id's are yet known and while obvious, rails has no way of knowing the belongs-to association will be set when saving them together.
There are two ways to handle this:
make the belongs_to optional, this will skip the validation, and the save will work. Something like belongs_to :labelable, polymorphic: true, optional: true
better, declare the inverse_of so rails knows when saving the labels it corresponds to the labelable relationship.
Like so:
has_many :labels, as: :labelable, dependent: :destroy, inverse_of: :labelable
You can declare the inverse_of on any assocation, so also on the belongs_to, but in most it suffices to just declare it on the has_many. It might not be enough in your specific scenario.

How to use a form from another controller to set a value that has a has_many :through relation in Rails4?

Here's my current situation:
I have a model :companies and a model :users(Devise). A company can have many moderating users through a third model :moderator_connections and vice versa. To select a current moderator, I've added the column current_company (not referenced) to the model :users.
I want to add a dropdown_menu under my main menu throughout the website. By selecting a current company from this menu, the user can switch to that company's content. I tried to do this by rendering a form-partial above the <%= yield %> in my application.html.erb. The form tries to edit the current_company integer (by changing it to the company_id of one of the connected companies) for the current_user, but I think I'm not even close to solving it :(
Here are my relations.
/models/company.rb:
has_many :moderator_connections
has_many :moderators, through: :moderator_connections, class_name: 'User', foreign_key: 'company_id'
/models/user.rb:
has_many :moderator_connections
has_many :moderated_companies, through: :moderator_connections, class_name: 'Company', foreign_key: 'user_id'
/models/moderator_connection.rb:
belongs_to :user
belongs_to :company
/layouts/application.html.erb:
<main>
<div class="container">
<%= render partial: "users/current_company_form" %>
<%= render partial: "shared/message" %>
<%= yield %>
</div>
</main>
/users/_current_company_form.html.erb:
<% if user_signed_in? %>
<%= form_for edit_user_registration_path do |f| %>
<div class="form-group">
<%= f.collection_select :current_company, current_user.moderator_connections(:company_id), :name, :id,{ prompt: "Choose a company" } %>
<%= f.hidden_field :current_company, :value => current_user.moderator_connections(:company_id) %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
It already goes wrong with the rendering of the partial, I receive the following error (depending on the page I'm in): No route matches [POST] "/pages/welcome"
To be honest, I have no idea how to create this (as you can conclude from my foolish attempts). Can anyone help me out?
I'm not totally sure, but I think this should work:
<% if user_signed_in? %>
<%= form_for current_user, url: edit_user_registration_path do |f| %>
<div class="form-group">
<%= f.collection_select(:current_company, current_user.moderated_companies, :id, :name, prompt: "Select a company") %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<% end %>
Let me know if that works, though!
UPDATE
I think you should change to use:
#user.rb
has_many :moderated_companies, through: :moderator_connections, source: :company
#company.rb
has_many :moderators, through: :moderator_connections, source: :user

Rails - Simple Form & Nesting models

I have three models in my Rails 4 app.
I have a projects, project questions and a project answers model.
Projects
has_many :project_questions, dependent: :destroy#, through: :projects
accepts_nested_attributes_for :project_questions
Project questions has these associations:
belongs_to :project#, counter_cache: true
has_many :project_answers, dependent: :destroy
belongs_to :user
accepts_nested_attributes_for :project_answers
Project answers associations are:
belongs_to :project_question#, counter_cache: true
belongs_to :user
My routes.rb has:
resources :projects do
# patch '/toggle-draft', to 'projects#toggle_draft', as: 'toggle_draft'
resources :project_questions do
resources :project_answers
end
end
In my projects_controller, I have permitted params for project questions and answers as follows:
project_question_attributes: [:title, :content, :user_id, :project_id,
project_answer_attributes: [:answer, :project_question_id]],
These params are also permitted in the Project questions and project answers controllers.
In my projects view, I want to render a partial that I have made in my project_questions view folder.
In projects show page, I have:
<%= link_to 'Ask a question', new_project_question_path %> <% end %>
<%= render 'project_questions/pqps' %>
In my project_questions partial which is called _pqps, I have;
<div class="containerfluid">
<div class="row">
<div class="col-md-10 col-md-offset-1">
<% f.simple_fields_for :project_questions, #project.project_questions.build do |q| %>
<div class="categorytitle">
<%= q.title %>
</div>
<div class="generaltext">
<%= q.content %>
</div>
<%= render 'project_answers/show' %>
<span class="editproject"> <% if current_user.id == #project.creator_id %>
<%= link_to 'Answer this question', new_project_answer_path %>
<% end %>
</span>
<% end %>
</div>
</div>
</div>
However, when I try this, I get an error that says:
undefined local variable or method `new_project_question_path'
Can anyone see what i've done wrong?
Thank you

Rails, Simple Form, Nested Forms

class Project
has_many :project_questions, dependent: :destroy#, through: :projects
accepts_nested_attributes_for :project_questions
end
I am trying to make an app with rails 4 and Simple Form.
I have models called projects, project_questions and project_answers.
The associations between them are:
Projects:
has_many :project_questions, dependent: :destroy#, through: :projects
accepts_nested_attributes_for :project_questions
Project questions:
belongs_to :project#, counter_cache: true
has_many :project_answers, dependent: :destroy
belongs_to :user
accepts_nested_attributes_for :project_answers
belongs_to :user
Project answers:
belongs_to :project_question#, counter_cache: true
belongs_to :user
User:
has_many :project_questions
has_many :project_answers
class ProjectQuestions
belongs_to :project#, counter_cache: true
has_many :project_answers, dependent: :destroy
belongs_to :user
accepts_nested_attributes_for :project_answers
end
ProjectAnswer:
belongs_to :project_question#, counter_cache: true
belongs_to :user
My objective is to have a partial on my projects show page which displays the questions and their answers relating to the project and to have a link to another form where you can ask a question or answer one. I am struggling.
In my controllers I have:
Projects:
def project_params
params.require(:project).permit(project_question_attributes: [:title, :content, :user_id, :project_id,project_answer_attributes: [:answer, :project_question_id]],
Project_question:
def new
#project_question = ProjectQuestion.new
#project_id = params[:project_id]
#project_question.project_answers[0] = ProjectAnswer.new
end
Project_answer:
def new
#project_answer = ProjectAnswer.new
end
def project_question_params
params[:project_question].permit(:id, :title, :content, :project_id, :user_id,project_answer_atttibutes: [:id, :answer, :project_question_id, :user_id])
end
Then in my views I have:
Project#show:
<% if current_user.id == #project.creator_id %>
<%= link_to 'Ask a question', new_project_project_question_path(:project_id => #project.id) %>
<% end %>
<%= render 'project_questions/pqps' %>
Project_question#pqps (a partial):
<div class="containerfluid">
<div class="row">
<div class="col-md-10 col-md-offset-1">
<div class="categorytitle">
<%= #project_question.try(:title) %>
</div>
<div class="generaltext">
<%= #project_question.try(:content) %>
</div>
<span class="editproject">
<% if current_user.id == #project.creator_id %>
<% end %>
</span>
</div>
</div>
</div>
My project_questions form partial is:
<div class="containerfluid">
<div class="row">
<div class="col-md-10 col-md-offset-1">
<% f.simple_fields_for :project_questions do |f| %>
<%= f.input :project_id, as: :hidden, input_html: {value: #project_question_id} %>
<%= f.input :title, label: 'Question:', :label_html => {:class => 'categorytitle'}, placeholder: 'Type your question here', :input_html => {:style => 'width: 100%', class: 'categorytitle'} %>
<%= f.input :content, label: 'Is there any context or other information?', :label_html => {:class => 'categorytitle'}, placeholder: 'Context might help to answer your question', :input_html => {:style => 'width: 100%', rows: 5, class: 'generalprojecttext'} %>
<%= f.button :submit %>
<% end %>
</div>
</div>
</div>
Project_answer#pa (a partial):
(not yet written - still trying to get the questions to work)
My routes are:
resources :projects do
resources :project_questions do
resources :project_answers
end
end
When I try this, I get an error in my project_questions form that says:undefined local variable or methodf' for #<#:0x000001013d8298>`
I can't see where it is talking about f outside the form block. I have tried adding another line to the form for simple_fields for project and then nesting the project questions form inside of it, but that doesn't change anything.
Can anyone see what I've done wrong?
Thank you
You should change this line <% f.simple_fields_for :project_questions do |f|
%> to this <%= simple_form_for :project_questions do |f| %> in order to make it work.
The plataformatec simple_form wiki has a pretty good instructional page on using nested forms.
(Just as a side note, I find that it's often easier to diagnose problems without using partials. Once everything is working, you can clean-up by refactoring into partials.)
I believe you need to pass a local variable to your partial, so it knows what the first "f" refers to.
<%= render 'project_questions/pqps', locals: {f: f} %>
(You also may choose to use a different local variable in the partial for project_questions, just so you're always clear what is being referenced in error messages.)

Resources