SQLite3::SQLException: no such column: problem with association and show view - ruby-on-rails

I am getting the following error message on the view : http://localhost:3000/microposts/2
SQLite3::SQLException: no such column: answers.micropost_id: SELECT "answers".* FROM "answers" WHERE ("answers".micropost_id = 2)
Here are my migrations:
| db > migration > create micropost |
class CreateMicroposts < ActiveRecord::Migration
def self.up
create_table :microposts do |t|
t.string :content
t.timestamps
end
end
def self.down
drop_table :microposts
end
end
| db > migration > create answer |
class CreateAnswers < ActiveRecord::Migration
def self.up
create_table :answers do |t|
t.string :location
t.text :body
t.references :micropost
t.integer :micropost_id
t.timestamps
end
end
def self.down
drop_table :answers
end
end
The Answer Controler :
def create
#answer = Answer.new(params[:answer])
#answer.micropost = #micropost; #answer.save && #micropost.save
redirect_to micropost_path(#micropost)
respond_to do |format|
if #answer.save
format.html { redirect_to(#answer, :notice => 'Answer was successfully created.') }
format.xml { render :xml => #answer, :status => :created, :location => #answer }
else
format.html { render :action => "new" }
format.xml { render :xml => #answer.errors, :status => :unprocessable_entity }
end
end
end
and the View:
<p id="notice"><%= notice %></p>
<p>
<b>Content:</b>
<%= #micropost.content %>
<h2>Location Answers:</h2>
<% #micropost.answers.each do |answer| %>
<p>
<b>Answer:</b>
<%= answer.body%>
</p>
<%end %>
<h2> Answer a location:</h2>
<%= form_for ([#micropost, #micropost.answers.build]) do |f| %>
<div class="field">
<%= f.label :location %><br />
<%= f.text_field :location %>
</div>
<div class="field">
<%= f.label :body %><br/>
<%= f.text_area :body %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
</p>
<%= link_to 'Edit', edit_micropost_path(#micropost) %> |
<%= link_to 'Back', microposts_path %>
I cannot find what's wrong in this application.
- I tried to rollback and migrate again it didn't work.
- I tried to manually add the "t.integer :micropost_id" in the migration it didn't work
My model have the association 'belongs_to" and "has_many" and I added "
resources :microposts do
resources :answers
end
to my config.rb file.

I believe the has_many association requires a relationship table to join against. If you had has_one and belongs_to (a one to one association) works with the simple _id column approach but has_many won't. So if you put this join table in and describe the has_many through it, you should get what you want.
Here's a really great guide for rails associations that I use when this stuff becomes unclear to me.

You don't need to set micropost_id in your migration. This is done by t.references
create_table :answers do |t|
t.string :location
t.text :body
t.references :micropost
t.timestamps
end
Best practice to set and index, for example:
add_index :answers, :micropost_id

Related

Could not save nested form in rails 5.1

I used nested_form gem and I am trying to build a form which has fields from two tables(Project, Question).
My model:
class Project < ApplicationRecord
has_many :questions
accepts_nested_attributes_for :questions
end
class Question < ApplicationRecord
belongs_to :project
end
My controller:
class ProjectsController < ApplicationController
layout 'application'
def index
#projects = Project.all
end
def show
#project = Project.find(params[:id])
end
def new
#project = Project.new
#questions = #project.questions.build
end
def create
#project = Project.new(project_params)
#project.save
respond_to do |format|
if #project.save
format.html { redirect_to #project, notice: 'Project was successfully created.' }
format.json { render :show, status: :created, location: #project }
else
format.html { render :new }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
private
def project_params
params.require(:project).permit(:name, question_attributes: [:id, :content, :_delete])
end
end
My view:
<%= nested_form_for(#project) do |f| %>
<% if #project.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#project.errors.count, "error") %> prohibited this project from being saved:</h2>
<ul>
<% #project.errors.full_messages.each do |message| %>
<li>
<%= message %>
</li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<%= f.fields_for :questions do |builder| %>
<%= render "question_fields", :ff => builder %>
<% end %>
<p>
<%= f.link_to_add "Add a questions",:questions %>
</p>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
And my schema file :
create_table "projects", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "questions", force: :cascade do |t|
t.integer "project_id"
t.string "content"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["project_id"], name: "index_questions_on_project_id", using: :btree
end
And _question_fields file is :
<p>
<%= ff.label :content, "Question" %>
<%= ff.text_area :content%>
<%= ff.link_to_remove "Remove this task"%>
</p>
Table Project will be saved but the table Question could not be saved.Why?
After change this line of code
def project_params
params.require(:project).permit(:name, questions_attributes: [:id, :content, :_delete])
end
I get the following error:
1 error prohibited this project from being saved:
Questions project must exist
I applied the change too ,but this time nothing could not be saved.
def project_params
params.require(:project).permit(:name, questions_attributes: [:id, :content, :project_id, :_delete])
end
The issue is with the project_params. As you have has_many :questions, the question_attributes should be changed to questions_attributes
def project_params
params.require(:project).permit(:name, questions_attributes: [:id, :content, :_delete])
end
Update:
Questions project must exist
Either permit project_id in questions_attributes
def project_params
params.require(:project).permit(:name, questions_attributes: [:id, :content, :project_id, :_delete])
end
or set optional: true on the association
class Question < ApplicationRecord
belongs_to :project, optional: true
end

rails paperclip - trouble uploading multiple images to one model through nested model

I have been trying to figure out how to upload multiple images to one model through a nested model for a while now with no luck. I have a Project model, and for each project i would like to upload multiple images. I created a model called Picture and nested it within the Project model, and have set up paperclip and everything seems fine except when I upload an image and click on "Create project", the image does not show on the "show" page. There is no error message displayed. Please help as I do not know how to proceed from here.
here is my code:
Project form:
<%= bootstrap_nested_form_for #project, :html => {:multipart => true} do |f| %>
<% f.fields_for :pictures do |builder| %>
<% if builder.object.new_record? %>
<p>
<%= builder.file_field :image %>
</p>
<% end %>
<%= builder.link_to_remove "Remove" %>
<% end %>
<p>
<%= f.link_to_add "Add Images", :pictures %>
</p>
<%= f.submit %>
<% end %>
Project controller:-
class ProjectsController < ApplicationController
before_action :set_project, only: [:show, :edit, :update, :destroy]
respond_to :html
def index
#projects = Project.all
respond_with(#projects)
end
def show
respond_with(#project)
end
def new
#project = Project.new
#project.pictures.build
respond_with(#project)
end
def edit
#project = Project.find(params[:id])
#project.pictures.build
end
def create
#project = Project.new(project_params)
if #project.save
flash[:notice] = "Successfully created project."
redirect_to #project
else
render :action => 'new'
end
end
def update
#project.update(project_params)
respond_with(#project)
end
def destroy
#project.destroy
respond_with(#project)
end
private
def set_project
#project = Project.find(params[:id])
end
def project_params
params.require(:project).permit(:id, :title, :description, :status, :phase, :location, pictures_attributes: [:id, :image])
end
end
Projects model:-
class Project < ActiveRecord::Base
has_many :pictures, :dependent => :destroy
accepts_nested_attributes_for :pictures, :reject_if => lambda { |t| t['picture'].nil? }
end
Pictures model:-
class Picture < ActiveRecord::Base
belongs_to :project
has_one :image
has_attached_file :image,
:path => ":rails_root/public/system/:attachment/:id/:style/:filename",
:url => "/system/:attachment/:id/:style/:filename",
:styles => { :medium => "300x300>", :thumb => "100x100>" }
validates_attachment_content_type :image, :content_type => ["image/jpg", "image/jpeg", "image/png", "image/gif"]
end
Show page:-
<% #project.pictures do |picture| %>
<%= image_tag picture.image_url %>
<% end %>
<p>
<strong>Title:</strong>
<%= #project.title %>
</p>
<p>
<strong>Description:</strong>
<%= #project.description %>
</p>
<p>
<strong>Status:</strong>
<%= #project.status %>
</p>
<p>
<strong>Phase:</strong>
<%= #project.phase %>
</p>
<p>
<strong>Location:</strong>
<%= #project.location %>
</p>
<%= link_to 'Edit', edit_project_path(#project) %> |
<%= link_to 'Back', projects_path %>
schema :-
ActiveRecord::Schema.define(version: 20150728092717) do
create_table "pictures", force: true do |t|
t.datetime "created_at"
t.datetime "updated_at"
t.string "image_file_name"
t.string "image_content_type"
t.integer "image_file_size"
t.datetime "image_updated_at"
t.integer "project_id"
end
add_index "pictures", ["project_id"], name: "index_pictures_on_project_id"
create_table "projects", force: true do |t|
t.string "title"
t.text "description"
t.string "status"
t.string "phase"
t.string "location"
t.datetime "created_at"
t.datetime "updated_at"
end
Your form and whitelist uses the property name image.
But you are rejecting any nested pictures if they don't have the picture param.
accepts_nested_attributes_for :pictures, :reject_if => lambda { |t| t['picture'].nil? }
Nested attributes params are not wrapped in a "model key" like rails form params usually are. This is what they look like:
params = {
project: {
pictures_attributes: [
{
image: 'foo.jpg'
}
]
}
}
You can catch these kind of errors quite simply with model specs:
require 'rails_helper'
RSpec.describe Project do
it 'accepts nested pictures' do
project = Project.new(pictures_attributes: [{ image: 'foo.jpg' }])
expect(project.pictures.first).to to_be_a Picture
end
end

Rails: Polymorphic assosiation and accepts_nested_attributes

The attachment won't save. What I am missing here?
In my application I have a project ,for each project user can upload many assets. The Upload is done by carrier wave.
here are the models
class Project < ActiveRecord::Base
has_many :assets,:as => :assetable,dependent: :destroy
accepts_nested_attributes_for :assets, :allow_destroy => true
end
class Asset < ActiveRecord::Base
belongs_to :project
belongs_to :user
belongs_to :assetable, :polymorphic => true
mount_uploader :attachment, AttachmentUploader #carrierwave
validates :attachment, presence: true
validates :project_id, presence: true
end
and these are the actions in my project_controller
def new
#project = Project.new
#asset = #project.assets.build
end
def create
#project = Project.new(project_params)
#project.assets.build
respond_to do |format|
if #project.save
format.html { redirect_to #project, notice: 'Project was successfully created.' }
format.json { render :show, status: :created, location: #project }
else
format.html { render :new }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
def project_params
params.require(:project).permit(:user_id, :summary, :start_date,assets_attributes: [:id, :project_id, :attachment,:user_id] )
end
this is how the form looks like
<%= form_for #project,:html => {:multipart => true } do |f| %>
<% if #project.errors.any? %>
<div id="error_explanation">
</div>
<% end %>
<%= f.fields_for :assets do |p| %>
<div class="field">
<%= p.label :attachment %><br>
<%= p.file_field :attachment,name: "assets[attachment][]" %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
schema.rb
create_table "assets", force: true do |t|
t.string "assetable_id"
t.string "assetable_type"
t.string "attachment"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "projects", force: true do |t|
t.string "user_id"
t.string "summary"
t.datetime "created_at"
t.datetime "updated_at"
end
projects/_form.html.erb
<%= form_for #project, :html => {:multipart => true } do |f| %>
<% if #project.errors.any? %>
<div id="error_explanation">
<%= #project.errors.inspect %>
</div>
<% end %>
<%= f.label :summary %>
<%= f.text_field :summary %>
<%= f.fields_for :assets do |p| %>
<div class="field">
<%= p.label :attachment %><br>
<%= p.file_field :attachment %>
<%= p.hidden_field :assetable_id %>
<%= p.hidden_field :assetable_type %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
projects_controller.rb
# GET /projects/new
def new
#project = Project.new
#project.assets.build
end
# POST /projects
# POST /projects.json
def create
#project = Project.new(project_params)
respond_to do |format|
if #project.save
format.html { redirect_to #project, notice: 'Project was successfully created.' }
format.json { render :show, status: :created, location: #project }
else
format.html { render :new }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
private
# Never trust parameters from the scary internet, only allow the white list through.
def project_params
params.require(:project).permit(:user_id, :summary, :start_date, assets_attributes: [:id, :assetable_id, :assetable_type, :attachment, :user_id] )
end
project.rb
class Project < ActiveRecord::Base
has_many :assets, :as => :assetable, dependent: :destroy
accepts_nested_attributes_for :assets, :allow_destroy => true
end
asset.rb
class Asset < ActiveRecord::Base
belongs_to :project
belongs_to :assetable, :polymorphic => true
mount_uploader :attachment, AttachmentUploader #carrierwave
validates :attachment, presence: true
validates :assetable_type, presence: true
end
Aside
Since you based this question off a previous question you asked I'll just mention: You only want to use a polymorphic association if you intend instances of your asset class to belong to different types of classes (ie. things other than a project).

Nested Forms with Strong Parameters; object_attribute method not available?

This example is kind of cliche. I am following the railscasts episode #196 (http://railscasts.com/episodes/196-nested-model-form-revised?autoplay=true) for double nested forms.
The error I get is that my accepts_nested_attributes_for command is not generating an answers_attributes for strong parameters.
Models:
class Survey < ActiveRecord::Base
has_many :questions
accepts_nested_attributes_for :questions
end
class Question < ActiveRecord::Base
belongs_to :survey
has_many :answers
has_many :users, through: :answers
accepts_nested_attributes_for :answers
end
class Answer < ActiveRecord::Base
belongs_to :question
belongs_to :user
end
Then my surveys controller:
class SurveysController < ApplicationController
def index
#survey = current_user.surveys
end
def new
#survey = Survey.new
#question = #survey.questions.build # the nested form won't show up if I don't
#answer = #question.answers.build #Not sure if I need this line. doesn't work either way.
end
def create
#survey = Survey.new(survey_params)
if #survey.save
redirect_to #survey, notice: "Survey successfully created."
else
render 'new'
end
end
# rest.. show, edit, update, destroy
private
def survey_params
params.require(:survey).permit!
end
#def survey_params
#params.require(:survey).permit(:user_id, :name, { questions_attributes: [:_destroy, :id, :survey_id, :content, { answers_attributes: [:_destroy, :id, :content, :user_id, :question_id]}]})
#end
end
The issue I'm having is this (empty form submit). When I run my real code, I get this:
{"utf8"=>"✓", "authenticity_token"=>"[token]=", "survey"=>{"user_id"=>"1", "name"=>"test", "questions_attributes"=>{"0"=>{"content"=>"test", "_destroy"=>"0"}}, "answers"=>{"content"=>"test", "_destroy"=>"0"}}, "commit"=>"Create", "action"=>"create", "controller"=>"surveys"}
Unpermitted attributes: answers
Then when I run the permit! method to test a force though I get this:
unknown attribute: answers
Rails should be looking for answers_attributes, not answers. That makes me think it's not recognizing the model. So, here is the schema.
create_table "questions", force: true do |t|
t.integer "survey_id"
t.string "content"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "answers", force: true do |t|
t.integer "question_id"
t.string "content"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_id"
end
Let me know if you have any ideas on how to fix my error of somehow telling rails to look for answers instead of answers_attributes.
UPDATE: Here is my form.
<%= form_for #survey do |f| %>
<%= f.label :user_id %>
<%= f.collection_select :user_id, User.all, :id, :email, {}, { :multiple => false } %><br>
<%= f.text_field :name, placeholder: "Survey name"%>
<%= f.fields_for :questions do |builder| %>
<%= builder.label :content, "Question 1"%>
<%= builder.text_area :content %>
<%= builder.check_box :_destroy %>
<%= builder.label :_destroy, "Remove Question" %>
<%= f.fields_for :answers do |builder| %>
<%= builder.text_field :content, placeholder: "Answer 1" %>
<%= builder.check_box :_destroy %>
<% end %>
<% end %><br>
<%= f.submit "Create", :class => "btn btn-large btn-warning" %>
<% end %>
The solution was an oversight on the forms. I used...
<%= f.fields_for :questions do |builder| %>
...to build the association under the survey and then mistakenly used...
<%= f.fields_for :answers do |builder| %>
...to build the answers under question. I needed to change the f variable so it shoulder have been something like this:
<%= builder.fields_for :answers do |x| %>
So, it was looking for answers under survey instead of under questions. Thanks guys.

how to save nested form attributes to database

I do not really understand how nested attributes work in Rails.
I have 2 models, Accounts and Users. Account has_many Users. When a new user fills in the form, Rails reports
User(#2164802740) expected, got Array(#2148376200)
Can Rails not read the nested attributes from the form? How can I fix it? How can I save the data from nested attributes to the database?
Thanks all~
Here are the MVCs:
Account Model
class Account < ActiveRecord::Base
has_many :users
accepts_nested_attributes_for :users
validates_presence_of :company_name, :message => "companyname is required."
validates_presence_of :company_website, :message => "website is required."
end
User Model
class User < ActiveRecord::Base
belongs_to :account
validates_presence_of :user_name, :message => "username too short."
validates_presence_of :password, :message => "password too short."
end
Account Controller
class AccountController < ApplicationController
def new
end
def created
end
def create
#account = Account.new(params[:account])
if #account.save
redirect_to :action => "created"
else
flash[:notice] = "error!!!"
render :action => "new"
end
end
end
Account/new View
<h1>Account#new</h1>
<% form_for :account, :url => { :action => "create" } do |f| %>
<% f.fields_for :users do |ff| %>
<p>
<%= ff.label :user_name %><br />
<%= ff.text_field :user_name %>
</p>
<p>
<%= ff.label :password %><br />
<%= ff.password_field :password %>
</p>
<% end %>
<p>
<%= f.label :company_name %><br />
<%= f.text_field :company_name %>
</p>
<p>
<%= f.label :company_website %><br />
<%= f.text_field :company_website %>
</p>
<% end %>
Account Migration
class CreateAccounts < ActiveRecord::Migration
def self.up
create_table :accounts do |t|
t.string :company_name
t.string :company_website
t.timestamps
end
end
def self.down
drop_table :accounts
end
end
User Migration
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.string :user_name
t.string :password
t.integer :account_id
t.timestamps
end
end
def self.down
drop_table :users
end
end
Thanks everyone. :)
Change the following view region:
<% form_for :account, :url => { :action => "create" } do |f| %>
into:
<% form_for #account do |f| %>
Inside your controller you should have something like this:
def new
#account = Account.new
# the new empty account doesn't have any users
# so the user fields inside your view won't appear unless you specify otherwise:
#account.users.build
#account.users.build
#account.users.build
end

Resources