I have this routing error when I add a consumption (after submit it fails) and I am stuck, What am I doing wrong?
A user can have several cars and for each of his car he wants to look after his gas consumptions.
I have three active record models
create_table "cars", force: :cascade do |t|
t.string "car_name"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "consumption_searches", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "consumptions", force: :cascade do |t|
t.float "total_price"
t.float "kilometers"
t.string "shop"
t.float "liter_price"
t.float "total_liters"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "car_id"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
car belongs_to :users and has_many :consumptions
user has_many :cars, and has_many :consumptions through: :cars
consumption belong_to :car and belongs_to :user
My create method in consumptions_controller.rb
def create
#car = Car.find(params[:car_id])
#consumption = Consumptions.new(consumption_params)
#consumption.car = #car
if #consumption.save!
redirect_to car_consumptions_path, notice: 'consumption was successfully created.'
else
render :new
end
end
cars_controller.rb
def show
#car = Car.find(params[:id])
#search = ConsumptionSearch.new(params[:search])
#consumptions = #search.date_range
#consumptions = #consumptions.order('created_at ASC').where(car_id: #car.id)
end
views/consumptions/_form.html.erb
<%= simple_form_for car_consumptions_path do |f| %>
...
<% end %>
routes.rb
Rails.application.routes.draw do
devise_for :users
root to: "cars#index"
resources :cars do
resources :consumptions
end
end
rails routes | grep consumption
car_consumptions GET /cars/:car_id/consumptions(.:format) consumptions#index
POST /cars/:car_id/consumptions(.:format) consumptions#create
new_car_consumption GET /cars/:car_id/consumptions/new(.:format) consumptions#new
edit_car_consumption GET /cars/:car_id/consumptions/:id/edit(.:format) consumptions#edit
car_consumption GET /cars/:car_id/consumptions/:id(.:format) consumptions#show
PATCH /cars/:car_id/consumptions/:id(.:format) consumptions#update
PUT /cars/:car_id/consumptions/:id(.:format) consumptions#update
DELETE /cars/:car_id/consumptions/:id(.:format) consumptions#destroy
As requested
EDIT
here is what I have in the HTML if it can help:
<form novalidate="novalidate" class="simple_form /cars/1/consumptions" action="/cars/1/consumptions/new" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="✓"><input type="hidden" name="authenticity_token" value="0nwq/pQSXCU2ptBjbewTCBffPLpZZUPAj6/HPQTGtYd8cHz9zv8R/C/JYnXPDpKw5o3/vGlVtav2Sa2nSvgOQdQ==">
Lets start with the controller
# GET /cars/:car_id/consumptions/new
def new
#car = Car.find(params[:car_id])
#consumption = #car.consumptions.new
end
# POST /cars/:car_id/consumptions
def create
#car = Car.find(params[:car_id])
#consumption = #car.consumptions.new(consumption_params)
# `.save!` will raise an exception and blow up if the record is invalid.
# Not good.
if #consumption.save
redirect_to car_consumptions_path(#car), notice: 'consumption was successfully created.'
else
render :new
end
end
Note the redirect:
# consumptions#index
redirect_to car_consumptions_path(#car), notice: 'consumption was successfully created.'
Since this is a nested route you need to provide the car_id segment.
You could also redirect to:
# consumptions#show
redirect_to [#car, #consumption], notice: 'consumption was successfully created.'
# or to cars#show
redirect_to #car, notice: 'consumption was successfully created.'
When using simple_form_for you pass it model instances that it binds the form to. When creating forms for nested routes you should pass an array:
<%= simple_form_for([#car, #consumption]) do |f| %>
<% end %>
This uses the polymorphic route helpers to find the correct path. You can use this same signature for link_to, redirect_to, url_for and path_for.
When declaring nested routes you should consider using the shallow option. It will only nest the collection routes (new, create, index) and not the member routes.
Try this:
<%= form_for [#cars, #consumptions] do |form| %>
...
<% end %>
or
<%= form_with(model: [ #cars, #consumptions ]) do |form| %>
*** Update:
resources :cars, shallow: true do
resources :consumptions
end
Into form:
<% = simple_form_for [#car, #consumption] do |f| %>
You are not showing your new action, but I assume you are setting a #car and a #consumption = #car.consumptions.build variables.
Try with this:
simple_form_for #consumption, url: url_for(controller: :consumptions, action: :create, car_id: #car.id) do |f|
It should work with simple_form_for [#car, #consumption] do |f| but you said "it doesn't work" which is too ambiguous (how does that not work? same error? new error? you should be more clear when responding to answers)
car_consumptions_path != car_consumption_path
I don't see any "car_consumptions" in your routes.
Related
I'm trying to build a very basic forum-like app, where Users can create Topics and reply to existing Topics.
The Topic creation works fine and I'm able to display Reply form, however, the Reply create action is not working properly. I don't have any errors, it just redirects_to topics_path.
This is following a tutorial, so the code is not mine. Is anyone able to spot the obvious cause for this? Any help much appreciated!
replies_controller.rb
def create
#topic = Topic.find(params[:topic_id])
#reply = #topic.replies.create(params[:reply].permit(:reply))
#reply.user_id = current_user.id if current_user
#reply.save
if #reply.save
redirect_to topic_path(#topic)
else
flash[:notice] = "Error."
redirect_to topics_path
end
end
reply.rb
class Reply < ApplicationRecord
belongs_to :post
belongs_to :user
end
replies/_form.html.erb
<%= form_for [#topic, #topic.replies.create] do |f| %>
<%= f.label :reply %>
<%= f.text_area :reply, class: "textarea", rows: "10" %>
<%= f.submit class: "button is-primary" %>
<% end %>
topic.rb
class Topic < ApplicationRecord
belongs_to :user
has_many :replies
end
schema.rb
create_table "topics", force: :cascade do |t|
t.string "title"
t.text "content"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
end
create_table "replies", force: :cascade do |t|
t.text "reply"
t.bigint "topic_id"
t.bigint "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["topic_id"], name: "index_replies_on_topic_id"
t.index ["user_id"], name: "index_replies_on_user_id"
end
In file replies/_form.html.erb
you should use build method build instead of create. Replace line:
<%= form_for [#topic, #topic.replies.create] do |f| %>
to
<%= form_for [#topic, #topic.replies.build] do |f| %>
There are some another problems with the code:
#reply = #topic.replies.create(params[:reply].permit(:reply))
In this line you call new + save, without user, which is required.
Change this to:
#reply = #topic.replies.new(params[:reply].permit(:reply))
Then, you call save twice:
#reply.save
if #reply.save
...
First line is unnecessary.
And finally, what is the cause of rollback, in your Reply model you have:
belongs_to :post
But in the schema.rb and in the params you have topic:
Schema.rb:
t.bigint "topic_id"
Params:
"reply"=>{"reply"=>"Test reply"}, "commit"=>"Create Reply", "topic_id"=>"4"}
Im creating a blog application in ruby on rails. I have "album" model and a "photo" model. In the album edit view I want to be able to delete the uploaded images which are associated to an album.
Unfortunately I'm getting an undefined method error for checkbox?!
Do you have any solution or hints to make this work?
How can I get to the bottom of why it does not work?
If you need further information just let me know.
albums/edit.html.erb
<%= form_for #album do |f| %>
<% if #album.errors.any? %>
<h2><%= pluralize(#album.errors.count, "error") %> prevent this post from saving:</h2>
<ul>
<% #album.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>k
</ul>
<% end %>
<div class="blog_edit_wrapper">
<%= f.label :title %><br>
<%= f.text_field :title, class: "blog_edit_title" %>
<br>
<br>
<%= f.submit 'Submit', class: "blog_edit_submit" %>
</div>
<% end %>
<hr>
<% #album.photos.each do |photo| %>
<%= image_tag(photo.gallery_image) %>
<%= photo.check_box :remove_gallery_image %>
<br>
<br>
<%= f.submit 'Submit', class: "blog_edit_submit" %>
<% end %>
photos_controller.rb
class PhotosController < ApplicationController
before_action :find_photo, only: [ :show, :edit, :update, :destroy]
before_action :set_album
def index
#photo = Photo.all
end
def new
#photo = #album.photos.new
end
def show
end
def create
#photo = #album.photos.new photo_params
#photo.album = #album
if #photo.save
redirect_to #album
else
render :new
end
end
def edit
end
def update
#photo = #album.photos.find params[:id]
if #photo.update photo_params
redirect_to #album, notice: "Your photo was successfully saved!"
else
render 'edit'
end
end
def destroy
#photo.destroy
redirect_to photos_path
end
private
def set_album
#album = Album.find params[:album_id]
end
def photo_params
params.require(:photo).permit(:gallery_image, :album_id, :title, :remove_gallery_image)
end
def find_photo
#photo = Photo.find(params[:id])
end
end
photo.rb
class Photo < ActiveRecord::Base
mount_uploader :gallery_image, ImageUploader
belongs_to :album
end
albums_controller.rb
class AlbumsController < ApplicationController
before_action :find_album, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
#albums = Album.all.order("created_at desc").paginate(page: params[:page], per_page: 12)
end
def new
#album = Album.new
end
def show
#photo = Photo.all
end
def create
#album = Album.new album_params
if #album.save
redirect_to #album
else
render :new
end
end
def edit
end
def update
if #album.update album_params
redirect_to #album, notice: "Your article was successfully saved!"
else
render 'edit'
end
end
def destroy
#album.destroy
redirect_to albums_path
end
private
def album_params
params.require(:album).permit(:title)
end
def find_album
#album = Album.find(params[:id])
end
end
album.rb
class Album < ActiveRecord::Base
has_many :photos
end
schema.rb
ActiveRecord::Schema.define(version: 20170424131600) do
create_table "albums", force: :cascade do |t|
t.string "title"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "friendly_id_slugs", force: :cascade do |t|
t.string "slug", null: false
t.integer "sluggable_id", null: false
t.string "sluggable_type", limit: 50
t.string "scope"
t.datetime "created_at"
end
add_index "friendly_id_slugs", ["slug", "sluggable_type", "scope"], name: "index_friendly_id_slugs_on_slug_and_sluggable_type_and_scope", unique: true
add_index "friendly_id_slugs", ["slug", "sluggable_type"], name: "index_friendly_id_slugs_on_slug_and_sluggable_type"
add_index "friendly_id_slugs", ["sluggable_id"], name: "index_friendly_id_slugs_on_sluggable_id"
add_index "friendly_id_slugs", ["sluggable_type"], name: "index_friendly_id_slugs_on_sluggable_type"
create_table "photos", force: :cascade do |t|
t.string "gallery_image"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "album_id"
end
create_table "posts", force: :cascade do |t|
t.string "title"
t.text "content"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "slug"
t.string "post_main_image"
end
add_index "posts", ["slug"], name: "index_posts_on_slug", unique: true
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
I believe your issue is that you're not using a form but just iterating over the photos.
Also you can place the code for editing the photos inside the first form.
Place this code below the <br> tags and remove the <% #album.photos.each do |photo| %> interation.
<%= fields_for(#album, #album.photos.build) do |f| %>
<%= image_tag(f.gallery_image)%>
<%= f.check_box :remove_gallery_image %>
<% end %>
Edit:
To clarify the above; place the aforementioned code inside your original edit #album form and remove the iteration for the #album.photos.each
I'm trying to create a posting form to post a url & some text. The nested form submits without an error but I can't display the subsequent content of that form in my show action controller.
Post Model
class Post < ActiveRecord::Base
has_many :texts
has_many :urls
accepts_nested_attributes_for :texts, :urls
end
Text Model
class Text < ActiveRecord::Base
belongs_to :post
end
Url Model
class Url < ActiveRecord::Base
belongs_to :post
end
Post Controller
class PostsController < ApplicationController
def index
#posts = Post.all
end
def show
#post = Post.find(params[:id])
#texts = #post.texts
#urls = #post.urls
end
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
if #post.save
redirect_to #post
else
render 'new'
end
end
private
def post_params
params.require(:post).permit(:texts_attributes => [:textattr], :urls_attributes => [:urlattr])
end
show.html.erb
<%= #text.textattr %>
<%= #url.urlattr %>
Database Schema
create_table "posts", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "texts", force: :cascade do |t|
t.text "textattr"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "post_id"
end
add_index "texts", ["post_id"], name: "index_texts_on_post_id"
create_table "urls", force: :cascade do |t|
t.string "urlattr"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "post_id"
end
add_index "urls", ["post_id"], name: "index_urls_on_post_id"
end
Error message after I press submit on the form
!(http://imgur.com/dzdss5z)
Your help would be amazing - thank you!
The #text and #url variables are never set in the controller's show action, so they are nil. Hence, there is an error when you try to call those attributes from them in the view.
You have set the #texts and #urls variables, so you can do something like this:
<% #texts.each do |text| %>
<%= text.textattr %>
<% end %>
<% #urls.each do |url| %>
<%= url.urlattr %>
<% end %>
I have two contollers:
students_controller.rb
teachers_controller.rb
Both of these controllers are generated with scaffolding. When I started the server, students's view /students/new is loading just fine but /teachers/new is giving me some error:
Error:
NoMethodError in TeachersController#new
undefined method `attribute_method_matcher' for nil:NilClass
Rails.root: /Users/pdahal/RubyOnRails/apps/myapplication
Application Trace app/controllers/teachers_controller.rb:27:in new'
app/controllers/teachers_controller.rb:27:innew'
teachers_controller.rb:
# GET /teachers/new
# GET /teachers/new.json
def new
#teacher = Teacher.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #teacher }
end
students_controller.rb
# GET /students/new
# GET /students/new.json
def new
#student = Student.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #student }
end
end
new.html.erb :
<%- model_class = Teacher -%>
<div class="page-header">
<h1><%=t '.title', :default => [:'helpers.titles.new', 'New %{model}'], :model => model_class.model_name.human.titleize %></h1>
</div>
<%= render :partial => 'form' %>
db/schema.rb
ActiveRecord::Schema.define(:version => 20131113051714) do
create_table "students", :force => true do |t|
t.string "name"
t.integer "age"
t.string "sex"
t.string "email"
t.string "major"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "teachers", :force => true do |t|
t.string "name"
t.integer "age"
t.string "email"
t.string "sex"
t.string "class"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
students_controller.rb has the exact same codes and is working just fine. Please help, thanks in advance.
The error was because I had used reserved word class on :
create_table "teachers", :force => true do |t|
t.string "name"
t.integer "age"
t.string "email"
t.string "sex"
t.string "class"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
I deleted that scaffold and regenerated new:
rails g teacher name age:integer email sex course
#replaced class with course, now its working great.
Thanks!
When I submit the form, the data is not being sent in the post and set via parameters. I can't for the life of me figure out why. This form is in the plan/show action, so that's why you see me setting the #action variable there. It is sent via JS.
routes.rb
resources :plans do
resources :actions
end
action.rb
belongs_to :plan
plan.rb
has_many :actions
plans_controller.rb
def show
#plan = current_user.plans.includes(:actions).find(params[:id])
#action = Action.new
respond_to do |format|
format.html # show.html.erb
format.json { render json: #plan }
end
end
actions_controller.rb
before_filter :get_plan
def create
#action = #plan.actions.new(params[:action])
#action.user_id = current_user.id
#action.save
end
private
def get_plan
#plan = current_user.plans.find(params[:plan_id])
end
create.js.erb in views/actions folder
$('div#actions').prepend("<%= escape_javascript(render #action) %>");
$('div#<%= dom_id(#action) %>').effect('highlight');
_form.html.erb partial
<%= form_for ([#plan, #action]), remote: true do |f| %>
<%= f.text_field :desc %>
<%= f.number_field :days %>
<%= f.submit %>
<% end %>
parameters sent via POST (missing action hash - why??)
Started POST "/plans/1/actions"
Parameters: {"utf8"=>"✓", "authenticity_token"=>"**removed**", "commit"=>"Create Action", "plan_id"=>"1"}
DB Schema
create_table "plans", :force => true do |t|
t.string "name"
t.integer "user_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "actions", :force => true do |t|
t.string "desc"
t.integer "plan_id"
t.integer "days"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
Action is a reserved word. If you call it anything else (except controller, which is also reserved) then it will work.
http://guides.rubyonrails.org/action_controller_overview.html#routing-parameters
The params hash will always contain the :controller and :action keys, but you should use the methods controller_name and action_name instead to access these values.