So I am trying to implement a polymorphic association for the first time and I'm running into a little bit of trouble.
I am trying to allow users to leave a note on a contact or organization. But after I submit a note I run into a routing error.
Here is the error I'm receiving (image)
Here are my routes:
Here are my routes (screenshot)
Here is my routes.rb file:
Rails.application.routes.draw do
get 'welcome/index'
resources :organizations do
resources :contacts do
resources :notes, module: :contacts
end
resources :notes, module: :organizations
end
root 'welcome#index'
end
Here is my notes_controller.rb file:
class NotesController < ApplicationController
def create
#note = #noteable.notes.new note_params
redirect_to #noteable, notice: "Your note was successful!!!"
end
private
def note_params
params.require(:note).permit(:note_title, :note_body)
end
end
Here is my contacts/notes_controller.rb file:
class Contacts::NotesController < ApplicationController
private
def set_noteable
#noteable = Contact.find(params[:contact_id])
end
end
Here is my oranizations/notes_controller.rb file:
class Organizations::NotesController < ApplicationController
private
def set_noteable
#noteable = Organization.find(params[:organization_id])
end
end
Here is my view/notes/_form.html.rb file:
<h1>New Note</h1>
<%= form_for [noteable, Note.new] do |form| %>
<p>
<%= form.label :note_title %><br>
<%= form.text_field :note_title %>
</p>
<p>
<%= form.label :note_body %><br>
<%= form.text_area :note_body %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
If you need anything else, I will provide it! Thank you ahead of time!
Try changing this:
resources :organizations do
resources :contacts do
resources :notes, module: :contacts
end
resources :notes, module: :organizations
end
to these lines:
resources :organizations do
resources :contacts do
resources :notes
end
resources :notes
end
You don't need the module there since they are already scoped by the parent resource.
Also move the contacts/notes_controller.rb to organizations/contacts/notes_controller.rb. Make sure you also move the view into the corresponding folder.
Hope it helps :)
Related
I have Categories (Parents) within which are listed Products (Children).
I want to be able to create a new Product directly from the navbar, anywhere in the app and then, during the creation, assign it to a Category.
However, I get the present error:
NoMethodError in Products#new
Showing /Users/istvanlazar/Mobily/app/views/products/new.html.erb where line #9 raised:
undefined method `products_path' for #<#<Class:0x00007febaa5aec98>:0x00007febae0f9e38>
Did you mean? product_show_path
## product_show_path is a custom controller that has nothing to do with this one,
enabling show and clean redirection from newest_products index bypassing categories nesting.
Extracted source (around line #9):
9 <%= form_for [#product] do |f| %>
10 <div class="form-styling">
11 <div>
12 <%= f.label :name %>
My App works as such:
Models
class Category < ApplicationRecord
has_many :products, inverse_of: :category
accepts_nested_attributes_for :products
validates :name, presence: true
end
class Product < ApplicationRecord
belongs_to :user, optional: true
belongs_to :category, inverse_of: :products
validates :category, presence: true
end
Routes
get 'products/new', to: 'products#new', as: 'new_product'
resources :categories, only: [:index, :show, :new, :edit] do
resources :products, only: [:index, :show, :edit]
# resources :products, only: [:index, :show, :new, :edit]
end
Controllers
class ProductsController < ApplicationController
before_action :set_category, except: :new
def index
#products = Product.all
#products = policy_scope(#category.products).order(created_at: :desc)
end
def show
#product = Product.find(params[:id])
end
def new
#product = Product.new
#product.user = current_user
end
def create
#product = Product.new(product_params)
#product.user = current_user
if #product.save!
redirect_to category_product_path(#category, #product), notice: "Product has been successfully added to our database"
else
render :new
end
end
private
def set_category
#category = Category.find(params[:category_id])
end
def product_params
params.require(:product).permit(:name, :price, :description, :category_id, :user, :id)
end
end
class CategoriesController < ApplicationController
def index
#categories = Category.all
end
def show
#category = Category.find(params[:id])
end
def new
# Non-existant created in seeds.rb
end
def create
# idem
end
def edit
# idem
end
def update
# idem
end
def destroy
# idem
end
private
def category_params
params.require(:category).permit(:name, :id)
end
end
Views
# In shared/navbar.html.erb:
<nav>
<ul>
<li>Some Link</li>
<li>Another Link</li>
<li><%= link_to "Create", new_product_path %></li>
</ul>
</nav>
# In products/new.html.erb:
<%= form_for [#product] do |f| %>
<div class="form-styling">
<div>
<%= f.label :name %>
<%= f.text_field :name, required: true, placeholder: "Enter product name" %>
<%= f.label :price %>
<%= f.number_field :price, required: true %>
</div>
<div>
<%= f.label :description %>
<%= f.text_field :description %>
</div>
<div>
<%= f.label :category %>
<%= f.collection_select :category_id, Category.order(:name), :id, :name, {prompt: 'Select a Category'}, required: true %>
</div>
<div>
<%= f.submit %>
</div>
</div>
<% end %>
Do you have any idea of where it went wrong? Or is it impossible to Create a Child before assigning it to a Parent..?
Thanks in advance.
Best,
Ist
You haven't defined any route to handle your new product form's POST. You've defined the new_product path, but this arrangement is breaking Rails' conventions and you're not providing a work-around.
You could define another custom route, e.g. post 'products', to: 'products#create', as: 'create_new_product' and then set that in your form like form_for #product, url: create_new_product_path do |f|.
However, you should consider changing the structure so that product routes are not nested under categories. Think twice before breaking conventions this way.
Edit: I misread the intention, ignore this. Go with Jeremy Weather's answer.
Or is it impossible to Create a Child before assigning it to a Parent..?
With the way you have your relationships and validations set up: yes, it is. A Product requires a Category, through the validates :category, presence: true. And if you're on Rails 5+, the association will already be required by default (meaning that validates is actually redundant). Meaning if you try to create a Product without a Category, it will fail, and the Product will not be saved to the database.
With that constraint in place, here's what I recommend trying:
Remove the custom /products/new route
remove get 'products/new', to: 'products#new', as: 'new_product'
Add :new and :create routes to allow product creation nested under categories
Add :create and :new, to the :only array to the resources :products nested under resources :categories
Move the file views/products/new.html.erb to views/categories/products/new.html.erb (creating a new categories directory in views if necessary).
In that file, alter the form_for so that the form is POSTing to the nested :create route:
form_for [#category, #product] do |f|
Visit the URL /categories/[insert some Category ID here]/products/new, filling out some data, and see if that works.
Your routes.rb should look like this:
resources :categories, only: [:index, :show, :new, :edit] do
resources :products, only: [:index, :show, :new, :create, :edit]
end
I am building a project management app.
My System is like- A project has many features, A feature has many tasks.
And the routes.rb is defined as
resources :projects do
resources :features do
resources :tasks
end
end
The first level , that is project to feature is working fine for new feature form , but when I try to implement new tasks form as ->
<%= form_for([#feature, #feature.tasks.build], class: "form-group row") do |form| %>
<%= form.label :name %>
<%= form.text_field :name, required:true, class: "form-control" %>
<%= form.submit class: "btn btn-primary m-2" %>
<% end %>
Now its showing error as
Here is my models->
class Task < ApplicationRecord
belongs_to :feature
end
class Feature < ApplicationRecord
belongs_to :project
has_many :tasks
end
And the controller for tasks looks like->
class TasksController < ApplicationController
before_action :set_feature
def new
#task = #feature.tasks.new
end
def create
#task = #feature.tasks.new(task_params)
if #task.save
redirect_to project_features_path
else
render :new
end
end
def edit
end
def update
if #task.update(task_params)
redirect_to project_feature_tasks_path
else
render :edit
end
end
def complete
#task.update(completed: true)
redirect_to project_feature_tasks_path
end
def destroy
#feature.task.destroy
redirect_to project_feature_tasks_path
end
private
def set_feature
#feature = Feature.find(params[:feature_id])
end
def task_params
params.require(:task).permit(:name,:completed, :project_id,:feature_id)
end
end
Any help is very much appreciated - I am stuck with this error for days now.
If you try running $ rails routes you can see why your current routes are failing you.
Prefix Verb URI Pattern Controller#Action
project_feature_tasks GET /projects/:project_id/features/:feature_id/tasks(.:format) tasks#index
POST /projects/:project_id/features/:feature_id/tasks(.:format) tasks#create
new_project_feature_task GET /projects/:project_id/features/:feature_id/tasks/new(.:format) tasks#new
edit_project_feature_task GET /projects/:project_id/features/:feature_id/tasks/:id/edit(.:format) tasks#edit
project_feature_task GET /projects/:project_id/features/:feature_id/tasks/:id(.:format) tasks#show
PATCH /projects/:project_id/features/:feature_id/tasks/:id(.:format) tasks#update
PUT /projects/:project_id/features/:feature_id/tasks/:id(.:format) tasks#update
DELETE /projects/:project_id/features/:feature_id/tasks/:id(.:format)
You would have to call:
form_for([#project, #feature, #feature.tasks.build], ...) do |form|
A better idea is to unnest the routes. You can do this by using the shallow option:
resources :projects do
resources :features, shallow: true do
resources :tasks
end
end
Or you can do it like so if you for some reason want to keep the member routes (show, edit, update, destroy) for features nested:
resources :projects do
resources :features
end
resources :features, only: [] do
resources :tasks
end
This should be super simple, but I can't see what I'm doing wrong.
The form in the 'new' page for uploads is getting an error.
'Uploads' belong to 'Event'
'Event' has many 'Uploads'
routes.rb is (as far as I know) correct.
I'm planning on using Refile to upload files to S3 (as per this tutorial... not sure if this is relevant at all though)
Upload.rb
class Upload < ActiveRecord::Base
belongs_to :event
attachment :upload_file
end
Event.rb
class Event < ActiveRecord::Base
has_many :uploads
end
uploads_controller.rb
class UploadsController < ApplicationController
before_action :set_event
def new
#upload = #event.uploads.create
end
private
def set_event
#event = Event.find(params[:event_id])
end
end
Routes.rb
Rails.application.routes.draw do
devise_for :users
root 'pages#home'
resources :events do
resources :coupons
resources :uploads
member do
post :check
end
end
views/uploads/new.html.erb (example)
<%= form_for #upload do |f| %>
<%= f.text_field :name %>
<% end %>
When I navigate to the 'new' page, I get the following error:
undefined method `upload_path' for #<#:0x007fb8709229f0>
Why can't I add a new Upload associated with Event? I know I'm missing something super simple, but I can't put my finger on it.
As uploads is nested in events, you get url for your upload path as follow:
/events/1/uploads/new
In this case, you have to specify #event in your form_for method like this:
<%= form_for [#event, #upload] do |f| %>
Or simply
<%= form_for #event.upload do |f| %>
<%= f.text_field :name %>
<%= f.submit %>
<% end%>
I am getting following error :
undefined method `recommendations_path' for #<#<Class:0x0078>>
I have Recommendation model
class Recommendation < ActiveRecord::Base
belongs_to :user
belongs_to :recommended_user, class_name: "User", foreign_key: :recommended_user_id
end
I have user model
class User < ActiveRecord::Base
has_many :recommendations
................
end
In recommendation controller
def new
#recommendation = current_user.recommendations.new
end
In new.html.erb
<%= form_for #recommendation do |f| %>
<%= f.text_field :relationship %>
<%= f.text_field :comment %>
<%= f.submit %>
<% end %>
My routes, where I think problem is:
devise_for :users
resources :users, only: [:show] do
collection do
get :customer_signup
get :employee_signup
end
member do
get :choose_role
get :become_a_customer
get :become_a_employee
end
end
resources :users do
resources :recommendations
end
Thats actually when the form is trying to identify the path for your #recommendation.
According to your routes.rb your form must be:
<%= form_for [:user, #recommendation] do |f| %>
I've been battling this for a while now and I can't figure it out. I have a user model using devise. Users can upload songs, and add youtube videos etc..
I'm trying to let users add/delete songs and videos from the devise edit registrations view.
Videos upload fine, but as songs are a nested resource of playlists, which belongs to user, I think I'm getting muddle up.
Music uploads with the same form on it's corresponding page, but not from the devise registration edit view.
routes:
devise_for :users, controllers: { registrations: "users/registrations", sessions: "users/sessions" }
resources :videos
resources :playlists do
resources :songs
end
Devise registrations controller:
def edit
#song = Song.new
#video = Video.new
end
Form in devise edit registrations:
<div id="user-music-box">
<p class="p-details-title"> Upload Music </p>
<%= simple_form_for [#user.playlist, #song] do |f| %>
<%= f.file_field :audio %>
<%= f.button :submit %>
<% end %>
</div>
<div id="user-video-box">
<p class="p-details-title"> Add videos </p>
<%= simple_form_for #video do |f| %>
<%= f.input :youtubeurl %>
<%= f.button :submit %>
<% end %>
</div>
As I said, videos (Which is a youtube url string) create and save no problem. The exact same form for songs, basically seems to just update the user registration. The song information is shown in the server logs, but no playlist_id is present and nothing gets saved.
Songs controller:
def new
if user_signed_in?
#song = Song.new
if current_user.playlist.songs.count >= 5
redirect_to user_path(current_user)
flash[:danger] = "You can only upload 5 songs."
end
else
redirect_to(root_url)
flash[:danger] = "You must sign in to upload songs"
end
end
def create
#song = current_user.playlist.songs.new song_params
#song.playlist_id = #playlist.id
if #song.save
respond_to do |format|
format.html {redirect_to user_path(current_user)}
format.js
end
else
render 'new'
end
end
Playlist.rb
class Playlist < ActiveRecord::Base
belongs_to :user
has_many :songs, :dependent => :destroy
accepts_nested_attributes_for :songs
end
song.rb
class Song < ActiveRecord::Base
belongs_to :user
belongs_to :playlist
has_attached_file :audio
validates_attachment_presence :audio
validates_attachment_content_type :audio, :content_type => ['audio/mp3','audio/mpeg']
end
Unless you're passing songs/playlists through accepts_nested_attributes_for you shouldn't be using registrations#edit. I'll detail both ways to achieve what you want below:
Nested Attributes
#app/models/user.rb
class User < ActiveRecord::Base
has_many :videos
has_many :playlists
has_many :songs, through: :playlists
accepts_nested_attributes_for :videos
end
#app/models/playlist.rb
class PlayList < ActiveRecord::Base
belongs_to :user
has_and_belongs_to_many :songs
end
#app/models/song.rb
class Song < ActiveRecord::Base
has_and_belongs_to_many :playlists
end
The importance of this is that to use it properly, you're able to edit the #user object directly, passing the nested attributes through the fields_for helper:
#config/routes.rb
devise_for :users, controllers: { registrations: "users/registrations", sessions: "users/sessions" }
#app/controllers/users/registrations_controller.rb
class Users::RegistrationsController < ApplicationController
before_action :authenticate_user!, only: [:edit, :update]
def edit
#user = current_user
#user.playlists.build.build_song
#user.videos.build
end
def update
#user = current_user.update user_params
end
private
def user_params
params.require(:user).permit(:user, :attributes, videos_attributes: [:youtubeurl], playlists_attributes: [song_ids: [], song_attributes: [:title, :artist, :etc]])
end
end
This will allow you to use:
#app/views/users/registrations/edit.html.erb
<%= form_for #user do |f| %>
<%= f.fields_for :videos do |v| %>
<%= v.text_field :youtubeurl %>
<% end %>
<%= f.fields_for :playlists do |p| %>
<%= p.collection_select :song_ids, Song.all, :id, :name %>
<%= p.fields_for :song do |s| %>
<%= f.text_field :title %>
<% end %>
<% end %>
<%= f.submit %>
<% end %>
This will give you a single form, from which you'll be able to create videos, playlists and songs for the #user.
Separate
The other option is to create the object separately.
There is no technical reason for preferring this way over nested attributes; you'd do it to make sure you have the routes in the correct order etc.
As a note, you need to remember that routes != model structure. You can have any routes you want, so long as they define a good pattern for your models:
# config/routes.rb
authenticated :user do #-> user has to be logged in
resources :videos, :playlists, :songs #-> url.com/videos/new
end
# app/controllers/videos_controller.rb
class VideosController < ApplicationController
def new
#video = current_user.videos.new
end
def create
#video = current_user.videos.new video_params
#video.save
end
private
def video_params
params.require(:video).permit(:youtubeurl)
end
end
# app/views/videos/new.html.erb
<%= form_for #video do |f| %>
<%= f.text_field :youtubeurl %>
<%= f.submit %>
<% end %>
The above will require the duplication of the VideosController for Playlists and Songs