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
Related
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 :)
Working with nested routes and associations. I have a partial which creates a tenant, but after the creation it stays with the form rendered and the url changes to /tenants. Desired behavior is that it needs to redirect_to the show page. Routes are as follows:
Rails.application.routes.draw do
devise_for :landlords
authenticated :landlord do
root "properties#index", as: "authenticated_root"
end
resources :tenants
resources :properties do
resources :units
end
root 'static#home'
end
So far the properties and units work (and the landlord) Issue is with Tenants. Originally I had Tenants nested under units, but had issues there as well. Partial looks like this:
<%= form_for #tenant do |f| %>
<%= f.label "Tenant Name:" %>
<%= f.text_field :name %>
<%= f.label "Move-in Date:" %>
<%= f.date_field :move_in_date %>
<%= f.label "Back Rent Amount:" %>
$<%= f.text_field :back_rent %>
<%= f.button :Submit %>
<% end %>
<%= link_to "Cancel", root_path %>
Tenants Controller looks like this:
before_action :authenticate_landlord!
#before_action :set_unit, only: [:new, :create]
before_action :set_tenant, except: [:new, :create]
def new
#tenant = Tenant.new
end
def create
#tenant = Tenant.new(tenant_params)
if #tenant.save
redirect_to(#tenant)
else
render 'new'
end
end
def show
end
def edit
end
def update
if #tenant.update(tenant_params)
redirect_to unit_tenant_path(#tenant)
else
render 'edit'
end
end
def destroy
end
private
def set_property
#property = Property.find(params[:property_id])
end
def set_unit
#unit = Unit.find(params[:unit_id])
end
def set_tenant
#tenant = Tenant.find(params[:id])
end
def tenant_params
params.require(:tenant).permit(:name, :move_in_date, :is_late, :back_rent, :unit_id)
end
end
Models have associations:
class Tenant < ApplicationRecord
belongs_to :unit, inverse_of: :tenants
end
class Unit < ApplicationRecord
belongs_to :property, inverse_of: :units
has_many :tenants, inverse_of: :unit
end
Lastly the show#tenants in rake routes is:
tenant GET /tenants/:id(.:format) tenants#show
I have extensively searched for this topic, but haven't had any success. Any help is appreciated. Rails 5.1
The route you are showing near the end of your question:
tenant GET /tenants/:id(.:format) tenants#show
is not the tenants index; it is the individual tenants/show route. You can tell this because it includes :id, which means it will show you a specific tenant having that id.
Try running rake routes again. The index route should look like this:
tenants GET /tenants(.:format) tenants#index
If you want to return to the tenants index after creating or updating a Tenant record, then you need to specify that path in your TenantsController. In both the #create and #update actions, your redirect line (after if #tenant.save and if #tenant.update, respectively) should read:
redirect_to tenants_path
That will take you to the TenantsController, #index action.
In the alternative, if you want to return to the individual tenant show page, then instead change both of those redirects in the TenantsController in both the #create and #update actions to:
redirect_to tenant_path(#tenant)
That will take you to the TenantsController, #show action for the current #tenant.
I am new to Ruby on Rails.I am facing a problem using nested resources.
I am building a learning app where there are courses and lessons.
Every course will have many lessons and a lesson belongs to only one course.
I am unable to create a lesson for a course currently.
Example : http://localhost:3000/courses/19/lessons/new is a page where i want to create and display lessons for course 19.
Routes.rb
Rails.application.routes.draw do
devise_for :users
resources :courses
resources :courses do
resources :lessons
end
resources :lessons
root 'pages#landing'
get 'pages/home' => 'pages#home' ,as: :home
get '/user/:id' => 'pages#profile',as: :profile
get '/users' => 'courses#index',as: :user_root
end
Course.rb
class Course < ActiveRecord::Base
belongs_to :user
has_many :lesson
validates :user_id , presence: true
end
Lesson.rb
class Lesson < ActiveRecord::Base
belongs_to :course
validates :course_id , presence: true
end
CourseController.rb
class CoursesController < ApplicationController
def index
#courses = Course.all;
end
def new
#course = Course.new;
end
def create
#course = Course.new(course_params);
#course.user_id = current_user.id;
if #course.save
redirect_to course_path(#course)
else
flash[:notice]="Course could not be created ! "
redirect_to new_course_path
end
end
def edit
end
def update
end
def destroy
#course = Course.find(params[:id]);
#course.destroy;
end
def show
#course = Course.find(params[:id]);
end
private
def course_params
params.require(:course).permit(:title, :description, :user_id)
end
end
LessonController.rb
class LessonsController < ApplicationController
def index
#lessons = Lesson.all;
end
def new
#lesson = Lesson.new;
end
def create
#lesson = Lesson.new(lesson_params);
#course = Course.find_by(id: [params[:course_id]]);
if #lesson.save
redirect_to new_course_lesson_path , flash[:notice] = "Lesson successfully saved !"
else
redirect_to new_course_lesson_path , flash[:notice] = "Lesson cannot be created ! "
end
end
def show
#lesson = Lesson.find(params[:id])
end
private
def lesson_params
params.require(:lesson).permit(:title,:description,:video,:course_id)
end
end
Lessonform.html.erb
<%= form_for ([#course,#lesson]) do |f| %>
<%= f.label :lesson_Title %>
<%= f.text_field :title ,placeholder: "Enter the lesson Title" ,:class=>"form-control" %><br />
<%= f.label :Description %>
<%= f.text_area :description ,placeholder: "Enter the lesson Description",rows:"8",:class=>"form-control" %><br />
<center>
<%= f.submit "Create lesson",:class =>"btn btn-lg btn-primary" %>
</center>
<% end %>
One problem i see is that you have defined route resources :lessons twice. Once, inside courses scope and second time outside.
The error seems to occur because in your view #course is nil. So, please check you set #course in a before_action inside lessons_controller#new action.
EDIT
class LessonsController < ApplicationController
before_action :set_course, only: [:new, :create]
def new
#lesson = #course.lessons.build
end
private
def set_course
#course = Course.find_by(id: params[:course_id])
end
end
Also replace has_many :lesson with has_many :lessons inside Course model.
First change you need to make in your Course model as you have singular lesson when defining many association:
has_many :lessons
Also let me know if their are any chances of lessons page being called without courses? If no then please remove:
resources :lessons
I guess also the two defining of courses in routes in creating issue. Please try removing the:
resources :courses
Let me know if you still face any issue.
I have two user types: Artist and Fan. I want Fans to be able to follow Artists. So far following them does not work, but unfollowing does. I have create and destroy set up the same way, but can't seem to get it to work. I get the error Couldn't find Artist without an ID when trying to create a Relationship. Anyway I can find the Artist's ID?
Code below:
relationships_controller.rb
class RelationshipsController < ApplicationController
before_action :authenticate_fan!
def create
#relationship = Relationship.new
#relationship.fan_id = current_fan.id
#relationship.artist_id = Artist.find(params[:id]).id #the error
if #relationship.save
redirect_to (:back)
else
redirect_to root_url
end
end
def destroy
current_fan.unfollow(Artist.find(params[:id]))
redirect_to (:back)
end
end
artists_controller.rb
def show
#artist = Artist.find(params[:id])
end
artists/show.html.erb
<% if fan_signed_in? && current_fan.following?(#artist) %>
<%= button_to "unfollow", relationship_path, method: :delete, class: "submit-button" %>
<% elsif fan_signed_in? %>
<%= form_for(Relationship.new, url: relationships_path) do |f| %>
<%= f.submit "follow", class: "submit-button" %>
<% end %>
<% end %>
models/fan.rb
has_many :relationships, dependent: :destroy
has_many :artists, through: :relationships
belongs_to :artist
def following?(artist)
Relationship.exists? fan_id: id, artist_id: artist.id
end
def unfollow(artist)
Relationship.find_by(fan_id: id, artist_id: artist.id).destroy
end
models/artists.rb
has_many :relationships, dependent: :destroy
has_many :fans, through: :relationships
belongs_to :fan
routes.rb
resources :relationships, only: [:create, :destroy]
Basically, you need to send artist_id to the action. Change your form to this. There is a lot of refactoring required but this one will work for you:
<%= form_for(Relationship.new, url: relationships_path) do |f| %>
<%= hidden_field_tag :artist_id, #artist.id %>
<%= f.submit "follow", class: "submit-button" %>
<% end %>
In controller, you can access it like:
#relationship.artist_id = Artist.find(params[:artist_id]).id
I would consider solving this with a nested route instead:
resources :artists, shallow: true do
resources :relationships, only: [:create, :destroy]
end
this will create these routes in addition to the regular CRUD routes for artist:
Prefix Verb URI Pattern Controller#Action
artist_relationships POST /artists/:artist_id/relationships(.:format) relationships#create
relationship DELETE /relationships/:id(.:format) relationships#destroy
Notice that we use shallow: true which scopes the create route under /artists/:artist_id but not the destroy route.
You can then change your form:
<%= form_for(Relationship.new, url: artist_relationships_path(artist_id: #artist)) do |f| %>
<%= f.submit "follow", class: "submit-button" %>
<% end %>
And your controller:
class RelationshipsController < ApplicationController
before_action :authenticate_fan!
def create
current_fan.relationships.build(artist: Artist.find(params[:artist_id]))
if #relationship.save
redirect_to(:back)
else
redirect_to root_url # makes more sense to redirect back to #artist ?
end
end
def destroy
#relationship = current_fan.relationships.find(params[:id])
#relationship.destroy
redirect_to(:back)
# or redirect back to the artist page.
# redirect_to #relationship.artist
end
end
Notice how we also refactor the destroy action - You never want to have a route with an :id param which points to a completely different resource. Thats just poor REST design. We don't even need to know the artist ID if we know the id of a relationship. Instead here the ID refers to the Relationship resource.
To create a link to destroy the relationship you would do:
<%= link_to 'Unfollow', relationship_path(#relationship), method: :delete %>
And lets get rid of the Fan#unfollow method.
While we are at it we can fix the Fan#following? method.
def following?(artist)
relationships.exists?(artist: artist)
end
By using the relationship (in the ActiveRecord sense of the word!) instead of querying the Relationship model directly you can use eager loading to avoid additional queries and also you don't have to specify the fan.
I'm currently developing an app which allows users to post from their own account, but if they're an administrator of a group or venue, they can also post as that entity. I'm struggling converting the polymorphic association ideas from some of the other questions out there as generally they're all based around being able to comment on multiple things and not from multiple things.
I think my main issue is that I have my user's post form on the homepage, so it does not have an ID in the URL.
My post controller looks like this:
class PostsController < ApplicationController
before_action :authenticate_user!, only: [:create, :destroy]
before_filter :load_postable
def index
end
def new
#post = Postabe.posts.new(post_params)
end
def create
#post = #postable.posts.build(post_params)
if #post.save
flash[:success] = "Post created!"
redirect_to root_url
else
#feed_items = []
render 'static_pages/home'
end
end
def destroy
#post.destroy
redirect_to root_url
end
private
def post_params
params.require(:post).permit(:content)
end
def load_postable
resource, id = request.path.split('/')[1, 2]
resource_name = resource.singularize.classify
if resource_name == "User"
#postable = current_user
else
#postable = resource_name.constantize.find(id)
end
end
end
and my _post_form.html.erb partial:
<%= form_for ([#postable, #postable.post.new]), remote: true do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Create a Post..." %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
my related routes:
devise_for :users, :controllers => { :omniauth_callbacks => "omniauth_callbacks", :registrations => "registrations" }
resources :users, :only => [:index] do
member do
get :favourite_users, :favourited_users
end
resources :posts
end
resources :venues do
resources :posts
end
resources :groups do
resources :posts
end
Models as follows:
class Post < ActiveRecord::Base
belongs_to :postable, polymorphic: true
end
class User < ActiveRecord::Base
has_many :posts, as: :postable, dependent: :destroy
end
class Venue < ActiveRecord::Base
has_many :posts, as: :postable, dependent: :destroy
end
class Group < ActiveRecord::Base
has_many :posts, as: :postable, dependent: :destroy
end
It seems that I keep getting the error
Couldn't find Post without an ID
but I don't know why it's looking for a Post ID if it's not been created yet. Any help would be appreciated!
You have before_filter :load_postable in your controller. By default it will run for all the actions in your controller, even when the id is not specified. The error is thrown by #postable = resource_name.constantize.find(id), id is nil for index method.
Change this line to:
before_filter :load_postable, except: [:index]