Model won't update on submission - ruby-on-rails

Im getting this weird error when i want to update my model (model name carts).
Error : The action 'update' could not be found for CartsController
this is my carts_controller.rb :
class CartsController < ApplicationController
include CartForcable
before_action :scoped_cart, only: [:show, :update]
def show
end
private
def scoped_cart
force_cart! lambda {|r| r.includes(:entries => {:sign => [:dimensions, :substrates]})}
end
def update
#cart = #cart.find(params[:id])
if #cart.update_attributes(cart_params)
flash[:notice] = translate 'flash.notice'
else
flash[:error] = translate 'flash.error'
end
support_ajax_flashes!
respond_to do |format|
format.html # renders view
format.json { render json: #entry }
end
end
end
and these are my routes.rb :
resources :categories, only: [:index] do
resources :signs, shallow: true, only: [:index]
end
resources :carts, only: [:show, :update]#, param: :cart_permalink
resource :cart, as: :user_cart, only: [:show, :update], param: :cart_permalink do
resources :cart_entries, only: [:index, :create, :update, :destroy], as: 'entries', path: 'entries'
end
resource :user, only: [:edit, :show, :update], as: 'current_user', path: 'profile'
resources :signs, only: [:show]
resources :pages, only: [:show], param: :permalink
and my show.html.erb has this form :
<%= form_for #cart, :url => {:controller => "carts", :action => "update" } do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :regnum %>
<%= f.text_field :regnum %></br>
<%= button_to "Order/update", {:controller => "carts",:action =>
'update', :id => #cart.id }, :method => :update %>
<% end %>
After i enter name or number (for example), and click update button or something, it doesnt upload any data into model carts(which has the right columns).
Thanks,
Michael

Your update method is private; controller actions need to be public. Move
def update
#cart = Cart.find(params[:id])
...
end
to be above the
private
line. You need to do the find on the Cart model, not a #cart instance.
You can use a show action although edit would be more standard and fits right in with the RESTful routes without the need to override convention. The Rails routing guide should help here: http://guides.rubyonrails.org/routing.html
Additionally, you need to define the value of #cart in your edit action:
def edit
#cart = Cart.find(params[:id])
end
This will ensure it has a value on the edit form and so comes back into your update action through the parameters.
So if you try to edit a cart with something like /carts/12345/edit (where 12345 is the id of the cart you want to update) it should all hang together.

This looks wrong:
#cart = #cart.find...
don't you mean...?:
#cart = Cart.find...
And it seems you are using "cart_permalink" instead of "id" on your routes
resource :cart, as: :user_cart, only: [:show, :update], param: :cart_permalink do
Check the server log and see the name of the parameter that holds the ID, also run "bundle exec rake routes" to double check.

Related

Bidirectional many-to-many association with Rails : how to create from both ways?

I have a question about many-to-many associations in Ruby on Rails.
I have 3 models in my app : Topic, Meeting and Todo associated with a manu-to-many association.
class Todo < ApplicationRecord
belongs_to :topic
belongs_to :meeting
end
then
class Meeting < ApplicationRecord
has_many :todos
end
and
class Topic < ApplicationRecord
has_many :todos
end
I made my routes and controller to be able to create new todos via a meeting :
Rails.application.routes.draw do
resources :meetings, only: [:index, :show, :new, :create, :edit, :update] do
resources :todos, only: [:index, :new, :create]
end
resources :todos, only: [:index, :show, :edit, :update, :destroy]
end
and
class TodosController < ApplicationController
def new
#topic = Topic.find(params[:topic_id])
#todo = Todo.new
end
def create
#todo = Todo.new(todo_params)
#meeting = Meeting.find(params[:meeting_id])
#todo.meeting = #meeting
#todo.save
redirect_to meeting_path(#meeting)
end
private
def todo_params
params.require(:todo).permit(:topic_id, :meeting_id, :note, :deadline, :title)
end
end
and my view :
<h3><%= #meeting.date %></h3>
<%= simple_form_for [#meeting, #todo] do |f| %>
<%= f.input :title %>
<%= f.input :note %>
<%= f.date_field :deadline %>
<%= f.association :topic, label_method: :nom, value_method: :id %>
<%= f.submit "Add a todo" %>
<% end %>
My problem is that I want to be able to create todo via topics aswell and when I add my routes :
resources :topics, only: [:index, :show, :new, :create] do
resources :todos, only: [:index, :new, :create]
end
When I tried to complete my controller and test it, it seems to be tricky. If I add:
#topic = Topic.find(params[:topic_id])
Then it tells me that it needs a meeting...
Any idea ?
You can create the different routes with:
resources :meetings do
resources :todos, only: [:index, :new, :create]
end
resources :topics do
resources :todos, only: [:index, :new, :create]
end
You can avoid duplication by using routing concerns:
concerns :todoable do
resources :todos, only: [:index, :new, :create]
end
resources :topics, concerns: :todoable
resources :meetings, concerns: :todoable
In your controller you can check for the presences of the meeting_id or topic_id parameters:
class TodosController < ApplicationController
before_action :set_parent
def new
#todo = Todo.new
end
def create
#todo = #parent.todos.new(todo_params)
if #todo.save
redirect_to #parent
else
render :new
end
end
private
def parent_class
#parent_class ||= if params[:meeting_id].present?
Meeting
else if params[:topic_id].present?
Topic
else
# raise an error?
end
end
def set_parent
id = params["#{parent_class.model_name.param_key}_id"]
#parent = parent_class.find(id)
end
def todo_params
params.require(:todo)
.permit(:topic_id, :meeting_id, :note, :deadline, :title)
end
end
<%= simple_form_for [#parent, #todo] do |f| %>
<%= f.input :title %>
<%= f.input :note %>
<%= f.date_field :deadline %>
<%= f.association :topic, label_method: :nom, value_method: :id if #parent.is_a?(Meeting) %>
<%= f.association :meeting, label_method: :nom, value_method: :id if #parent.is_a?(Topic) %>
<%= f.submit "Add a todo" %>
<% end %>

Rails - Searching/showing available option when creating new instance of an object

I've got a one to many relationship with one movie per list entry, where a movie can be used in a list entry. My list_entries table has a movie_id and a list_id.
database schema
I've nested list_entries in lists so I can pass the list_id directly when creating a new instance.
Rails.application.routes.draw do
devise_for :users
root to: 'pages#home'
resources :movies, only: [:new, :index, :create, :show, :destroy, :edit, :update]
resources :users, only: [:show, :edit, :update]
resources :lists, only: [:new, :create, :show, :index, :destroy] do
resources :list_entries
end
end
Right now I can create and destroy list entries but I have to specify the movie id manually.
The UX I want to achieve is for the user to be able to search for movies from themy list_entries/new form but I don't even know where to begin.
The form as it is now:
<%= simple_form_for #list_entry, url: list_list_entries_path do |f| %>
<%= f.input :comment %>
<%= f.input :movie_id %>
<%= f.submit "Add", class: "btn devise-button" %>
<% end %>
My list entries controller:
class ListEntriesController < ApplicationController
before_action :find_list, only: [:index, :create, :show, :new, :destroy]
def new
#list_entry = ListEntry.new
end
def index
#list_entries = ListEntry.where(list: #list)
end
def show
#list_entry = ListEntry.find(params[:id])
end
def destroy
#list_entry = ListEntry.find(params[:id])
#list_entry.destroy
redirect_to list_list_entries_path
end
def create
#list_entry = ListEntry.new(entry_params)
#list_entry.list_id = params[:list_id]
if #list_entry.save
redirect_to list_list_entries_path
else
render 'new'
end
end
private
def find_list
#list = List.find(params[:list_id])
end
def entry_params
params.require(:list_entry).permit(:comment, :movie_id)
end
end
If you don't want to manually specify a movie_id in the form, you can use the simple_form_for association helper:
<%= f.input :comment %>
<%= f.association :movie %>
I believe it should be labeled based off of the movie title, but if not, you may have to specify a #to_label method in your Movie model.
Alternatively, you could query for the movies in your #new action and use them to do whatever you like in your view:
def new
#list_entry = ListEntry.new
#movies = Movie.all # or whatever query you think is relevant
end
The #collection_select documentation might be useful here:
https://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/collection_select

No route matches [GET] "/accounts.json"

I am attempting to provide a user with the ability to create a subdomain. When clicking Create Account, the system redirects from "accounts/new" to "accounts" and no subdomain is getting populated.
routes.rb
class SubdomainPresent
def self.matches?(request)
request.subdomain.present?
end
end
class SubdomainBlank
def self.matches?(request)
request.subdomain.blank?
end
end
Saasapp::Application.routes.draw do
constraints(SubdomainPresent) do
root 'projects#index', as: :subdomain_root
devise_for :users, controllers: { registrations: 'users/registrations' }
resources :users, only: :index
resources :projects, except: [:index, :show, :destroy]
end
constraints(SubdomainBlank) do
root 'visitors#new'
resources :accounts, only: [:new, :create]
end
end
accounts_controller.rb
class AccountsController < ApplicationController
skip_before_action :authenticate_user!, only: [:new, :create]
def new
#account = Account.new
end
def create
#account = Account.new(account_params)
if #account.valid?
Apartment::Database.create(#account.subdomain)
Apartment::Database.switch(#account.subdomain)
#account.save
redirect_to new_user_session_url(subdomain: #account.subdomain)
else
render action: 'new'
end
end
new.html.erb
<%= simple_form_for #account do |f| %>
<%= f.input :subdomain do %>
<div class="input-group">
<%= f.input_field :subdomain, class: 'form-control' %>
<span class="input-group-addon">.demo.dev</span>
</div>
<% end %>
<%= f.button :submit, class: 'btn-primary' %>
<% end %>
error in logs
Started GET "/accounts.json" for 127.0.0.1 at 2017-02-08 21:56:08 -0500
ActionController::RoutingError (No route matches [GET] "/accounts.json"):
This line:
resources :accounts, only: [:new, :create]
is telling Rails "only set up the new and create route for an account".
It basically says "do not set up an index route"
if you want an index route (ie that will show a list of all the accounts) then you need to update that line to eg:
resources :accounts, only: [:new, :create, :index]
Add :index route to line:
resources :accounts, only: [:new, :create]
In AccountsController create index action and handle
render json: YourObject

Routing Error No Route Matches (correct route)

I'm in the process of building an edit form. I have completed the form and it renders as it should. When I go to submit the update to the form I get a no route error. The path to my edit page is for example '/topics/1/bookmarks/1/edit'. This page loads perfectly fine. That page contains a partial of the form that will be used to edit the record. When I select the submit button however it re-routes to '/topics/1/bookmarks/1' and gives me the following:
Routing Error
No route matches [PATCH] "/topics/1/bookmarks/1"
Below are the files that should be of importance let me know if there is something I did not share. That would be important to view.
bookmarks_controller.rb
def edit
#topic = Topic.find(params[:topic_id])
#bookmark = Bookmark.find(params[:id])
end
def update
#topic = Topic.find(params[:topic_id])
#bookmark = Bookmark.find(params[:id])
if #bookmark.update_attributes(params.require(:bookmark).permit(:url, :topic_id, :description))
flash[:notice] = "Bookmark was updated"
redirect_to [#topic, #bookmark]
else
flash[:error] = "There was an error saving the Bookmark. Please try again."
render :edit
end
end
config/routes.rb
resources :topics do
resources :bookmarks, only: [:show, :new, :edit]
end
bookmarks/_form.html.erb
<%= form_for [topic, bookmark] do |f| %>
<%= f.label :description %>
<%= f.text_field :description %>
<%= f.label :url %>
<%= f.text_field :url %>
<%= f.submit %>
<% end %>
bookmarks/edit.html.erb
<%= render partial: 'form', locals: {topic: #topic, bookmark: #bookmark} %>
You don't have an update route, which is what actually updates the database. Just change
resources :bookmarks, only: [:show, :new, :edit]
to
resources :bookmarks, only: [:show, :new, :edit, :update]
OR better yet,
resources :bookmarks, except: [:index, :create, :destroy]
If you have a new action, then you should want a create action too. So, finally:
resources :bookmarks, except: [:index, :destroy]

Unable to create posts in rails forum on the topics page

I am new to rails and am trying create a forum. The forum has many topics, topics belong to a forum and have many microposts, and microposts belong to both topics and users. However, no matter what I try, the posts will not be created. Currently when I try to post, I get the routing error "No route matches [GET] "/topics""
My routes.rb file:
resources :users
resources :sessions, only: [:new, :create, :destroy]
resources :microposts, only: [:create, :destroy]
resources :forums, only: [:index, :show]
resources :topics, only: [:show]
_micropost_form.html.erb
<%= form_for(#micropost) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.hidden_field :topic_id, value: #topic.id %>
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.text_field :summary, placeholder: "One-line summary..." %>
<%= f.text_area :content, placeholder: "Compose a new post..." %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
microposts_controller.rb
class MicropostsController < ApplicationController
before_action :signed_in_user, only: [:create, :destroy]
before_action :correct_user, only: :destroy
def create
##topic = Topic.find_by_id(params[:topic_id])
#micropost = current_user.microposts.build(micropost_params)
if #micropost.save
flash[:success] = "Your solution has been posted!"
redirect_to topic_path(#topic)
else
redirect_to topic_path(#topic)
end
end
def destroy
#micropost.destroy
redirect_to root_url
end
private
def micropost_params
params.require(:micropost).permit(:summary, :content, :user_id)
end
def correct_user
#micropost = current_user.microposts.find_by(id: params[:id])
redirect_to root_url if #micropost.nil?
end
end
As you can see, I commented out the first line in my create function because I've tried posting based on the the micropost's relationship to the topic to no avail. Thanks in advance and let me know if it would help if I posted more code!
In your :topics resource, you didn't defined the index method, that's why you won't be able to get to topic's list or index page. Try to change your route like this:
resources :topics, only: [:index, :show]
or remove only attribute from resources, it will automatically include all your methods by default.
resources :topics
Also if you have relationship between models, you should define nested routes in your routes
file, For example, you can define them like this, you can change them accordingly:
try to change your route file like this:
resources :users
resources :sessions, only: [:new, :create, :destroy]
resources :forums do
resources :topics do
resources :microposts, only: [:new, :create, :destroy]
end
end
In above case, you can access your forums like this:
http://localhost:3000/forums
you can access your topics like this:
http://localhost:3000/forums/id/topics
you can access your microposts like this:
http://localhost:3000/forums/id/topics/id/microposts
If you want to access /microposts directly you have to put it outside any resource.
resources :microposts, only: [:index]
now you will be able to access it:
http://localhost:3000/microposts
Hope it will help. Thanks.

Resources