Apologies in advance if this is a stupid question. I am learning Rails from scratch, so thanks for bearing with me.
So, I have a 'Person' model, shown below
class Person < ActiveRecord::Base
belongs_to :team
has_and_belongs_to_many :events, through: :event_people
validates :first_name, presence: true, length: { maximum: 255 }
validates :last_name, presence: true, length: { maximum: 255 }
validates :email, presence: true, length: { maximum: 255 }
end
and have created a form to create a new person, with associated team ID
<h1>Add new person</h1>
<div class="row">
<div class="col-md-8">
<%= form_with url: new_person_path do |person| %>
<div class="form-group">
<%= person.label :first_name %>
<%= person.text_field :name, id: :person_first_name, class:"form-control" %>
</div>
<div class="form-group">
<%= person.label :last_name %>
<%= person.text_field :name, id: :person_last_name, class:"form-control" %>
</div>
<div class="form-group">
<%= person.label :email %>
<%= person.text_field :email, id: :person_email, class:"form-control" %>
</div>
<div class="form-group">
<%= person.label :job_title %>
<%= person.text_field :job_title, id: :person_job_title, class:"form-control" %>
</div>
<div class="form-group">
<% #teams = Team.all %>
<%= person.label :team %>
<%= person.select(:team_id, #teams.collect{|a| [a.name, a.id]}, { include_blank: true }, {:class => "form-control"}) %>
</div>
<div class="actions">
<%= person.submit 'Submit button', {:class => 'btn btn-primary'}%>
</div>
<% end %>
</div>
</div>
and my routes.rb file is set up like this:
Rails.application.routes.draw do
resources :people
resources :teams
resources :organisations
resources :events
end
But whenever I submit the form to create a new person, I get the following error in the console:
POST http://localhost:3000/people/new 404 (Not Found)
Event though the relevant methods exist in my people_controller.rb:
class PeopleController < ApplicationController
def index
#people = Person.all
end
def show
#people = Person.find(params[:id])
end
def new
#people = Person.new
respond_to do |format|
format.html # new.html.erb
format.json { render :json => #people }
end
end
def create
#people = Person.new(params[:person])
respond_to do |format|
if #people.save
format.html { redirect_to(#people,
:notice => 'Person was successfully created.') }
format.json { render :json => #people,
:status => :created, :location => #people }
else
format.html { render :action => "new" }
format.json { render :json => #people.errors,
:status => :unprocessable_entity }
end
end
end
Can anyone suggest what I might be doing wrong?
When you have an underlying model you will want to use <%= form_with model: #people do |person| %>
Reference: http://api.rubyonrails.org/v5.1/classes/ActionView/Helpers/FormHelper.html#method-i-form_with
You are posting to a method which, at least as far as I can tell, is supposed to output an empty form for entering attributes of your person.
Try a GET first, which would be in keeping with the rails interpretation of is REST URLs. Likely, the correct route also has no /new at the end.
Next, try rails routes to list all available routes.
By the way, if in development, rails should give you a meaningful, large help page, likely including available routes. Check your development.rb for some flag to enable that.
Related
So after two days of reading up on this topic I feel lost between problems in older versions of rails and some individual stuff that I probably did wrong.
I have a form where a User can create an account. Every user needs to put in his number plate and a car is created if it does not yet exist with that number plate and assigned to the user.
My form shows the validation error messages for the user, but not for the car (it only marks the input fields for the number plates red).
So my questions is: How do I get the error messages to render be shown properly?
I also suspect after all the topics I read, that my user controller might be doing some stuff that I should not do manually.
My form:
<%= form_for(#user, html: { multipart: true }) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="row">
<div class="col-xs-6 col-xs-offset-3">
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Bestätigung" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
</div>
</div>
<div class="row">
<div class="col-xs-6 col-xs-offset-3">
<%= f.fields_for :car, #car do |car| %>
<%= car.label :plateprefix, "Nummernschild" %>
<div class="form-inline">
<%= car.text_field :plateprefix %>
<%= car.label :plate, "-" %>
<%= car.text_field :plate %>
</div>
<% end %>
</div>
</div>
user.rb:
class User < ActiveRecord::Base
belongs_to :car, inverse_of: :users
validates :name, presence: true, length: { maximum: 50 }
car.rb:
class Car < ActiveRecord::Base
has_many :users, inverse_of: :car
accepts_nested_attributes_for :users
validates :plateprefix, presence: true
and finally my car controller:
def new
#user = User.new
end
def create
if verify_recaptcha
#user = User.new(user_params)
if Car.exists?(:country => 'D', :plateprefix => params[:user][:car][:plateprefix].upcase, :plate => params[:user][:car][:plate].upcase)
#car = Car.find_by(:country => 'D', :plateprefix => params[:user][:car][:plateprefix].upcase, :plate => params[:user][:car][:plate].upcase)
#user.car_id = #car.id
else
#car = Car.new(:country => 'D', :plateprefix => params[:user][:car][:plateprefix].upcase, :plate => params[:user][:car][:plate].upcase)
#car.country = 'D'
#car.save
#user.car_id = #car.id
end
if #user.save
#user.send_activation_email
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
#user = User.new
render 'new'
end
else
#user = User.new
render 'new'
end
end
Ok, so after 2 further days of reading I solved it:
First I moved the accepts_nested_attributes from the car.rb to the user.rb (and from plural to singular)
user.rb
accepts_nested_attributes for :car
but then for some reason in my form I had to change the variable in the fields_for to plural (:cars - some of rails magic is beyond me but I am getting there I think)
Next i also recreated my controller to use the methods provided by ActiveModel/Record and went to do:
if Car.exists?(:country => 'D', :plateprefix => params[:user][:cars][:plateprefix].upcase, :plate => params[:user][:cars][:plate].upcase)
#user.car = Car.find_by(:country => 'D', :plateprefix => params[:user][:cars][:plateprefix].upcase, :plate => params[:user][:cars][:plate].upcase)
else
#user.create_car(:country => 'D', :plateprefix => params[:user][:cars][:plateprefix].upcase, :plate => params[:user][:cars][:plate].upcase)
end
So this cleaned up my code a bit and I finally got my errors to show properly!
I'm stuck and I don't know why it is not working right.
I have a model product which has many tags.
When I update the product rails update properly the products attributes but is creating another tag record instead of just updating it.
here is my code:
View form:
<%= form_for ([#product.user, #product]), id: 'edit_form' do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="field">
<%= f.fields_for :tags do |t| %>
<%= t.label :name %>
<%= t.text_field :name %>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
product model:
class Product < ActiveRecord::Base
belongs_to :user, :foreign_key => "user_id"
has_many :tags, :dependent => :destroy
accepts_nested_attributes_for :tags, reject_if: :all_blank, allow_destroy: true, :update_only => true
end
tags model:
class Tag < ActiveRecord::Base
belongs_to :product, :foreign_key => "product_id"
# before_save { name.downcase! }
end
product controller:
def edit
user = User.find(params[:user_id])
#product = user.products.find(params[:id])
#tags = #product.tags.all
respond_to do |format|
format.html
format.js
end
end
def update
user = User.find(params[:user_id])
#product = user.products.find(params[:id])
#tags = #product.tags.all
respond_to do |format|
if #product.update(product_params)
format.html { redirect_to([#product.user, #product], :notice => 'Product successfully updated.') }
else
format.html { render :action => "edit" }
end
end
end
def product_params
params.require(:product).permit(:name, :description, tags_attributes: :name)
end
You have to pass the tag id in the permit params in your controller
def product_params
params.require(:product).permit(:name, :description, tags_attributes: [:id,:name])
end
I am having a hard time with what I thought would be an textbook update example. Searched SO but couldn't find the answer. In short, when I click submit the user_id in the profile model gets wiped out and no other data gets saved. I am using Rails 3.2.2.
Here is what I have...
User model...
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :profile_attributes
has_one :profile
accepts_nested_attributes_for :profile
end
Profile model...
class Profile < ActiveRecord::Base
validates :first_name, :presence => true
validates :last_name, :presence => true
belongs_to :user
attr_accessible :first_name, :last_name
end
Users controller...
class UsersController < ApplicationController
def new
#user = User.new
#user.accounts_users.build()
#user.build_profile()
respond_to do |format|
format.html # new.html.erb
format.json { render json: #user }
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to #user, notice: 'Profile was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
end
Nested form...
<%= form_for #user, :validate => true do |f| %>
<%= f.fields_for :profile do |p| %>
<fieldset>
<div class="field">
<%= p.label :first_name %>
<%= p.text_field :first_name %>
</div>
<div class="field">
<%= p.label :last_name %>
<%= p.text_field :last_name %>
</div>
<% end %>
<div class="field">
<%= f.label :email %>
<%= f.text_field :email %>
</div>
<div class="actions">
<%= f.submit 'Edit Profile', :class => "btn btn-large btn-success" %>
<%= cancel %>
</div>
</fieldset>
<% end %>
Edit: I edited the UsersController to include the new action. Why would the new action affect the edit/update actions?
I had a similar problem before. Can we see the code for your new action? Do you have #user.build_profile in there (right beneath #user = User.new)? Or is the new action working fine?
I'm struggling to come up with the proper way to design a form that will allow me to input data for two different models. The form is for an 'Incident', which has the following relationships:
belongs_to :customer
belongs_to :user
has_one :incident_status
has_many :incident_notes
accepts_nested_attributes_for :incident_notes, :allow_destroy => false
So an incident is assigned to a 'Customer' and a 'User', and the user is able to add 'Notes' to the incident. I'm having trouble with the notes part of the form. Here how the form is being submitted:
{"commit"=>"Create",
"authenticity_token"=>"ECH5Ziv7JAuzs53kt5m/njT9w39UJhfJEs2x0Ms2NA0=",
"customer_id"=>"4",
"incident"=>{"title"=>"Something bad",
"incident_status_id"=>"2",
"user_id"=>"2",
"other_id"=>"AAA01-042310-001",
"incident_note"=>{"note"=>"This is a note"}}}
It appears to be attempting to add the incident_note as a field under 'Incident', rather than creating a new entry in the incident_note table with an incident_id foreign key linking back to the incident.
Here is the 'IncidentNote' model:
belongs_to :incident
belongs_to :user
Here is the form for 'Incident':
<% form_for([#customer,#incident]) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :other_id, "ID" %><br />
<%= f.text_field :capc_id %>
</p>
<p>
<%= f.label :title %><br />
<%= f.text_field :title %>
</p>
<p>
<%= label_tag 'user', 'Assign to user?' %>
<%= f.select :user_id, #users.collect {|u| [u.name, u.id]} %>
</p>
<p>
<%= f.label :incident_status, 'Status?' %>
<%= f.select :incident_status_id, #statuses.collect {|s| [s.name, s.id]} %>
</p>
<p>
<% f.fields_for :incident_note do |inote_form| %>
<%= inote_form.label :note, 'Add a Note' %>
<%= inote_form.text_area :note, :cols => 40, :rows => 20 %>
<% end %>
</p>
<p>
<%= f.submit "Create" %>
</p>
<% end %>
And finally, here are the incident_controller entries for New and Create.
New:
def new
#customer = current_user.customer
#incident = Incident.new
#users = #customer.users
#statuses = IncidentStatus.find(:all)
#incident_note = IncidentNote.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #incident }
end
end
Create:
def create
#users = #customer.users
#statuses = IncidentStatus.find(:all)
#incident = Incident.new(params[:incident])
#incident.customer = #customer
#incident_note = #incident.incident_note.build(params[:incident_note])
#incident_note.user = current_user
respond_to do |format|
if #incident.save
flash[:notice] = 'Incident was successfully created.'
format.html { redirect_to(#incident) }
format.xml { render :xml => #incident, :status => :created, :location => #incident }
else
format.html { render :action => "new" }
format.xml { render :xml => #incident.errors, :status => :unprocessable_entity }
end
end
end
I'm not really sure where to look at this point. I'm sure it's just a limitation of my current Rails skill (I don't know much). So if anyone can point me in the right direction I would be very appreciative. Please let me know if more information is needed!
Thanks!
Check api for fields_for method and scroll to one-to-many section.
Your model has many :incident_notes, not one incident_note, that is why it doesn't understand relationship and tries to find a field with this name.
So it should be:
<% f.fields_for :incident_notes do |inote_form| %>
<%= inote_form.label :note, 'Add a Note' %>
<%= inote_form.text_area :note, :cols => 40, :rows => 20 %>
<% end %>
It iterates through all incident_notes assigned to incident and produces fields for each of them.
You also have to build at least one note in you new action, otherwise there will be none:
def new
#incident = Incident.new
#incident.incident_notes.build
# ...
end
I have two models, links and tags, associated through a third, link_tags. The following code is in my Link model.
Associations:
class Link < ActiveRecord::Base
has_many :tags, :through => :link_tags
has_many :link_tags
accepts_nested_attributes_for :tags, :allow_destroy => :false,
:reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } }
end
class Tag < ActiveRecord::Base
has_many :links, :through => :link_tags
has_many :link_tags
end
class LinkTag < ActiveRecord::Base
belongs_to :link
belongs_to :tag
end
links_controller Actions:
def new
#link = #current_user.links.build
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #link }
end
end
def create
#link = #current_user.links.build(params[:link])
respond_to do |format|
if #link.save
flash[:notice] = 'Link was successfully created.'
format.html { redirect_to links_path }
format.xml { render :xml => #link, :status => :created, :location => #link }
else
format.html { render :action => "new" }
format.xml { render :xml => #link.errors, :status => :unprocessable_entity }
end
end
end
View code from new.html.erb:
<% form_for [current_user, #link], :url => account_links_path do |f| %>
<%= render :partial => "form", :locals => { :f => f } %>
<% end %>
And the corresponding partial:
<%= f.error_messages %>
<p>
<%= f.label :uri %><br />
<%= f.text_field :uri %>
</p>
<p>
<%= f.label :title %><br />
<%= f.text_field :title %>
</p>
<h2>Tags</h2>
<% f.fields_for :tags_attributes do |tag_form| %>
<p>
<%= tag_form.label :name, 'Tag:' %>
<%= tag_form.text_field :name %>
</p>
<% unless tag_form.object.nil? || tag_form.object.new_record? %>
<p>
<%= tag_form.label :_delete, 'Remove:' %>
<%= tag_form.check_box :_delete %>
</p>
<% end %>
<% end %>
<p>
<%= f.submit 'Update' %>
</p>
The following line of code, in the create action in the Link controller throws an error:
#link = #current_user.links.build(params[:link])
The error: Tag(#-621698598) expected, got Array(#-609734898)
Are there additional steps needed in the has_many => :through case? These seem to be the only indicated changes for the basic has_many case.
Take a look at the line of your code
<% f.fields_for :tags_attributes do |tag_form| %>
You need to use just :tags instead of :tags_attributes.
This will solve your issue
Make sure that You have build links and tags in your controller like
def new
#link = #current_user.links.build
#link.tags.build
end
I found this here on stackoverflow:
Rails nested form with has_many :through, how to edit attributes of join model?
please tell me if it worked.
In order for this to work, you need to pass in the right params hash:
params = {
:link => {
:tags_attributes => [
{:tag_one_attr => ...}, {:tag_two_attr => ...}
],
:link_attr => ...
}
}
And your controller will look like:
def create
#link = Link.create(params[:link]) # this will automatically build the rest for your
end
Try this:
<% f.fields_for :tags_attributes do |tag_form| %>
In your controller in the new action (that loads the form partial), are you building a #tag through your link?
So you should see something along the lines of:
#link = Link.new
#tag = #link.tags.build
It might be best to post the contents of the new and create action of your links_controller.
try
<% f.fields_for :tags do |tag_form| %>
(ie lose the _attributes in :tag_attributes) That's how I've usually done nested forms
You need to build a tag in your controller or in the view
def new
#link = #current_user.links.build
#link.tags.build
end
#in your view you can just use the association name
<% f.fields_for :tags do |tag_form| %>