I can't save the data into my model. Every time when the code run it will ran into the else statement which failed to save the data in the CREATE action. Any idea?
This is my invoices_controller.rb
class InvoicesController < ApplicationController
def new
#permits = Permit.find(params[:permit_id])
#invoice = Invoice.new
end
def create
#permit = Permit.find(params[:permit_id])
#invoice = #permit.build_invoice(invoice_params)
if #invoice.save
redirect_to payment_path
else
redirect_to root_path
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_invoice
#invoice = Invoice.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def invoice_params
params.require(:invoice).permit(:vehicle_type, :name, :department, :carplate, :duration, :permitstart, :permitend, :price, :time)
end
end
Invoices/new.html.erb ( This is the data I wanted to save)
<% provide(:title, 'Invoice') %>
<h1>Invoice</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3" id="datashow">
<%= form_for(#invoice) do |f| %>
<h2>Time : <%=#permits.created_at%></h2></br>
<h2>Invoice ID : <%=#permits.id%></h2></br>
<%= f.label :"Vehicle" %>
<%= f.text_field :vehicle_type, :value => #permits.vehicle_type, readonly: true %>
<%= f.label :"License Plate" %>
<%= f.text_field :carplate, :value => #permits.carplate, readonly: true %>
<%= f.label :"Student ID" %>
<%= f.text_field :studentid, :value => #permits.studentid, readonly: true %>
<%= f.label :name %>
<%= f.text_field :name, :value => #permits.name, readonly: true %>
<%= f.label :"Department of applicant" %>
<%= f.text_field :department, :value => #permits.department, readonly: true %>
<%= f.label :permit_start %>
<%= f.text_field :permitstart, :value => #permits.permitstart, readonly: true %>
<%= f.label :permit_end %>
<%= f.text_field :permitend, :value => #permits.permitend, readonly: true %>
<%= f.label :"Price" %>
<%= (f.text_field :price, :value => '$AUD 50' , readonly: true) %>
<%= hidden_field_tag(:permit_id, #permits.id) %>
<%= f.submit "Make Payment", class: "btn btn-primary" %>
<% end %>
</div>
</div>
Invoice.rb
class Invoice < ApplicationRecord
belongs_to :user
has_one :receipt
belongs_to :permit
end
Permit.rb
class Permit < ApplicationRecord
belongs_to :user
has_one :invoice
end
If you are unsure why your object is not created, you have multiple options.
First you can use #invoice.save! instead of #invoice.save during debugging. This will raise an exception and give you some clues, what's going wrong.
Or you can use a debugger and inspect #invoice.errors.full_messages.
Further more you can output #invoice.errors.full_messages via Rails.logger.error #invoice.errors.full_messages.to_sentence.
Or you can use the error message as a flash message flash[:error] = #item.errors.full_messages.to_sentence
This should help you find the error.
from: build method on ruby on rails
build won't "create" a record in database, just create a new object in memory so that the view can take this object and display something, especially for a form.
So build isn't working because you aren't creating (create and saving) a record. build doesn't save a record.
Try:
def create
#permit = Permit.find(params[:permit_id])
#invoice = #permit.invoices.create(invoice_params)
if #invoice.save
redirect_to payment_path
else
redirect_to root_path
end
end
Related
I am running a Rails 5.1 app with the following information:
Models
class Company < ApplicationRecord
has_many :complaints
accepts_nested_attributes_for :complaints
validates :name, presence: true
end
class Complaint < ApplicationRecord
belongs_to :company
validates :username, :priority, presence: true
end
Controller
class ComplaintController < ApplicationController
def new
#company = Company.new
#company.complaints.build
end
def create
#company = Company.new(company_params)
respond_to do |format|
if #company.save
format.html { redirect_to complaint_url }
else
format.html { render :new }
end
end
end
private
def company_params
params.require(:company).permit(:name, complaints_attributes: [:username, :priority])
end
Form in view
<%= form_for #company do |f| %>
<%= f.label :name, "Company" %>
<%= f.text_field :name, type: "text" %>
<%= f.fields_for :complaints do |complaint| %>
<%= complaint.label :username, "Username" %>
<%= complaint.text_field :username %>
<%= complaint.label :priority, "Priority" %>
<%= complaint.text_field :priority %>
<% end %>
<%= f.submit 'Submit' %>
<% end %>
If I have just one input field for the complaint_attributes part of the form (in other words just one field for username and one field for priority as shown above), this works just fine.
However, if I want to have multiple fields for username/priority in the form, so that I can submit multiple username/priority combinations in a single submission, I find that submitting the form will only save the last username/priority values from the form. Example of this view would be:
<%= form_for #company do |f| %>
<%= f.label :name, "Company" %>
<%= f.text_field :name, type: "text" %>
<%= f.fields_for :complaints do |complaint| %>
<div>
<%= complaint.label :username, "Username" %>
<%= complaint.text_field :username %>
<%= complaint.label :priority, "Priority" %>
<%= complaint.text_field :priority %>
</div>
<div>
<%= complaint.label :username, "Username" %>
<%= complaint.text_field :username %>
<%= complaint.label :priority, "Priority" %>
<%= complaint.text_field :priority %>
</div>
<% end %>
<%= f.submit 'Submit' %>
<% end %>
I noticed that when submitting the form, I get a hash like this (for submitting single complaint):
{"utf8"=>"✓", "authenticity_token"=>"...", "company"=>{"name"=>"Test", "complaints_attributes"=>{"0"=>{"username"=>"test_person", "priority"=>"1"}}}, "commit"=>"Submit"}
Is there any way to modify the params to make it similar to this and have it saved to the DB?:
{"utf8"=>"✓", "authenticity_token"=>"...", "company"=>{"name"=>"Test", "complaints_attributes"=>{"0"=>{"username"=>"test_person", "priority"=>"1"}"1"=>{"username"=>"test_person", "priority"=>"2"}}}, "commit"=>"Submit"}
Or if not the above, what would be the best way to have the username/priority values saved if using multiple fields for them in a single form?
EDIT: I should point out that I can dynamically add the username/priority field groups as needed, so I don't want to be restricted to a set number.
the second block will override the first fields... you should instead build many complaints in the controller:
def new
#company = Company.new
3.times { #company.complaints.build }
end
and then with the following form it should generate to inputs according to the number of complaints you have built:
<%= form_for #company do |f| %>
<%= f.label :name, "Company" %>
<%= f.text_field :name, type: "text" %>
<%= f.fields_for :complaints do |complaint| %>
<%= complaint.label :username, "Username" %>
<%= complaint.text_field :username %>
<%= complaint.label :priority, "Priority" %>
<%= complaint.text_field :priority %>
<% end %>
<%= f.submit 'Submit' %>
<% end %>
I have the following models:
class Person < ApplicationRecord
has_many :interests, dependent: :destroy
accepts_nested_attributes_for :interests
validates_presence_of :email
validates_inclusion_of :gender, :in => %w(M F), message: "Gender can only be in M or F"
has_secure_password
def name
"#{first_name} #{last_name}"
end
def interests_concatenated
interests.map { |i| i.interest }.join(", ")
end
end
class Interest < ApplicationRecord
belongs_to :person
end
My controller is as follows:
class PeopleController < ApplicationController
def index
#person = Person.all
end
def new
#person = Person.new
#person.interests.build
end
def create
#person = Person.new(people_params)
if #person.save
session[:user_id] = #person.id
redirect_to(people_path)
else
flash = "Email or gender can't be blank!"
render 'new'
end
end
private
def people_params
params.require(:person).permit(:email, :first_name, :last_name, :gender, :password,:password_confirmation, interests_attributes: [:hobby])
end
end
My form is as follows:
<%= form_for #person do |f| %>
<p>
<%= f.label :email %> <br>
<%= f.text_field :email %>
</p>
<p>
<%= f.label :first_name %> <br>
<%= f.text_field :first_name %>
</p>
<p>
<%= f.label :last_name %> <br>
<%= f.text_field :last_name %>
</p>
<p>
<%= f.label :gender %> <br>
<%= f.label(:gender_male, "Male") %>
<%= f.radio_button(:gender, "M") %> <br>
<%= f.label(:gender_female, "Female") %>
<%= f.radio_button(:gender, "F") %> <br>
</p>
<p>
<%= f.label :password %> <br>
<%= f.password_field :password %>
</p>
<p>
<%= f.label :password_confirmation %> <br>
<%= f.password_field :password_confirmation %>
</p>
<p>
<%= f.fields_for :interests do |i| %>
<%= i.label :hobby %>
<%= i.text_field :hobby %>
<% end %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
Here is the byebug console log when I run it:
Very stumped why it's not working. Could it be something to do with the parameters?
Here is the log file when I submit the form:
Instead of:
#interests = #person.interests.new
try
#interests = #person.interests.build
new creates a fresh, clean, completely empty new object... but build is the special Rails association method that will fill it with appropriate defaults (like, eg the right person_id)
I found a working solution by adding this in my interests model:
class Interest < ApplicationRecord
belongs_to :person, **optional: true**
end
Since #person fails to save each time, the biggest clue was in the error message "Interest person must exist", I found this StackOverflow solution to be helpful. Also this blog post on why this is needed was helpful in shedding light on the issue.
Thanks to everyone that weighed in on it!
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 am trying to build a basic recipes app but am having trouble allowing the user to input multiple ingredients for one recipe. The array of permitted params for the ingredients ends up empty. So I guess my question is - how do I permit the array of ingredients?
My controller:
class RecipesController < ApplicationController
def new
#recipe = Recipe.new
#ingredient = Ingredient.new
end
def create
safe_params = params.require(:recipe).permit(:title, :instruction, :category_id)
ingredient_params = params.require(:recipe).permit(:ingredient => [])
#recipe = Recipe.new(safe_params)
#recipe.save
ingredient_params.each do |i|
#recipe.ingredients << Ingredient.find_or_create_by(name: i[:ingredient][:name])
end
render body: YAML::dump(ingredient_params)
#redirect_to index_path(id: #recipe.id)
end
end
Form:
<%= form_for(#recipe, :url => create_path) do |f| %>
<%= f.label :category %>
<%= f.select :category_id, options_for_select(Category.all.map{|c|[c.title, c.id]}) %>
<%= f.label :title %>
<%= f.text_field :title%>
<%= f.label :instruction %>
<%= f.text_area(:instruction, size: "50x10") %>
<%= f.fields_for "ingredients[]", #ingredient do |i| %>
<%= i.label :name %>
<%= i.text_field :name %>
<%= i.text_field :name %>
<%= i.text_field :name %>
<% end %>
<%= f.submit "Submit" %>
<% end %>
Models:
class Recipe < ActiveRecord::Base
has_and_belongs_to_many :ingredients
accepts_nested_attributes_for :ingredients
belongs_to :category
end
class Category < ActiveRecord::Base
has_many :recipes
end
class Ingredient < ActiveRecord::Base
has_and_belongs_to_many :recipes
end
There are several issues here, I'll just provide what I'd do:
#app/controllers/recipes_controller.rb
class RecipesController < ApplicationController
def new
#recipe = Recipe.new
#recipe.ingredients.new
end
def create
#recipe = Recipe.new safe_params
#recipe.save
end
private
def safe_params
params.require(:recipe).permit(:title, :instruction, :category_id, ingredients_attributes: [:name])
end
end
#app/views/recipes/new.html.erb
<%= form_for #recipe do |f| %>
<%= f.label :category %>
<%= f.collection_select :category_id, Category.all, :id, :name %>
<%= f.label :title %>
<%= f.text_field :title%>
<%= f.label :instruction %>
<%= f.text_area(:instruction, size: "50x10") %>
<%= f.fields_for :ingredients do |i| %>
<%= i.label :name %>
<%= i.text_field :name %>
<% end %>
<%= f.submit "Submit" %>
<% end %>
If you wanted to have multiple ingredients fields, you'll have to build multiple objects in the controller:
def new
#recipe = Recipe.new
3.times do
#recipe.ingedients.build
end
end
Everything else looks like it will work well.
--
As an extra, if you want to populate has_and_belongs_to_many relationships, you'll be able to just pass the [relationship]_ids parameter:
<%= form_for #recipe do |f| %>
<%= f.collection_select :ingredient_ids, Ingredient.all, :id, :name %>
<%= f.submit %>
<% end %>
This will only work for currently existing ingredients. If you want to create new ingredients, the above will work.
Need to make few changes to your code
class RecipesController < ApplicationController
def new
#recipe = Recipe.new
# here you can decide how many ingredients do you want. (Not in form looping through text fields)
3.times do
ingredient = #recipe.ingredients.build
end
end
so fields for ingredients were get generated for three times.
<%= f.fields_for :ingredients do |i| %>
<%= i.label :name %>
<%= i.text_field :name %>
<% end %>
Please go through following link, it will clear your idea about nested forms
http://railscasts.com/episodes/196-nested-model-form-part-1
I am having problems with my ruby on rails app. I have two models - 'patient' and 'address', a patient has one address, and an address belongs to a patient.
Patient.rb
class Patient < ActiveRecord::Base
has_many :charge_slips
has_one :address
validates_presence_of :last_name
validates_presence_of :first_name
validates_presence_of :middle_name
end
Address.rb
class Address < ActiveRecord::Base
belongs_to :patient
validates_associated :patient
end
Patient-controller.rb
class PatientController < ApplicationController
def index
#title = "Outpatient Services - Patient"
#today = Date.today.to_formatted_s(:long)
#patients = Patient.find(:all)
end
def new
#patient = Patient.new
#address = Address.new
end
def create
#patient = Patient.new(params[:patient])
#patient.created_on = Date.today.to_formatted_s(:long)
if #patient.save
#address = Address.new(params[:address])
#address.patient_id = #patient.id
if #address.save
redirect_to :action => 'index'
else
redirect_to :action => 'new'
end
redirect_to :action => 'index'
else
redirect_to :action => 'new'
end
end
end
new.html.rb
<%= content_tag('h3', 'Create New Patient') %>
<hr>
<% form_for #patient, :url => { :action => "create" } do |patient_form| -%>
<%= error_messages_for :patient %>
<%= patient_form.label :last_name, 'Last Name:' %> <%= patient_form.text_field :last_name, :size => 30 %><br>
<%= patient_form.label :first_name, 'First Name:' %> <%= patient_form.text_field :first_name, :size => 30 %><br>
<%= patient_form.label :middle_name, 'Middle Name:' %> <%= patient_form.text_field :middle_name, :size => 30 %><br>
<fieldset>
<legend>Patient's Permanent Address</legend>
<%= error_messages_for :address %>
<% patient_form.fields_for #address do |address_fields| -%>
<%= address_fields.label :street_name, 'Street Name:' %> <%= address_fields.text_field :street_name %><br>
<%= address_fields.label :barangay, 'Barangay:' %> <%= address_fields.text_field :barangay %><br>
<%= address_fields.label :city_municipality, 'City/Municipality:' %> <%= address_fields.text_field :city_municipality %><br>
<%= address_fields.label :country, 'Country:' %> <%= address_fields.text_field :country %><br>
<%= address_fields.label :zip_cide, 'Zip Code:' %> <%= address_fields.text_field :zip_code %><br>
<% end -%>
</fieldset>
<%= submit_tag "Add Patient" %>
<% end -%>
Everytime I add a new patient an error is thrown. Here is a part of the error:
ActiveRecord::AssociationTypeMismatch in PatientController#create
Address(#31360520) expected, got HashWithIndifferentAccess(#23815500)
RAILS_ROOT: C:/www/Outpatient Application Trace | Framework Trace | Full Trace
C:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.3/lib/active_record/associations/association_proxy.rb:263:in `raise_on_type_mismatch'
C:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.3/lib/active_record/associations/has_one_association.rb:52:in `replace'
C:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.3/lib/active_record/associations.rb:1246:in `address='
C:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.3/lib/active_record/base.rb:2740:in `send'
C:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.3/lib/active_record/base.rb:2740:in `attributes='
C:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.3/lib/active_record/base.rb:2736:in `each'
C:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.3/lib/active_record/base.rb:2736:in `attributes='
C:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.3/lib/active_record/base.rb:2434:in `initialize'
C:/www/Outpatient/app/controllers/patient_controller.rb:14:in `new'
C:/www/Outpatient/app/controllers/patient_controller.rb:14:in `create'
I am new to RoR and would like to learn the language through practice. I want to know what might be wrong with the code. Thanks!
First your Patient model needs an accepts_nested_attributes_for
class Patient < ActiveRecord::Base
has_many :charge_slips
has_one :address
validates_presence_of :last_name
validates_presence_of :first_name
validates_presence_of :middle_name
accepts_nested_attributes_for :address
end
Your controller can be simplified a great deal. There is no need to save the address separately since #patient.save will take care of that. You don't need to set
the created_on attribute manually since it will be set automagically :) Also when #patient.save fails you probably want to render :action => 'new' and not redirect_to :action => 'new'. This will redisplay the form with any validation errors (redirect_to will not.)
Also note that i renamed your controller class to PatientsController instead of PatientController. This will be more in line with Rails' RESTful conventions and will also help you simplify your view a bit. If you do this you'll need a map.resources :patients in your routes.db file, and you'll need to rename your files too.
class PatientsController < ApplicationController
def index
#title = "Outpatient Services - Patient"
#today = Date.today.to_formatted_s(:long)
#patients = Patient.find(:all)
end
def new
#patient = Patient.new
#patient.build_address
end
def create
#patient = Patient.new(params[:patient])
if #patient.save
redirect_to :action => 'index'
else
render :action => 'new'
end
end
end
Your view has a small error. It needs to be fields_for :address and not fields_for #address. Also since your controller is now RESTful your can remove the :url => { :action => "create" } part.
<%= content_tag('h3', 'Create New Patient') %>
<hr>
<% form_for #patient do |patient_form| -%>
<%= error_messages_for :patient %>
<%= patient_form.label :last_name, 'Last Name:' %> <%= patient_form.text_field :last_name, :size => 30 %><br>
<%= patient_form.label :first_name, 'First Name:' %> <%= patient_form.text_field :first_name, :size => 30 %><br>
<%= patient_form.label :middle_name, 'Middle Name:' %> <%= patient_form.text_field :middle_name, :size => 30 %><br>
<fieldset>
<legend>Patient's Permanent Address</legend>
<%= error_messages_for :address %>
<% patient_form.fields_for :address do |address_fields| -%>
<%= address_fields.label :street_name, 'Street Name:' %> <%= address_fields.text_field :street_name %><br>
<%= address_fields.label :barangay, 'Barangay:' %> <%= address_fields.text_field :barangay %><br>
<%= address_fields.label :city_municipality, 'City/Municipality:' %> <%= address_fields.text_field :city_municipality %><br>
<%= address_fields.label :country, 'Country:' %> <%= address_fields.text_field :country %><br>
<%= address_fields.label :zip_cide, 'Zip Code:' %> <%= address_fields.text_field :zip_code %><br>
<% end -%>
</fieldset>
<%= submit_tag "Add Patient" %>
<% end -%>
Hope this helps :)