I can't get along with saving Students with one POST when i"m saving Project.
My Projects controller looks like:
class ProjectsController < ApplicationController
def index
#projects = Project.all
end
def new
#project = Project.new
3.times do
student = #project.students.build
end
end
def create
#project = Project.new(project_params)
#project.status = "Waiting"
# I'm not sure about these lines
#project.students.each do |student|
student = Student.new(params[:name])
end
#project.save!
redirect_to projects_path
end
private
def project_params
params.require(:project).permit(:name, :lecturer)
end
end
And a new_project view looks like:
<h1>Creating new project...</h1>
<%= form_for #project, url: projects_path do |f| %>
<p>
<%= f.label :name %>
<%= f.text_field :name %>
</p>
<p>
<%= f.label :lecturer %>
<%= f.text_field :lecturer %>
</p>
<p>
<%= f.fields_for :students do |s| %>
<%= s.label :name %>
<%= s.text_field :name %>
<% end %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
And my question is how to save Project and Students (assigned to it) using one form?
First, your project_params method isn't allowing the students' names to be submitted. Try something like this:
def project_params
params.require(:project).permit(:name, :lecturer, students_attributes: [ :name ] )
end
Next, in your Project model you'll need the line
accepts_nested_attributes_for :students
(You might have put it there already - but if you didn't, you'll need to.)
Now that that's done, you shouldn't need these lines in your Project#create method:
#project.students.each do |student|
student = Student.new(params[:name])
end
Because your project can now accept nested attributes for students, they should be created automatically with the project when you save it.
Related
I have three models, companies, transactions and subtransactions. Each has_many of the next. I need to build a form that saves to both transactions and multiple records of subtransactions.
I'm struggling with the best approach to the form and the appropriate way to save to the DB. Ideally, the records get saved at once if all pass validation.
My simplified form:
<%= form_for(#transaction) do |f| %>
<div class="field" id="transaction_amount">
<%= f.label :total %><br>
<%= f.text_field :total %>
</div>
<% 1.upto(5) do |i| %>
<%= fields_for("subtransaction[#{i}]") do |s| %>
<div class="field" id="subtotal_amount">
<%= s.label :subtotal, "Subtotal #{i}" %><br>
<%= s.text_field :subtotal %>
</div>
<% end %>
<% end %>
<% end %>
My TransactionsController:
def new
#transaction = company.transactions.build if logged_in?
end
def create
#transaction = company.transactions.build(transaction_params)
params[:subtransaction].each do |i|
# build and save
end
if #transaction.save ## #subtransaction.save
flash[:success] = "Success!"
redirect_to success_url
else
render :new
end
end
The way to do this is to pass the nested attributes through the various models:
#app/models/company.rb
class Company < ActiveRecord::Base
has_many :transactions
accepts_nested_attributes_for :transactions
end
#app/models/transaction.rb
class Transaction < ActiveRecord::Base
has_many :sub_transactions
accepts_nested_attributes_for :sub_transactions
end
This will give you the ability to use the following:
#app/controllers/transactions_controller.rb
class TransactionsController < ApplicationController
def new
#transaction = company.transactions.new
5.times do
#transaction.sub_transactions.build
end
end
def create
#transaction = company.transactions.new transaction_params
#transaction.save
end
private
def transaction_params
params.require(:transaction).permit(sub_transactions_attributes: [:subtotal])
end
end
Then...
#app/views/transactions/new.html.erb
<%= form_for #transaction do |f| %>
<%= f.fields_for :sub_transactions do |s| %>
<%= s.text_field :subtotal %>
<% end %>
<%= f.submit %>
<% end %>
This will allow you to save the various nested attributes through the main object. It is the correct, conventional, way to do it.
model
class Transaction < ActiveRecord::Base
has_many :subtransations
accepts_nested_attributes_for :subtransactions
end
controller
def new
#transaction = Transaction.new
#transaction.subtransactions.build
end
def create
company = Company.first # Or whatever you need.
# We merge the company id to the transaction params since
# a transaction belongs_to a company.
#transaction = Transaction.create!(create_params.merge(company_id: company.id))
end
private
def create_params
params.require(:transaction).permit(
:total,
subtransactions_attributes: [:id, :subtotal]
)
end
view
<%= form_for(#transaction) do |f| %>
<div class="field">
<%= f.label :total %><br>
<%= f.text_field :total %>
</div>
<%= f.fields_for :subtransaction do |s| %>
<div class="field" id="subtotal_amount">
<%= s.label :subtotal %><br>
<%= s.text_field :subtotal %>
</div>
<% end %>
<% end %>
This will allow to create only one subtransaction with the transaction record
If you want to create more than one subtransaction record you should use cocoon gem, it's easy to setup and will save much precious time.
I have a 'post' controller in that I have two variable title and body which I am passing through strong parameters.But I need to use two other variable which are path and name which are in different model name 'Document'..And also I am saving the content in database ..but unable to do so..getting this error view [posts/_form.html.erb]
undefined method `name' for #
[posts_controller]
class PostsController < ApplicationController
before_action :authenticate_user!
def index
#posts = Post.user_post(current_user).order('created_at DESC').paginate(:page => params[:page], :per_page => 5)
end
def new
#post = Post.new
end
def show
#post = find_params
end
def create
#post = Post.create(post_params)
#post.user = current_user
if #post.save
redirect_to #post
else
render 'new'
end
end
def edit
#post = find_params
end
def update
#post = find_params
if #post.update(post_params)
redirect_to #post
else
render 'edit'
end
end
def destroy
#post = find_params
#post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:title, :body)
Document.new(params,:files=>[])
end
def find_params
Post.find(params[:id])
end
end
[post/_form.html.erb]
<%= form_for #post,html: { multipart: true } do |f| %>
<% if #post.errors.any? %>
<div id="errors">
<h2><%= pluralize(#post.errors.count, "error") %> prevented this post from saving:</h2>
<ul>
<% #post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.label :title %><br>
<%= f.text_field :title %><br>
<br>
<%= f.label :body %><br>
<%= f.text_field :body %><br>
<br>
<%= f.label :name %> <br>
<%= f.text_field :name %><br>
<br>
<br>
<%= f.label :path %><br>
<%= f.file_field :path %><br>
<%= f.submit %>
<% end %>
[document.rb]
class Document < ActiveRecord::Base
validates :name, presence: true
validates :path, presence: true
validates :resource_type, presence: true
validates :resource_id, presence: true
mount_uploader :path, PathUploader
validates :name, presence: true
# def self.abc
# params.permit(:name,:path)
# end
def initialize(params,file)
params=file[:name]
#params.permit(name =>:name,path =>:path)
end
end
undefined method `name' for #
You're referencing a non-existent attributes for your Post form:
<%= form_for #post,html: { multipart: true } do |f| %>
<%= f.label :title %><br>
<%= f.text_field :title %><br>
<br>
<%= f.label :body %><br>
<%= f.text_field :body %><br>
<%= f.submit %>
<% end %>
Remove :name & :path references.
--
If you want to pass "extra" attributes to another model, you need to use accepts_nested_attributes_for or set the params separately to your "primary" model:
#app/models/post.rb
class Post < ActiveRecord::Base
has_many :documents
accepts_nested_attributes_for :documents
end
#app/models/document.rb
class Document < ActiveRecord::Base
belongs_to :post
end
This will allow you to pass the documents as "nested" attributes of your Post model:
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
def new
#post = Post.new
#post.documents.build
end
def create
#post = Post.new post_params
#post.save
end
private
def post_params
params.require(:post).permit(:title, :body, documents_attributes: [:name, :path])
end
end
#app/views/posts/_form.html.erb
<%= form_for #post do |f| %>
<%= f.text_field :title %>
<%= f.text_area :body %>
<%= f.fields_for :documents do |d| %>
<%= d.text_field :name %>
<%= d.text_field :path %>
<% end %>
<%= f.submit %>
<% end %>
So undefined method on a model will indicate that, well, the method doesn't exist on the model. Want to see a model's methods? Post.methods. However, in this example, the column name is not defined on the model., and you're trying to tell Post that it has a name. What you need to do is nest your parameters.
While there is a ton of cleaning up that might want to focus on first, your answer is found in the accepts_nestable_attributes_for class methods, as shown here, http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html, and strong_params documentation as shown here, http://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html
In your case, you want to create a new document from a post. Your permitted params hash will look like this,
params.require(:post).permit(:title, :body, :document_attributes => [:name])
Ensure that document_attributes is singular; if a person has_many pets (for example), then you'd have pets_attributes.
In your form, something that often trips people up is the builder.
<%= form_for #post do |f| %>
<%= f.text_field :title %>
<%= f.text_field :body %>
<%= f.fields_for #post.document do |document_field| %>
<%= document_field.text_field :name %>
<% end %>
<%= f.submit %>
<% end %>
Make sure that you're telling ERB that <%= f.fields_for %>, not just <% f.fields_for %>.
I have no idea why it is showing me this problem NO Method in Author.
Showing /home/muba/muba/app/views/author/new.html.erb where line #3 raised:
<%= form_for #author do |f| %>
<div class="form-group">
<%= f.label :first_name %><br/>
<%= f.text_field :first_name, class:'form-control' %><br/>
</div>
<% end %>
Author.rb Controller
class AuthorController < ApplicationController
def new
#page_title= 'Add new Author'
#author = Author.new
end
def create
end
def update
end
def edit
end
def destroy
end
def index
end
def show
end
def author_params
params.require(:author).permit(:first_name, :last_name)
end
end
But Same Thing I did for my Categories page that is working. The code is:
<%= form_for #category do |f| %>
<div class="form-group">
<%= f.label :name %><br/>
<%= f.text_field :name, class:'form-control' %><br/>
</div>
<%=f.submit "Submit", class:'btn btn-primary' %>
<%= link_to "Cancel", categories_path, class:'btn btn-default' %>
<% end %>
Category.rb controller
class CategoriesController < ApplicationController
def new
#page_title= 'Add new Category'
#category= Category.new
end
def create
end
def update
end
def edit
end
def destroy
end
def index
end
def show
end
def category_params
params.require(:category).permit(:name)
end
end
I noticed there is issue in your first line it should be
class AuthorsController < ApplicationController
instead of
class AuthorController < ApplicationController
also make sure your controller's file name is authors_controller.rb
Ruby on Rails follow linguistic convention. Check Ruby and Rails Naming Conventions
In your view :
<%= form_for #author do |f| %>
<div class="form-group">
<%= f.label :first_name %><br/>
<%= f.text_field :first_name, class:'form-control' %><br/>
</div>
<% end %>
here , make sure the first_name is present as a column name in your Author model in the table in the database . It will check the exact name in the table which will raise an error if the name doesn't match .
I hope this helps.
I'm trying to create a reviews model for company pages. For this I have:
Models
user.rb
has_many :reviews
class Review < ActiveRecord::Base
belongs_to :user
belongs_to :company
end
class Company < ActiveRecord::Base
has_many :reviews
end
My reviews controller is:
def create
#company = Company.find_by_slug(params[:id])
#review = #company.reviews.create(params[:review])
#review.save
redirect_to company_path(#company)
end
and I have this code in the company show page:
<% #company.reviews.each do |review| %>
<p>
<strong>Title:</strong>
<%= review.title %>
</p>
<p>
<strong>Avantage:</strong>
<%= review.avantage %>
</p>
<p>
<strong>Inconvenient:</strong>
<%= review.inconvenient %>
</p>
<% end %>
</br>
<%= form_for([#company, #company.reviews.build]) do |f| %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :avantage %><br>
<%= f.text_area :avantage %>
</div>
<div class="field">
<%= f.label :inconvenient %><br>
<%= f.text_area :inconvenient %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
However, when I go to a specific company page and try to create a review for this company I'm getting this error message undefined method reviewsfor nil:NilClass
Instead of #company = Company.find_by_slug(params[:id]) use this code #company = Company.friendly.find(params[:company_id])
There are a couple of things you may find useful:
If you're using Rails 4, you may encounter a further problem. In the third line of your create method, you are using unsecure params directly in a .create call. Check out the Rails Guide page on "strong params".
If you implement strong parameters as mentioned above, you should probably deliberately omit the company_id field from the list of permitted params.
Assuming your users are allowed to write a review for any company in your system, it might be simpler for you to embed the company_id as a hidden field in your form. This would allow you to also simplify the controller method. For example:
# _form.html.erb
<%= form_for(#review) do |f| %>
<%= f.hidden_field :company_id, value: #company.id %>
...bla bla bla
<% end %>
Then, in your reviews_controller...
# reviews_controller.rb
def create
#review = Review.new(approved_params)
if #review.save
flash[:success] = 'Review created!'
else
flash[:error] = "Review wasn't saved"
end
#company = #review.company
redirect_to #company
end
def approved_params
params.require(:review).permit(:title, :avantage, :inconvenient, :company_id)
end
In your companies_controller, you should add this to your show method
# companies_controller.rb
def show
#company = Company.find(params[:id]
# add this line below...
#review = Review.new
end
I hope this helps.
I have this controller
class PeopleController < ApplicationController
def new
#person = Person.new
#person.phones.new
end
# this is the action that gets called by the form
def create
render text: person_params.inspect
# #person = Person.new(person_params)
# #person.save
# redirect_to people_path
end
def index
#person = Person.all
end
private
def person_params
params.require(:person).permit(:name, phones_attributes: [ :id, :phone_number ])
end
end
and this view
<%= form_for :person, url: people_path do |f| %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<%= f.fields_for :phones do |f_phone| %>
<div class="field">
<p>
<%= f_phone.label :phone_number %><br />
<%= f_phone.text_field :phone_number %>
</p>
</div>
<% end %>
<p>
<%= f.submit %>
</p>
<% end %>
When I fill out both form fields and hit "Save Person" I only get {"name"=>"foo"} - the phone number seems to vanish.
However, when I change phones_attributes to phones I get {"name"=>"foo", "phones"=>{"phone_number"=>"123"}} (this would however cause problems with the create function.
What's wrong here?
Please note that this question is strongly related to that one: accepts_nested_attributes_for: What am I doing wrong as well as to this posting: https://groups.google.com/forum/#!topic/rubyonrails-talk/4RF_CFChua0
You don't have #phones defined in the controller:
def new
#person = Person.new
#phones = #person.phones.new
end
Finally found the problem. In the view there should be
<%= form_for #person, url: people_path do |f| %>
Instead of
<%= form_for :person, url: people_path do |f| %>
#phron said that already here:
accepts_nested_attributes_for: What am I doing wrong