Rails form_for with nested resources - ruby-on-rails

I'm having issues with routing a rails app to use a new and edit form.
The routes use the username within the URL.
lists GET /lists(.:format) lists#index
POST /user/:username/lists(.:format) lists#create
new_list GET /user/:username/lists/new(.:format) lists#new
edit_list GET /user/:username/lists/:id/edit(.:format) lists#edit
list GET /user/:username/lists/:id(.:format) lists#show
PATCH /user/:username/lists/:id(.:format) lists#update
PUT /user/:username/lists/:id(.:format) lists#update
DELETE /user/:username/lists/:id(.:format) lists#destroy
root GET / static#welcome
show_user GET /user/:id(.:format) users#show
My routes.rb-
get 'lists', to: 'lists#index', as: :lists, via: 'get'
scope 'user/:username' do
resources :lists, except: [:index]
end
My shared form, for use with updating and creating-
<%= form_for [#user, #list], url: list_path(#user, #list) do |f| %>
Using the above settings, I can edit lists correctly, but if I navigate to lists#new, I get the following error-
No route matches {:action=>"show", :controller=>"lists", :id=>nil, :username=>"test"} missing required keys: [:id]
If I pluralize the path in my form to be url: lists_path(#user, #list), then the new page will now load, but when trying to create I get the below error-
No route matches [POST] "/lists.test"
How can I fix these routes so I can use the same shared form for both edits and creating?
Adding controller code-
class ListsController < ApplicationController
before_action :set_list, only: [:show, :edit, :update, :destroy]
def index
#lists = List.all
end
def show
end
# GET /lists/new
def new
#list = List.new
#user = User.find_by(username: params[:username])
end
def edit
#user = User.find_by(username: params[:username])
end
def create
#list = current_user.lists.new(list_params)
respond_to do |format|
if #list.save
format.html { redirect_to list_path(#list.user.username, #list.id), notice: 'List was successfully created.' }
else
format.html { render :new }
end
end
end
def update
respond_to do |format|
if #list.update(list_params)
format.html { redirect_to list_path(#list.user.username, #list.id), notice: 'List was successfully updated.' }
else
format.html { render :edit }
end
end
end
def destroy
#list.destroy
respond_to do |format|
format.html { redirect_to lists_url, notice: 'List was successfully destroyed.' }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_list
#list = List.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def list_params
params.require(:list).permit(:name, :category, :user_id)
end
end

Try this - Change the :url value in the form_for to [[#list], :username => #user.username]
Like so:
<%= form_for [#user, #list], url: [[#list], :username => #user.username] do |f| %>

Related

Couldn't find Portfolio with 'id'=edit

I'm getting this error message (Couldn't find Portfolio with 'id'=edit) when I try to edit my show page.
def set_portfolio_item
#portfolio_item = Portfolio.find(params[:id])
end
I've tried editing the id by putting edit there but it still doesn't work
class PortfoliosController < ApplicationController
skip_before_action :verify_authenticity_token
before_action :set_portfolio_item, only: [:edit, :show, :update, :destroy]
layout 'portfolios'
access all: [:show, :index, :angular], user: {except: [:destroy, :new, :create, :update, :edit, :sort]},site_admin: :all
def index
#portfolio_items = Portfolio.by_position
#page_title = "Portfolios"
end
def sort
params[:order].each do |key, value|
Portfolio.find(value[:id]).update(position: value[:position])
end
render json: { status: "updated" }
end
def new
#portfolio_item = Portfolio.new
3.times { #portfolio_item.technologies.build }
end
def create
#portfolio_item = Portfolio.new(portfolio_params)
respond_to do |format|
if #portfolio_item.save
format.html { redirect_to portfolios_path, notice: 'Your portfolio item is now live.'}
else
format.html { render :new }
end
end
end
def edit
end
def update
respond_to do |format|
if #portfolio_item.update(portfolio_params)
format.html { redirect_to portfolios_path, notice: 'The record successfully updated.' }
else
format.html { render :edit }
end
end
end
def show
end
def destroy
#this going to perform the look up
# Destroy/delete the record
#portfolio_item.destroy
# Redirect
respond_to do |format|
format.html { redirect_to portfolios_url, notice: 'Post was removed.' }
end
end
private
def portfolio_params
params.require(:portfolio).permit(
:title,
:subtitle,
:body,
:main_image,
:thumb_image,
technologies_attributes: [:name]
)
end
def set_portfolio_item
#portfolio_item = Portfolio.find(params[:id])
end
end
when clicking on http://localhost:3000/portfolios/edit, I expected to go to the show edit page, but getting
ActiveRecord::RecordNotFound in PortfoliosController#show
Couldn't find Portfolio with 'id'=edit
this is my routes for show
portfolios_show GET /portfolios/:id(.:format) portfolios#show
blog GET /blogs/:id(.:format) blogs#show
rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show
rails_blob_representation GET /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show
rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format)
active_storage/disk#show
this is my route for edit
edit_user_password GET /password/edit(.:format) devise/passwords#edit
edit_user_registration GET /edit(.:format) devise/registrations#edit
edit_portfolio GET /portfolios/:id/edit(.:format) portfolios#edit
edit_blog GET /blogs/:id/edit(.:format) blogs#edit
When I click on http://localhost:3000/portfolios/edit, I can error message
ActiveRecord::RecordNotFound in PortfoliosController#show Couldn't find Portfolio with 'id'=edit
If you want to edit a portfolio, you must supply an id, like so http://localhost:3000/portfolios/1/edit. Your current URL can only be interpreted as portfolios#show, with "edit" being the :id part of the route.

Cloud9 (rails setup) - url doesn't change along with actions

I'm trying to build a blog using rails in c9 ubuntu setup. While I was optimizing routes, I realized that my url in browser does not change along with my actions, though everything works just fine.
The url that can be seen in the browser is actually set to display all the blogs' posts, and it does, but when I try to click on only one (in this case 'My Blog Post 1') it performs the action, but url remains the same.
However, if I type the correct url manually, it also works.
Everything is working, but my url doesn't change automatically, but I have to do it manually. In this stage I can handle that, but I'm afraid I'll have problems with that later on. This is my first time that I use cloud9 environment.
Controller:
class BlogsController < ApplicationController
before_action :set_blog, only: [:show, :edit, :update, :destroy]
# GET /blogs
# GET /blogs.json
def index
#blogs = Blog.all
end
# GET /blogs/1
# GET /blogs/1.json
def show
end
# GET /blogs/new
def new
#blog = Blog.new
end
# GET /blogs/1/edit
def edit
end
# POST /blogs
# POST /blogs.json
def create
#blog = Blog.new(blog_params)
respond_to do |format|
if #blog.save
format.html { redirect_to #blog, notice: 'Blog was successfully created.' }
else
format.html { render :new }
end
end
end
# PATCH/PUT /blogs/1
# PATCH/PUT /blogs/1.json
def update
respond_to do |format|
if #blog.update(blog_params)
format.html { redirect_to #blog, notice: 'Blog was successfully updated.' }
else
format.html { render :edit }
end
end
end
# DELETE /blogs/1
# DELETE /blogs/1.json
def destroy
#blog.destroy
respond_to do |format|
format.html { redirect_to blogs_url, notice: 'Blog was successfully destroyed.' }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_blog
#blog = Blog.friendly.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def blog_params
params.require(:blog).permit(:title, :body)
end
end
Routes:
Rails.application.routes.draw do
resources :portofolioos, except: [:show]
get 'portofolioo/:id', to: 'portofolioos#show', as: 'portofolioo_show'
get 'about', to: 'pages#about'
get 'contact', to: 'pages#contact'
resources :blogs
root to: 'pages#home'
end
View just has a form. It's pretty much scaffold, I only changed show page to display id in url and added friendly_id gem, but none of it is displayed in url, but it works just fine.
<p><%= link_to portofolio_item.title, portofolioo_show_path(portofolio_item) %></p>

Rails 4 controller News generated by scaffold not working properly

I searched about this question, but had no success.
I´m trying to learn ruby on rails, came from php. I generated a webapp with the generation tool, second I generated a News controller with scaffold. The devise and pundit are installed too with gems.
The program works perfectly, the problem is related to the News module, I generated it with scaffold.
The routes where created with the command: resources :news
My idea is to create one _form.html.erb and it be called to create a new record or to updated an existing record. Some tutorials teach to create a new.html.erb and an update.html.erb file and duplicate the code, but I know that is possible to have partials as the main form part.
I´m using simple_form_for and the code to do the new is:
# GET /news/new
def new
#news = New.new
authorize New
end
The _form.html.erb
<%= simple_form_for(#news) do |f| %>
<%= f.input :titulo %>
<%= f.input :resumo %>
<%= f.button :submit %>
<% end %>
When I enter to edit, it works, but to add a new it throws.
ActionController::UrlGenerationError at /news/new
No route matches {:action=>"show", :controller=>"news", :locale=>:en} missing required keys: [:id]
Sorry for my bad english, I´m without direction here, is there any way that I can solve it?
Thanks.
====== UPDATED =======
routes.rb
Rails.application.routes.draw do
root to: 'visitors#index'
devise_for :users
resources :users
resources :news
end
New.rb (Model)
class New < ActiveRecord::Base
belongs_to :user
end
application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_filter :set_locale
def default_url_options(options={})
{ locale: I18n.locale }
end
private
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
end
end
news_controller.rb (Complete)
class NewsController < ApplicationController
before_filter :authenticate_user!
after_action :verify_authorized
#before_action :set_news, only: [:show, :edit, :update, :destroy]
# GET /news
# GET /news.json
def index
#news = New.all
authorize New
end
# GET /news/1
# GET /news/1.json
def show
#news = New.find(params[:id])
authorize New
end
# GET /news/new
def new
#news = New.new
authorize New
end
# GET /news/1/edit
def edit
#news = New.find(params[:id])
authorize New
end
# POST /news
# POST /news.json
def create
#news = New.new(news_params)
respond_to do |format|
if #news.save
format.html { redirect_to #news, notice: 'New was successfully created.' }
format.json { render :show, status: :created, location: #news }
else
format.html { render :new }
format.json { render json: #news.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /news/1
# PATCH/PUT /news/1.json
def update
respond_to do |format|
if #news.update(news_params)
format.html { redirect_to #news, notice: 'New was successfully updated.' }
format.json { render :show, status: :ok, location: #news }
else
format.html { render :edit }
format.json { render json: #news.errors, status: :unprocessable_entity }
end
end
end
# DELETE /news/1
# DELETE /news/1.json
def destroy
#news.destroy
respond_to do |format|
format.html { redirect_to news_url, notice: 'New was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_news
#news = New.find(params[:id])
end
private
def news_params
params.require(:news).permit(:titulo, :resumo, :texto, :published_at, :user_id)
end
end
Command rake routes
news_index GET /news(.:format) news#index
POST /news(.:format) news#create
new_news GET /news/new(.:format) news#new
edit_news GET /news/:id/edit(.:format) news#edit
news GET /news/:id(.:format) news#show
PATCH /news/:id(.:format) news#update
PUT /news/:id(.:format) news#update
DELETE /news/:id(.:format) news#destroy
Thanks in advance.
======= UPDATE 2 ===========
Changing my New action to this:
def new
#news = New.create(params[:id])
end
It solved, but everytime I enter, it creates an empty record...
Use news_index_path for GET /news and POST /news. Rails doesn't figure out the pluralization correctly for the "news" term.
Check the output of rake routes, it will be obvious.

Rails association new_path error

I have two models post and topic in my rails app
class Post < ActiveRecord::Base
#relation between topics and post
belongs_to :topic
#post is valid only if it's associated with a topic:
validates :topic_id, :presence => true
#can also require that the referenced topic itself be valid
#in order for the post to be valid:
validates_associated :topic
end
And
class Topic < ActiveRecord::Base
#relation between topics and post
has_many :posts
end
I am trying to create association between both of them.
I want multiple post corresponding to each topic
I have used nested routes
Rails.application.routes.draw do
# nested routes
resources :topics do
resources :posts
end
resources :userdetails
devise_for :users, :controllers => { :registrations => "registrations" }
My Post controller looks like
class PostsController < ApplicationController
# before_action :set_post, only: [:show, :edit, :update, :destroy]
before_filter :has_userdetail_and_topic, :only =>[:new, :create]
# GET /posts
# GET /posts.json
#for new association SAAS book
protected
def has_userdetail_and_topic
unless(#topic =Topic.find_by_id(params[:topic_id]))
flash[:warning] = 'post must be for an existing topic'
end
end
public
def new
#post = #topic.posts.build
###topic = Topic.find(params[:topic_id1])
end
def index
#posts = Post.all
end
# GET /posts/1
# GET /posts/1.json
def show
end
# GET /posts/new
# GET /posts/1/edit
def edit
end
# POST /posts
# POST /posts.json
def create
##topic.posts << #post
##current_user = current_user.id
#current_user.posts << #topic.posts.build(params[:post])
##post = Post.new(post_params )
##post.userdetail_id = current_user.id
#Association functional between topic and post
#Class variable used
###topic.posts << #post
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render :show, status: :created, location: #post }
else
format.html { render :new }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /posts/1
# PATCH/PUT /posts/1.json
def update
respond_to do |format|
if #post.update(post_params)
format.html { redirect_to #post, notice: 'Post was successfully updated.' }
format.json { render :show, status: :ok, location: #post }
else
format.html { render :edit }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# DELETE /posts/1
# DELETE /posts/1.json
def destroy
#post.destroy
respond_to do |format|
format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_post
#post = Post.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def post_params
params.require(:post).permit(:topic_id,:issue, :description, :rating, :userdetail_id)
end
end
I am trying to navigate from topics/index via code <td><%= link_to 'Write', new_topic_post_path(#topic) %> </td>
but when i try to go at localhost:3000/topics]
I am getting error
No route matches {:action=>"new", :controller=>"posts", :topic_id=>nil} missing required keys: [:topic_id]
Can any body tell me about this error, as i am new to rails please clearly specify answer.
And I have one more doubt, please tell me if i am doing association between topic and post incorrectly.I have confusion about this line of code -
#topic.posts << #post
What the error missing required keys: [:topic_id] is telling you is that you need to provide a hash with the key topic_id:
<%= link_to 'Write', new_topic_post_path(topic_id: #topic) %>
Passing a resource as to a route helper only works for the id param:
<%= link_to #topic, topic_path(#topic) %>
Is a kind of shorthand for:
<%= link_to #topic, topic_path(id: #topic.to_param) %>
Addition:
#prcu is also correct. The #topic record needs to be saved to the database. Records which are not saved do not have an id since the database assigns the id column when the record is inserted.
You also need to set the #topic instance variable in PostsController:
#topic = Topic.find(params[:id])
This is commonly done with a before filter:
before_filter :set_topic, only: [:new]
def set_topic
#topic = Topic.find(params[:id])
end
The same also need to be done in TopicsController#index.
#topic is not set or it's not persisted. You can not use topic not saved to db in this helper.

Saving data into profile model does not work

I have an issue with associating two models in my rails application: Users & Profiles. An individual user profile should be created after a new user signs up. After signing up the user, saving data into the actual profile model is not successful. I can not get it to work. Please find a detailed description below.
Here is my setup:
I use Rails 4.0.0.rc2 and ruby 2.0.0p195.
Both models are associated like this:
profile.rb
class Profile < ActiveRecord::Base
belongs_to :user
end
user.rb
has_one :profile, dependent: :destroy
accepts_nested_attributes_for :profile
before_create :build_profile
As I use the devise gemI have created a registrationscontrollerto change the after_sign_up_path:
class RegistrationsController < Devise::RegistrationsController
protected
def after_sign_up_path_for(resource)
new_user_profile_path(:user_id => #user)
end
end
Whenever I sign up a new user the actual sign up works fine, the user is subsequently being directed to http://localhost:3000/users/42/profile/new for example. However, when I then enter the data into the profile form fields and click on submit I get the following error:
No route matches [POST] "/users/profile"
Although one could expect a routing error, you will notice a different error when looking at the actual domain:
http://localhost:3000/users//profile
In case you still want to have a look at my routes.rb please do (relevant excerpt):
devise_for :users, :controllers => { :registrations => "registrations" }
devise_scope :user do
get 'signup', :to => "devise/registrations#new", as: :signup
get 'login', :to => "devise/sessions#new", as: :login
get 'logout', :to => "devise/sessions#destroy", as: :logout
end
resources :users do
resource :profile
end
However, as noted above I don't really have a routing issue. It rather seems like I have an issue with the current user_id not being properly shown in the domain, which can be related to either my actual profile form or the new action on the profiles controller.
I start my profile form on new.html.erb like this:
<%= form_for #profile, url: user_profile_path(#user) do |f| %>
My profiles_controller.rblooks like this:
class ProfilesController < ApplicationController
before_action :set_profile, only: [:show, :edit, :update, :destroy]
# GET /profiles
# GET /profiles.json
def index
#profiles = Profile.all
end
# GET /profiles/1
# GET /profiles/1.json
def show
end
# GET /profiles/new
def new
#profile = Profile.new
end
# GET /profiles/1/edit
def edit
end
# POST /profiles
# POST /profiles.json
def create
#profile = Profile.new(profile_params)
respond_to do |format|
if #profile.save
format.html { redirect_to #profile, notice: 'Profile was successfully created.' }
format.json { render action: 'show', status: :created, location: #profile }
else
format.html { render action: 'new' }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /profiles/1
# PATCH/PUT /profiles/1.json
def update
respond_to do |format|
if #profile.update(profile_params)
format.html { redirect_to #profile, notice: 'Profile was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
# DELETE /profiles/1
# DELETE /profiles/1.json
def destroy
#profile.destroy
respond_to do |format|
format.html { redirect_to profiles_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_profile
##profile = Profile.find(params[:id])
#profile = current_user.profile
end
# Never trust parameters from the scary internet, only allow the white list through.
def profile_params
params.require(:profile).permit(:photo, :address, :zip, :city, :state, :country, :telephone, :experience, :levels, :ages, :travel, :teachinglocation, :onlineteaching, :quotation, :aboutme, :subjects, :specialties, :lessondetails, :equipment)
end
end
What do I do wrong? How can I properly ensure that a newly signed up user can properly save his profile data?
It would be so great, if you could help me out.
It appears the Profile controller is not configured with the correct url - as can be seen by the missing User ID information.
You can see all currently defined paths by running the command rake routes from the command line.
I am a beginner at RESTful design and Rails, so do not consider the following expert advice.
Try changing the location of your redirect_to in the Profile create method:
# POST /profiles
# POST /profiles.json
def create
...
format.html { redirect_to user_profile_path(#profile.user, #profile), notice: 'Profile was successfully created.' }
...
If this works, the path should also be updated in the update and delete methods. As well as the format.json sections.
For additional info about this topic see:
2.9 Creating Paths and URLs From Objects
http://guides.rubyonrails.org/routing.html

Resources