I have two models jobs and clients.
A user can simple create a client and then assign them a number of jobs.
Here's my models for both.
job.rb
class Job < ActiveRecord::Base
has_and_belongs_to_many :clients
end
client.rb
class Client < ActiveRecord::Base
has_and_belongs_to_many :jobs
end
My form for creating a new job looks like this:
<%= simple_form_for :job do |f| %>
<%= f.input :name %>
<%= <%= collection_select(:job, :client_ids, Client.all, :id, :name, {:include_blank => 'None'}, { :multiple => true }) %>%>
<%= f.button :submit %>
<% end %>
So as you can see there is a drop down box on the form which contains all of the clients.
When try to save it however, I recieve this messed:
ActiveRecord::UnknownAttributeError in JobsController#create
unknown attribute: client_id
Application Trace | Framework Trace | Full Trace
app/controllers/jobs_controller.rb:22:in `new'
app/controllers/jobs_controller.rb:22:in `create'
Request
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"0ZVYpM9vTgY+BI55Y9yJDwCJwrwSgGL9xjHq8dz5OBE=",
"job"=>{"name"=>"Sample Monthly",
"client_id"=>"1"},
"commit"=>"Save Job"}
My job controller is quite basic and looks like this:
class JobsController < ApplicationController
def index
#jobs = Job.find(:all)
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #job }
end
end
def new
#jobs = Job.new
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #job }
end
end
def create
#jobs = Job.new(params[:job])
respond_to do |format|
if #jobs.save
format.html { redirect_to #jobs, notice: 'Job was successfully created.' }
format.json { render json: #jobs, status: :created, location: #jobs }
else
format.html { render action: "new" }
format.json { render json: #jobs.errors, status: :unprocessable_entity }
end
end
end
def show
#jobs = Job.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #jobs }
end
end
end
I have a junction table setup with both job_id and client_id as a integer value in them.
So I think its just a case of defining them in my controller under the new and create action like the error message suggests.
This is my first Rails app though sand im not quite sure how.
Any help would be greatly appreciated!
Problem might be in your form. Try to replace this
<%= <%= collection_select(:job, :client_ids, Client.all, :id, :name, {:include_blank => 'None'}, { :multiple => true }) %>%>
with
<%= f.association :clients %>
so the form will look like this
<%= simple_form_for #job do |f| %>
<%= f.input :name %>
<%= f.association :clients %>
<%= f.button :submit %>
<% end %>
Related
I am building a book repository in rails and I need to be able to add an author inside a book create form that passes the author post into the list of authors once the book has been added. In the same book creation resource I have already created the has_many: authors in the book.rb file and in the author.rb file I have created the belongs_to: author and that works fine I can select the books that author might have created with the following setup:
book.rb
class Book < ActiveRecord::Base
has_attached_file :jacket_cover, :styles => { :medium => "300x300>", :thumb => "100x100>" }, :default_url => "/images/:style/missing.png"
validates_attachment_content_type :jacket_cover, :content_type => /\Aimage\/.*\Z/
validates :jacket_cover, :title, :synopsis, :body, presence: true
belongs_to :author
scope :available, ->{ where(available: true) }
scope :unavailable, ->{ where(available: [nil, false]) }
end
author.rb
class Author < ActiveRecord::Base
has_many :books
end
books_controller.rb
class BooksController < ApplicationController
before_action :set_book, only: [:show, :edit, :update, :destroy]
def index
#books = Book.all
end
def show
end
# GET /books/new
def new
#book = Book.new
end
# GET /books/1/edit
def edit
end
def create
#book = Book.new(book_params)
respond_to do |format|
if #book.save
format.html { redirect_to #book, notice: 'Book was successfully created.' }
format.json { render action: 'show', status: :created, location: #book }
else
format.html { render action: 'new' }
format.json { render json: #book.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #book.update(book_params)
format.html { redirect_to #book, notice: 'Book was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #book.errors, status: :unprocessable_entity }
end
end
end
def destroy
#book.destroy
respond_to do |format|
format.html { redirect_to books_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_book
#book = Book.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def book_params
params.require(:book).permit(:title, :synopsis, :body, :jacket_cover)
end
end
authors_controller.rb
class AuthorsController < ApplicationController
before_action :set_author, only: [:show, :edit, :update, :destroy]
def index
#authors = Author.all
end
def show
end
def new
#author = Author.new
end
# GET /authors/1/edit
def edit
end
def create
#author = Author.new(author_params)
respond_to do |format|
if #author.save
format.html { redirect_to #author, notice: 'Author was successfully created.' }
format.json { render action: 'show', status: :created, location: #author }
else
format.html { render action: 'new' }
format.json { render json: #author.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #author.update(author_params)
format.html { redirect_to #author, notice: 'Author was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #author.errors, status: :unprocessable_entity }
end
end
end
def destroy
#author.destroy
respond_to do |format|
format.html { redirect_to authors_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_author
#author = Author.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def author_params
params.require(:author).permit(:name, :biography, :books_ids => [] )
end
end
This allows me to create the books and the authors which is fine but I am now looking to nest the author create into my book create too.
form for book create
<%= simple_form_for(#book, :html => { :multipart => true }) do |f| %>
<%= f.error_notification %>
<div class="inputs">
<%= f.file_field :jacket_cover %>
<%= f.input :title %>
<%= f.input :synopsis %>
<%= f.input :body %>
</div>
<div class="actions">
<%= f.button :submit %>
</div>
<% end %>
form for author create
<%= simple_form_for(#author) do |f| %>
<%= f.error_notification %>
<div class="inputs">
<%= f.input :name %>
<%= f.association :books,
as: :check_boxes,
value_method: :id,
label: 'Books' %>
<%= f.input :biography %>
</div>
<div class="actions">
<%= f.button :submit %>
</div>
<% end %>
Is it possible to nest another resource into an already created resource so adding an author create inside a book create page?
Yes,you need accepts_nested_attributes_for and simple_fields_for helper provided for the simple_form_for(as you are using simple_form_for)
Step #1
In your Book model,you should add accepts_nested_attributes_for :author
Step #2
Modifying your new method of books_controller.rb
As you have belongs_to :author in your Book model,your new method of your BooksController would be
def new
#book = Book.new
#book.build_author #This is very important
end
Step #3
Your book_params method should be modified to
def book_params
params.require(:book).permit(:title, :synopsis, :body, :jacket_cover,author_attributes: [:name,:biography,..,..])
end
Step #4
Finally,your form for book create would be something like this
<%= simple_form_for(#book, :html => { :multipart => true }) do |f| %>
<%= f.error_notification %>
<div class="inputs">
<%= f.file_field :jacket_cover %>
<%= f.input :title %>
<%= f.input :synopsis %>
<%= f.input :body %>
</div>
<%= f.simple_fields_for :author do |a| %>
... author fields...
....................
<% end %>
<div class="actions">
<%= f.button :submit %>
</div>
<% end %>
Is it possible to nest another resource into an already created resource so adding an author create inside a book create page?
Yes.
You probably want a longer answer than that though.
You need to get nested attributes sorted out.
In Book model:
has_one :author, dependent: :destroy
accepts_nested_attributes_for :authors
In BooksController :new add line
#book = Author.new
#book.build_author
And in form for #book:
<%= form_for(#book, :html => { :multipart => true }) do |f| %>
<%= f.error_notification %>
<div class="inputs">
#book fields
</div>
<%= f.fields_for :author do |author| %>
#author fields
<%= f.button :submit %>
</div>
<% end %>
And don't forget to modify book_params method as shown in answer by Rich Peck.
Your question is somewhat verbose, but I'll detail what you need to know
--
Nested Objects
Is it possible to nest another resource into an already created resource so adding an author create inside a book create page?
Rails is built on top of Ruby (an object orientated language). This means Rails is object orientated too, and so if you wanted to create an author at the same time as creating a book (this is only applicable for create), you'll want to use the accepts_nested_attributes_for directive for your model:
#app/models/book.rb
Class Book < ActiveRecord::Base
has_one :author
accepts_nested_attributes_for :author
end
#app/controllers/books_controller.rb
Class BooksController < ApplicationController
def new
#book = Book.new
#book.build_author #-> this will be authors.build if multiple
end
def create
#book = Book.new(book_params)
#book.save
end
private
def book_params
params.require(:book).permit(:title, :synopsis, :body, author_attributes: [:biography])
end
end
This will allow you to create the following (using form_for for simplicity sake):
#app/views/books/new.html.erb
<%= form_for #book do |f| %>
<%= f.text_field :title %>
<%= f.text_field :synopsis %>
<%= f.text_field :body %>
<%= f.fields_for :author do |a| %>
<%= a.text_field :biography %>
<% end %>
<%= f.submit %>
<% end %>
This will create the book record & associated author record, too
I am new to rails and am trying to create multiple entries in a table through my Test and TestQuestions models without success. Ultimately I would like to select 50 questions based on their category. I am stuck at this step, trying to pass category_id parameters to update my test_questions table from the TestController / Test Model.
The commented out line in the controller below: "#test.test_questions.build(:question_id => 5).save" works to make one question number 5, but when I call #test.build_category_test!(category_params).save instead to call a method and pass an array,I get the error undefined method `build_category_test!' for #Test:0x000000047f0928
Models
class TestQuestions < ActiveRecord::Base
belongs_to :test
end
class Test < ActiveRecord::Base
has_many :test_questions, class_name: "TestQuestions",
foreign_key: "test_id"
def self.build_category_test!(category_ids)
unless category_ids.blank?
category_ids.each do |u|
test_questions.build!(:question_id => 5)
end
end
end
end
Controller
class TestsController < ApplicationController
def create
#test = Test.new(test_params)
respond_to do |format|
if #test.save
##test.test_questions.build(:question_id => 5).save
#test.build_category_test!(category_params).save
format.html { redirect_to #test, notice: 'Test was successfully created.' }
format.json { render action: 'show', status: :created, location: #test }
else
format.html { render action: 'new' }
format.json { render json: #test.errors, status: :unprocessable_entity }
end
end
def category_params
params[:category][:category_ids]
end
end
View of test/new.html.erb
<%= form_tag tests_path, :method => :post do %>
<%= hidden_field_tag "user_id", #user_id %>
<ul>
<% for c in #categories %>
<li>
<%= check_box_tag "category[category_ids][]", c.id %>
<%= c.category %>
</li>
<% end %>
</ul>
<%= submit_tag "Create Test" %>
Log of parameters: "user_id"=>"1", "category"=>{"category_ids"=>["1", "2"]}, "commit"=>"Create Test"}
The method build_category_test! should be an instance method:
def build_category_test!(category_ids) # #test.build_category_test!
Instead of a class method:
def self.build_category_test!(category_ids) # Test.build_category_test!
If I leave the input box blank. I get this error everytime. I don't want it to make new record when it's blank. when not, I want it to make new record.
this input box is nested and the code of controller is written like this to avoid error
def create
# Check if there is any contact info added
if params[:girl][:contact_attributes][:mail].empty?
params[:girl].delete(:contact_attributes)
end
#girl = Girl.new(params[:girl])
respond_to do |format|
if #girl.save
format.html { redirect_to #girl, notice: 'Girl was successfully created.' }
format.json { render json: #girl, status: :created, location: #girl }
else
format.html { render action: "new" }
format.json { render json: #girl.errors, status: :unprocessable_entity }
end
end
end
view is like this
<%= form_for(#girl) do |f| %>
....
<div class="field">
<%= f.label :mail %><br />
<%= f.fields_for :contact_attributes, #girl.contact do |contact| %>
<%= contact.text_field :mail %>
<% end %>
</div>
....
<div class="actions">
<%= f.submit %>
</div>
<% end %>
my model
class Girl < ActiveRecord::Base
has_many :users
has_one :contact
accepts_nested_attributes_for :contact
attr_accessible :id, :name_en, :name_ja, :gender_id, :contact_attributes, :photo, :tag_list
searchable do
text :name_en, :name_ja
text :contact do
contact.mail
end
end
has_attached_file :photo,
:styles => {
:thumb=> "100x100>",
:small => "400x400>" }
acts_as_taggable_on :tags
acts_as_commentable
end
You have to set
#girl = Girl.new
inside your else block, just before
format.html { render action: "new" }
The error happens because you render the new template and inside it the form_for(#girl) gets a nil object - #girl. In order to render the line <%= f.label :mail %><br /> it tries to call the mail method on the given #girl object in order to get its default value. Since the #girl object is nil and not set in the create action before you render the new template you get this error.
UPDATE:
I misunderstood your situation in the answer on the first part of this post. The solution in my opinion is redirecting to the new girl path instead of just rendering the new action. While rendering only renders the view redirecting will make a full-stack request process. Assuming you have the route new_girl_path set you should replace format.html { render action: "new" } with
format.html { redirect_to new_girl_path }
You can run `rake routes and see what named routes you have set.
I problem is the following few lines of code.
if params[:girl][:contact_attributes][:mail].empty?
params[:girl].delete(:contact_attributes)
end
If mail is empty in user contact you have removed the contact attributes and created only the user object.
So if you call #girl.contact you will get nil.
I don't know why you have removed the contact attributes.If you still want to do it you need to add one more line.
if #girl.save
format.html { redirect_to #girl, notice: 'Girl was successfully created.' }
format.json { render json: #girl, status: :created, location: #girl }
else
#Assuming you have the association like: user has_one contact
#user.build_contact
format.html { render action: "new" }
format.json { render json: #girl.errors, status: :unprocessable_entity }
end
And one more thing
<%= f.fields_for :contact_attributes, #girl.contact do |contact| %>
can be simply written as
<%= f.fields_for :contact do |contact| %>
Replace same line of code with <%= form_for( :girl, :url => {:action => :create}) do |f| %>
I'm trying to use a list of clients that have been created from my client model in my new model called Jobs.
Basically. A user should be able to view a list of jobs that are currently assigned to any one client and then drill down into further information.
I have inserted a new column into my jobs database called client_id and have inserted the following code in my _form view to be able to see a drop down list of all the clients
<%= f.label :client_id %><br />
<%= f.collection_select :client_id, #clients, :id, :name, :prompt =>
"Select" %>
However. When I hit submit it tries to POST it to jobs/new which according to my resource route. Doesn't exist.
I have also inserted some dummy data into the database and though that is showing up fine when I try to edit it. Pressing save will not do anything to the record.
Parameters: {"utf8"=>"✓", "authenticity_token"=>"0ZVYpM9vTgY+BI55Y9yJDwCJwrwSgGL9xjHq8dz5OBE=", "job"=>{"name"=>"Sample Monthly", "client_id"=>"2", "frequency"=>"Monthly", "owner"=>"Me"}, "commit"=>"Save Job", "id"=>"1"}
Job Load (0.3ms) SELECT "jobs".* FROM "jobs" WHERE "jobs"."id" = ? LIMIT 1 [["id", "1"]]
(0.1ms) begin transaction
(0.5ms) UPDATE "jobs" SET "name" = 'Sample Monthly', "frequency" = 'Monthly', "updated_at" = '2012-05-12 17:04:23.818967' WHERE "jobs"."id" = 1
(108.3ms) commit transaction
Redirected to http://localhost:3000/jobs/1
Here's my controller..
class JobsController < ApplicationController
before_filter :load_clients, :only => [ :new, :create, :edit ]
def index
#job = Job.find(:all)
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #job }
end
end
def new
#job = Job.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #Job }
end
end
def create
#job = Job.new(params[:job])
respond_to do |format|
if #job.save
format.html { redirect_to #job, notice: 'Job was successfully created.' }
format.json { render json: #job, status: :created, location: #job }
else
format.html { render action: "new" }
format.json { render json: #job.errors, status: :unprocessable_entity }
end
end
end
def show
#job = Job.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #job }
end
end
def update
#job = Job.find(params[:id])
respond_to do |format|
if #job.update_attributes(params[:job])
format.html { redirect_to #job, notice: 'Job was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #job.errors, status: :unprocessable_entity }
end
end
end
private
def load_clients
#client = Client.find(:all)
end
end
I suppose to get this working is a relatively easy fix but this is my forst Rails app and i'm not sure where to start.
Edit:
As requested. Here is my jobs form:
<%= form_for(:job) do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :client_id %><br />
<%= f.collection_select :client_id, #client, :id, :name, :prompt =>
"Select" %>
</div>
<div class="field">
<%= f.label :frequency %><br />
<%= f.text_field :frequency %>
</div>
<div class="field">
<%= f.label :owner %><br />
<%= f.text_field :owner %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Edit2: Here's my Job model.
class Job < ActiveRecord::Base
belongs_to :clients
end
Try replace :job with #job in your form.
And also looks like you are missing edit action in your controller.
Brief overview of my app.
It's quite basic in that the User first of all creates a set A client on one page and then uses another to create and assign jobs to the user.
My Client model and view are working as expected but im unable to link my jobs model.
Here is my jobs model.
class Client < ActiveRecord::Base
has_and_belongs_to_many :jobs
end
class Job < ActiveRecord::Base
has_and_belongs_to_many :clients
end
Here is also my clients controller.
class JobsController < ApplicationController
def index
#jobs = Job.find(:all)
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #job }
end
end
def new
#jobs = Job.new
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #job }
end
end
def create
#jobs = Job.new(params[:job])
respond_to do |format|
if #jobs.save
format.html { redirect_to #jobs, notice: 'Job was successfully created.' }
format.json { render json: #jobs, status: :created, location: #jobs }
else
format.html { render action: "new" }
format.json { render json: #jobs.errors, status: :unprocessable_entity }
end
end
end
def show
#jobs = Job.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #jobs }
end
end
end
In my form I have two fields. One for the job name and another which is a drop down on all the clients listed in the database.
When fill this out however and I press save im getting the following error.
ActiveRecord::UnknownAttributeError in JobsController#create
**unknown attribute: client_id**
Application Trace | Framework Trace | Full Trace
app/controllers/jobs_controller.rb:22:in `new'
app/controllers/jobs_controller.rb:22:in `create'
Request
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"0ZVYpM9vTgY+BI55Y9yJDwCJwrwSgGL9xjHq8dz5OBE=",
"job"=>{"name"=>"Sample Monthly",
"client_id"=>"1"},
"commit"=>"Save Job"}
I have a junction table setup called clients_jobs also..
class AddClientsJobsTable < ActiveRecord::Migration
def up
create_table :clients_jobs, :id => false do |t|
t.belongs_to :job, :client
t.integer :client_id
t.integer :job_id
end
end
def down
drop_table :clients_jobs
end
end
I assume I need to declare client_id
somewhere but this is my first Rails app and im not sure where.
Any help would be greatly appreciated.
Edit: Here's my Job's form.
<%= simple_form_for :job do |f| %>
<%= f.input :name %>
<%= select("job", "client_id", Client.all.collect {|c| [ c.name, c.id ] }, {:include_blank => 'None'})%>
<%= f.button :submit %>
<% end %>
Your model states that job - client is a habtm association, but your form implements as if job belongs to (one) client. If indeed your intention is to be able to assign a job to multiple clients, your for should look something like:
<%= collection_select(:job, :client_ids, Client.all, :id, :name, {:include_blank => 'None'}, { :multiple => true }) %>
note plural 'client_ids' and allowing multiple in the input.
If a job belongs to only one user, you should not use has_and_belongs_to_many :clients.