Unable to Destroy record. No association found - ruby-on-rails

I am a new student learning about Ruby on Rails. For my current assignment I have to build a Question feature. Everything works but the deletion of a "Question".
I am trying to follow the Form Helper One to Many. Thanks for looking.
Instructions
Complete QuestionsController and its corresponding views. Accept input for resolved in the Question form using a checkbox.
Test your changes in the browser. Confirm that you can:
see an index of all questions,
view an individual question,
create new questions,
edit and update questions, and
delete questions.
When I go to edit a question and then proceed to delete it with the checkbox this error is thrown:
No association found for name `question'. Has it been defined yet?
Here is my console output:
Started GET "/questions/1/edit" for ::1 at 2015-04-23 15:42:11 -0400
Processing by QuestionsController#edit as HTML
Parameters: {"id"=>"1"}
Completed 500 Internal Server Error in 2ms (ActiveRecord: 0.0ms)
ArgumentError (No association found for name `question'. Has it been defined yet?):
app/models/question.rb:3:in `<class:Question>'
app/models/question.rb:1:in `<top (required)>'
app/controllers/questions_controller.rb:27:in `edit'
questions_controller.rb
class QuestionsController < ApplicationController
def index
#questions = Question.all
end
def show
#question = Question.find(params[:id])
end
def create
#question = Question.new(params.require(:question).permit(:title, :body, :resolved))
if #question.save
flash[:notice] = "Question was saved."
redirect_to #question
else
flash[:error] = "There was an error saving the question. Please try again."
render :new
end
end
def new
#question = Question.new
end
def edit
#question = Question.find(params[:id])
end
def update
#question = Question.find(params[:id])
if #question.update_attributes(params.require(:question).permit(:title, :body, :resolved, :_destroy))
flash[:noteice] = "Question was updated."
redirect_to #question
else
flash[:error] = "There was an error saving the question. Please try again."
render :edit
end
end
end
question.rb
class Question < ActiveRecord::Base
has_many :answers
accepts_nested_attributes_for :question, allow_destroy: true
end
edit.html.erb
<div class="col-md-8">
<%= form_for #question do |f| %>
<div class="form-group">
<%= f.label :title %>
<%= f.text_field :title, class: 'form-control', placeholder: "Enter question title" %>
</div>
<div class="form-group">
<%= f.label :body %>
<%= f.text_area :body, rows: 8, class: 'form-control', placeholder: "Enter question body" %>
</div>
<div class="form-group">
<%= f.label :resolved %>
<%= f.check_box :resolved %>
</div>
<div class="form-group">
<%= f.label :destroy %>
<%= f.check_box :_destroy %>
</div>
<div class="form-group">
<%= f.submit "Save", class: 'btn btn-success' %>
</div>
<% end %>
Edit: added my answers.rb.
answers.rb
class Answer < ActiveRecord::Base
belongs_to :question
end

Nested attributes are for the relation. Your Question has many Answers. The point of accepts_nested_attributes_for is that it lets you update "nested" associations from the parent model. The argument is the name of the association to accept attributes for. In this case, your questions should accept nested attributes for their answers:
class Question < ActiveRecord::Base
has_many :answers
accepts_nested_attributes_for :answers
end
See the documentation, where the example class Book accepts nested attributes for author and pages. A Book does not accept nested attributes for a book, similarly your question should not accept nested attributes for a question.
As far as deleting the question, you cannot use accepts_nested_attributes to point a model back to itself so that _destroy will destroy the parent model.
You'll have to check for the presence of that attribute and destroy the record in your controller.

The problem is the accepts_nested_attributes_for line in the Question class.
You wrote
accepts_nested_attributes_for :question
but it likely should be
accepts_nested_attributes_for :answers
since that is the name of the has_many relationship. Rails is barking because there is no such relationship called :question inside the Question class. Your class should look like this:
class Question < ActiveRecord::Base
has_many :answers
accepts_nested_attributes_for :answers, allow_destroy: true
end

You need in answers.rb
belongs_to :question
in order to complete the association

Related

Simple_form_for many to many with validation

Setup
I have a simple many to many relationship between a Submit and an Answer through SubmitAnswer.
Answers are grouped by a Question (in my case each question has three answers) - think of it as a multiple choice quiz.
I have been trying to use SimpleFormFor to make a form which renders a predetermined set of questions, where each question has a predetermined set of answers.
Something like this:
#form
<%= simple_form_for Submit.new, url: "/questionnaire" do |f| %>
<% #questions.each do |question| %>
<%= f.association :answers, collection: question.answers %>
<% end %>
<%= f.submit :done %>
<% end %>
#controller
def create
#submit = Submit.new(submit_params)
#submit.user = current_user
if #submit.save
redirect_to root_path
else
render :new
end
end
def submit_params
params.require(:submit).permit(answer_ids: [])
end
When I submit the form, Rails creates the join table, SubmitAnswers, automatically.
So here is the crux of the matter: Whats the easiest way to re-render the form, errors and all, if not all questions have been answered, ie if #submit.answers.length != #question.length ?
I can add a custom error with errors.add(:answers, 'error here'), but when I re-render, the correctly selected answers arent repopulated, which is suboptimal.
For completions sacke, here are my models:
class Submit < ApplicationRecord
belongs_to :user
has_many :submit_answers
has_many :answers, through: :submit_answers
end
class SubmitAnswer < ApplicationRecord
belongs_to :submit
belongs_to :answer
end
class Answer < ApplicationRecord
has_many :submit_answers
has_many :submits, through: :submit_answers
end
Alright, after some digging we did find the answer to make the form work, albeit with more pain that we anticipated a simple many-to-many should take.
#model
class Submit < ApplicationRecord
belongs_to :user
has_many :submit_answers
has_many :answers, through: :submit_answers
accepts_nested_attributes_for :submit_answers
end
#controller
def new
#submit = Submit.new
#questions.count.times { #submit.submit_answers.build }
end
def create
#submit = Submit.new(submit_params)
#submit.user = current_user
if #submit.save
redirect_to root_path
else
render :home
end
end
def submit_params
params.require(:submit).permit(submit_answers_attributes:[:answer_id])
end
#form
<%= simple_form_for #submit do |f| %>
<%= f.simple_fields_for :submit_answers do |sa| %>
<%= sa.input :answer_id, collection: #answers[sa.options[:child_index]], input_html: { class: "#{'is-invalid' if sa.object.errors.any?}"}, label: #questions[sa.options[:child_index]].name %>
<div class="invalid-feedback d-block">
<ul>
<% sa.object.errors.full_messages.each do |msg| %>
<li> <%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.submit :done %>
<% end %>
The solution is to use simple_fields_for/fields_for. Note that <%= sa.input :answer_id %> must be :answer_id, not :answer, which is something I had tried before.
Also one must allow accepts_nested_attributes_for :submit_answers, where :submit_answers is the join_table.
I prebuild my SubmitAnswers like so: #questions.count.times { #submit.submit_answers.build } which generates an input field for each question, all of which get saved on the form submit, a la build.
For the strong_params one needs to permit the incoming ids:
params.require(:submit).permit(submit_answers_attributes:[:answer_id]), so in this case submit_answers_attributes:[:answer_id].
For anyone wondering what the params look like:
{"authenticity_token"=>"[FILTERED]",
"submit"=>
{"submit_answers_attributes"=>
{"0"=>{"answer_id"=>""}, "1"=>{"answer_id"=>""}, "2"=>{"answer_id"=>""}, "3"=>{"answer_id"=>""}, "4"=>{"answer_id"=>""}, "5"=>{"answer_id"=>""}, "6"=>{"answer_id"=>""}}},
"commit"=>"done"}
As for the errors, im sure there might be a better way, but for now I have just manually added them with input_html: { class: "#{'is-invalid' if sa.object.errors.any?}"}.
On a final note, the sa.object # => SubmitAnswer allows me to retrieve the Model, the errors of that Model or whatever else one might want.

Ruby on rails Validation does not work for Form objects that contain Action text, etc

Error Overview
There is a Github URL of a simple app that reproduces the problem in the bottom paragraph.
I'm creating an application that allows article submission. To make it easier to submit articles, I introduced Rails Action text. I also decided to add a tagging feature, so I have a tags table, an article table, and a user table in the database. The article form looks like the following image.
The article form
I was able to submit an article, but I wanted to check the validation of the condition that the article could not be submitted with a blank field, so I submitted it with a blank field, but I got the following error.
NoMethodError
I don't know how to solve this problem and I need your help.
The relevant source code
This is the view file of the corresponding section.↓
<%= render "shared/header" %>
<%= form_with model: #article, url: articles_path, class:'form-wrap', local: true do |f| %>
<%= render 'shared/error_messages', model: f.object %>
<div class='article-form'>
<div class="title-field">
<%= f.label :title, "題名" %>
<%= f.text_area :title, class:"input-title" %>
</div>
<div class="tag-field", id='tag-field'>
<%= f.label :name, "タグ" %>
<%= f.text_field :name, class:"input-tag" %>
</div>
<div class="content-field">
<%= f.label :content, "記事本文" %>
<%= f.rich_text_area :content %>
</div>
<div id="search-result">
</div>
</div>
<div class="submit-post">
<%= f.submit "Send", class: "submit-btn" %>
</div>
<% end %>
This is the controller code.↓
class ArticlesController < ApplicationController
before_action :authenticate_user!, only: [:new, :create]
def index
#article = Article.all.order(created_at: :desc)
end
def new
#article = ArticlesTag.new
end
def create
#article = ArticlesTag.new(article_params)
if #article.valid?
#article.save
return redirect_to root_path
else
render :new
end
end
def search
return nil if params[:keyword] == ""
tag = Tag.where(['name LIKE ?', "%#{params[:keyword]}%"] )
render json:{ keyword: tag }
end
private
def article_params
params.require(:articles_tag).permit(:title, :content, :name).merge(user_id: current_user.id)
end
end
This is the code of the Article model.↓
class Article < ApplicationRecord
has_rich_text :content
belongs_to :user
has_one_attached :image
has_many :article_tags
has_many :tags, through: :article_tag_relations
end
This is the code of The Tag model. ↓
class Tag < ApplicationRecord
has_many :article_tag_relations
has_many :articles, through: :article_tag_relations
validates :name, uniqueness: true
end
This is the intermediate model between The Tag model and The Article model.↓
class ArticleTagRelation < ApplicationRecord
belongs_to :article
belongs_to :tag
end
This is the Form object class that collects the tags and articles tables.
class ArticlesTag
include ActiveModel::Model
attr_accessor :title, :name, :content, :user_id
with_options presence: true do
validates :title
validates :name
validates :content
validates :user_id
end
def save
article = Article.create(title: title, content: content, user_id: user_id)
tag = Tag.where(name: name).first_or_initialize
tag.save
ArticleTagRelation.create(article_id: article.id, tag_id: tag.id)
end
end
Database Status
Action text table
Article Tag Relation table
Article table
Tag table
Please help me.
A simple application that reproduces the error.
github URL
A simple app that reproduce the error
This is the error you are getting
undefined method `body' for "":String
That says that it is trying to call the method body on an empty string.
Why is it trying to call that method? Because you wrote this:
<%= f.rich_text_area :content %>
So the form helper is expecting that content contains an instance of a RichText (https://api.rubyonrails.org/classes/ActionText/RichText.html)
However, content actually just contains an instance of String, because your ArticlesTag model does not declare that it has_rich_text (like your Articles Model does)
I note that your ArticlesTag model is not persisted. I am not sure how to use rich text with a model that is not persisted - I suspect it might not be possible.

build method not working for multiple fields on rails

I have two models.
Question.rb
Class Question
belongs_to :quiz
has_many :possible_answers
end
PossibleAnswer.rb
Class possible_answer
belongs_to :question
end
I am trying to add multiple possible answers to a question by doing these changes to questions controller and form.
questions_controller.rb
def new
#question = #quiz.questions.build
5.times { #question.possible_answers.build }
end
_form.html.erb
<p>
<label>Specify some choices:</label>
</p>
<%= f.fields_for :possible_answers do |c| %>
<p>
<%= c.text_field :title, placeholder: "Type your choice", class: "form-control" %>
</p>
<% end %>
By what I read it should give 5 fields to enter possible answers, but instead still giving single field. Can anybody please help me out here ?
I think you must do it this way:
def new
#question = #quiz.questions.build
5.times { #question.possible_answers << Possible_answer.build }
end

Nested Forms with find_or_create_by method

I've been stuck on this issue all day now and I'm fairly certain there is an easy fix that I am just not seeing due to my inexperience. A bit of background on what I'm trying to do before I discuss my problem. I have a model called Companies that can have many Locations. Similarly, a location can have multiple Companies. Because of this I created a has_many :through relationship.
class Company < ApplicationRecord
has_many :company_locations
has_many :locations, :through => :company_locations
accepts_nested_attributes_for :company_locations
accepts_nested_attributes_for :locations
end
class Location < ApplicationRecord
has_many :company_locations
has_many :companies, :through => :company_locations
end
class CompanyLocation < ApplicationRecord
belongs_to :company
belongs_to :location
end
Because of this structure, when a Company's location is created/updated I want to check whether this location (by name) exists. If it does, I use form the association between the Company and that Location. If it does not, the location is created and then the association is created. It is my understanding that the best way to do this is through a find_or_create_by method. However, the various ways I have tried do not seem to be creating this functionality.
Right now just to get something working my view for new Companies is this:
<h1> Add Company </h1>
<%= form_for :company, url: companies_path do |f| %>
<p>
<%= f.label :name %><br>
<%= f.text_field :name%>
</p>
<p>
<%= f.label :website %><br>
<%= f.text_field :website%>
</p>
<p>
<%= f.label :description %><br>
<%= f.text_area :description %>
</p>
<%=fields_for :locations do |location_form|%>
<%= location_form.label :name, 'Location' %>
<%= location_form.text_field :name %>
<%end%>
<p>
<%= f.submit %>
</p>
<% end %>
Now inside the create action in my Companies controller is where I am experiencing difficulties. As said before, I want to check if the location that is being added to the company already exists or not. Because of this, I am using a find_or_create_by method. However, I cannot seem to figure out how to properly handle the strong params/slice the params in a way to make this work without error.
def new
#company = Company.new
#company_locations = #company.company_locations.build
#location = #company_locations.build_location
end
def create
#company = Company.new(company_params)
#location = Location.find_or_create_by(name: (company_params.slice(:location_attributes[0][:name)))
#company.locations << #location
#company.save
redirect_to #company
end
private
def company_params
params.require(:company).permit(:name,:website, :description, location_attributes: [:name])
end
Currently, I'm getting an error saying 'no implicit conversion of Symbol into Integer' which leads me to believe that I am accessing the hash wrong, however, any other method I have tried results in the a Location being created with "NULL" set as the name. I'm really stumped on this one, and to completely honest I'm not sure I am approaching this nested form correctly. In the future, I hope to use JQuery/Javascript/Cocoon to be able to dynamically add fields in the form to add more locations at once. I've been trying to follow other Stack Overflow posts and forums to no avail. Any help/guidance is much appreciated! Thank you.
UPDATE
Still stuck on this one. Here is the params hash from the server log for an example company:
{"utf8"=>"✓",
"authenticity_token"=>"xpDOq6D5YRZ3VUXLBLgu8SfRIkRXgMQHXIRUtArNp1smtXShB/i54fQQVEHgqy64kdj1R+u0t/JVihLCXQVZpg==",
"company"=>{"name"=>"Google", "website"=>"www.google.com", "description"=>"Google is a search engine."},
"locations"=>{"name"=>"Mountain View"},
"commit"=>"Save Company"}
UPDATE 2
Params hash after Pavan's suggestions.
{"utf8"=>"✓",
"authenticity_token"=>"xJHZVYSfHmjR3BOMS49yRzwD35NV5F7uyCou8yOmtKAktGNfI57Gn1KZAgavnHIOigoIkOnQLRvBJGiFdG5KXQ==",
"company"=>{"name"=>"Logitech", "website"=>"www.logitech.com", "description"=>"This is logitech"},
"locations"=>{"name"=>"Chicago"},
"commit"=>"Save Company"}
UPDATE 3
Changed the form for tag to:
<%= form_for #company, url: companies_path do |f| %>
This is the new updated params hash:
{"utf8"=>"✓",
"authenticity_token"=>"UiiwNXzOiDqZd0Vv1dDu4jkyRQU4e9LKLixqKH+rvKCyDQo/289QzRoyVOUxw+6rjzuSBoRPoT8nIixeKGNCXQ==",
"company"=>{"name"=>"Seagate", "website"=>"www.seagate.com", "description"=>"This is seagate"},
"locations"=>{"name"=>"Los Angeles "},
"commit"=>"Create Company"}
I see a couple of mistakes
You need to build the associations correctly
def new
#company = Company.new
#company_locations = #company.company_locations.build
#location = #company.locations.build #since you have defined nested attributes in company
end
Change location_attributes to locations_attributes
def company_params
params.require(:company).permit(:name,:website, :description, locations_attributes: [:name])
end
And try using company_params.slice(:locations_attributes[0][:name])

Rails 4, saving attributes nested

I have a model Job. A job require many skills. But when I'm trying to save my job it fails. I'm not sure I understand what I'm doing wrong.
My models:
class Skill < ActiveRecord::Base
has_many :job_skills
has_many :jobs, through: :job_skills
end
class JobSkill < ActiveRecord::Base
belongs_to :skill
belongs_to :job
end
class Job < ActiveRecord::Base
has_many :job_skills, :inverse_of => :job
has_many :skills, through: :job_skills
accepts_nested_attributes_for :job_skills
end
My view:
<%= form_for #job do |f| %>
<div class="row">
<div class="col-md-8">
<h4>General informations</h4>
<br />
<div class="form-group">
<%= f.label :title %>
<%= f.text_field :title, :autofocus => true, class:'form-control' %>
</div><br />
<%= f.fields_for :job_skills do |s| %>
<%= s.text_field :id %>
<% end %>
</div>
</div>
<div class="submit" style="position:relative;">
<%= f.submit "Save", class: 'button button-small' %>
</div>
<% end %>
My controller:
class JobsController < ApplicationController
before_filter :authenticate_user!, :has_company?, :except => [:index, :show]
def create
#job = Job.new(job_params)
#job.company_id = current_user.company_id
#job.user_id = current_user.id
if #job.save
flash[:notice] = "This job offer has been saved."
return redirect_to job_path(#job)
else
flash[:error] = #job.errors.full_messages.to_sentence
render action: :new
end
end
def new
if current_user.company.present?
#job = Job.new(email:current_user.email)
#job.job_skill.build
else
flash[:error] = "You need to create a company before posting a job"
return redirect_to new_company_path()
end
end
private
def job_params
params.require(:job).permit(:status, :title, :description, :remote ,:job_type, :visa_sponsor, :email, :salary_max, :salary_min, :country, :state, :city, job_skill_attributes: [:id])
end
end
So, I'm not sure what I'm doing wrong, when I'm trying to save I get the following error:
#job = Job.new(job_params)
ActiveRecord::RecordNotFound Exception:
Couldn't find Skill with ID=4 for Job with ID= nil
Your pieces of code are a bit confusing for me.
It seems, that you want to create a job and define, what skills are needed for this job.
Why do you need nested attributes?
Normally, you
either edit a list of all the skills, that are probably needed for a job and then assign the propper skills to that job, than you have a has_and_belongs_to_many relationship and can use form helpers for collections. In this case, you don't need a model JobSkill (but a table jobs_skills to store the relationship and is handles transparently by Rails)
or add random skills to a job, then your job has_may :skills and every skill belongs to exactly one job. Here you can use nested attributes. And then you need a way to add nested skill instances i.e. with cocoon. Again, you don't need a model JobSkill.
Which one is your usecase, so I can explain it in more detail.

Resources