At the time I have two models: Patient (has many), and Treatment (belongs to).
Until now I displayed the form for a new treatment on the patient show page and all worked fine. But now i want to outsource the treatments form to an new page. To visualize it better:
<%= render "treatments/form" %>
change to:
<% link_to "new", "treatments/form" %>
SO my problem is that I always become an route error:
No route matches [GET] "/patients/treatments/form"
But the routes look so, and i thought they would work:
resources :patients do
resources :treatments
resources :paintings
end
And the controller of the treatments:
class TreatmentsController < ApplicationController
def create
#patient = Patient.find(params[:patient_id])
#treatment = #patient.treatments.create(params[:treatment])
redirect_to patient_path(#patient)
end
def destroy
#patient = Patient.find(params[:patient_id])
#treatment = #patient.treatments.find(params[:id])
#treatment.destroy
redirect_to patient_path(#patient)
end
end
Since your proposed form is really just a way of creating a new patient treatment, you should consider following RESTful conventions and create a new TreatmentsController action called new:
# app/controllers/treatments_controller.rb
class TreatmentsController < ApplicationController
def new
#patient = Patient.find(params[:patient_id])
end
Since your treatments routes are a nested resource of the patients nested resource routes, you'll need to pass the patient_id to your link helper:
<%= link_to "New Patient Treatment", new_patient_treatment_path(#patient) %>
This will enable you to properly access your nested route for treatment#new.
Related
I've recently been working with controllers and routing. Typically when I write my new and create routes, they are:
get '/pages/new' => 'pages#new', as: :new_page
post '/pages/ => 'pages#create'
and the controller actions are:
def new
#page = Page.new
end
def create
#page = Page.new(page_params)
if #page.save
flash[:notice] = "Successfully created page."
redirect_to page_path(#page)
else
render action: 'new'
end
end
private
def page_params
params.require(:page).permit(:book_id, :text, :page_number)
end
So that works for new_page_path and post_pages_path.
But what if did new_page_path(book_id: #book.id)? (Also a page belongs to a book). What does this mean and how does that change my new and create methods? Also, if I were to create a new page, how would I change the count of the number of pages in my book?
I don't know how your models look like, but i suppose that you've got association: has_many.
If so, you might want to use nested resources in routes https://guides.rubyonrails.org/routing.html#nested-resources
resources :books do
resources :pages
end
that gives you always:book_id in params for pages actions. You can easily create a page associated with the book than by:
#book = Book.find params[:book_id]
#book.pages.build(pages_params)
And you don't really need to handle increasing count on create, if you properly set association. book.pages.count will tell you the truth.
My app is a web forum. Root page is a list of user-submitted
categories. I click one and it links to a list of user-submitted posts
about that category. I click a post and it links to a list of comments
about that post. Those are the 3 tiers.
CATEGORIES INDEX This is a list of clickable categories
<% #categories.each do |category| %>
<%= link_to category.title, category %>
<% end %>
CATEGORIES SHOW I clicked a category, now I'm here looking at a list of posts
<%= render :partial => #category.posts %>
<% end %>
_POSTS The posts are rendered from this here partial
<%= div_for(post) do %>
<%= link_to post.body, Post %>
Clicking that post link takes me to POSTS INDEX.
I'm not sure if this is a desirable flow of a Rails app. It seems odd
to go from Categories, to Posts, to Comments using Categories_Index,
Categories_Show, and Posts_Index, respectively. I don't know how to display or submit comments from this POSTS INDEX. #comments.each do |comments| provides an error and so does the render: partial method. I can not use the same methods for Comments that I used for Categories and Posts.
MODELS Models are complete with has_many, belongs_to, etc.
CATEGORIES CONTROLLER
def index
#categories = Category.all
end
def create
#category = current_user.categories.build(categories_params)
end
POSTS CONTROLLER
def create
#category = Category.find(params[:category_id])
#post = #category.posts.new(post_params)
COMMENTS CONTROLLER
def index
#subcomments = Subcomment.all
end
def create
#subcomment = current_user.subcomments.build(subcomment_params)
end
ROUTES
Rails.application.routes.draw do
resources :comments
resources :posts
devise_for :users
resources :categories do
resources :posts do
end
resources :comments
end
root "categories#index"
I successfully added posts to categories. How can I add comments to posts? Is my approach correct?
I assumed you have the following Model Relationships:
Model Category
has_many :posts
Model Post
has_many :comments
belongs_to :category
Model Comment
belongs_to :post
You are asking "How can I add comments to posts?"
In the page where you render all POSTS data,
you should USE posts ID as you main parameter.
So, meaning you should have post_id column/field inside Comments Table.
After saving the comments data, usually like [title, message, date ....].
In your Post Controller, you can get comments like:
// multiple Posts data
#posts = Post.all
#post.each do |post|
post.comments
...
end
//single Post
#post = Post.first // or Post.find(:id => params[:post_id])
#post.comments
If you are sending data using form, just put some hidden text field,
setting the name & value:
name="post_id"
// or something like:
name="comment[:post_id]"
//depends on how you constract the form.
Then set the value:
value="<%= params[:post_id ]%>"
Finnally, you can get the value like getting the other comments_field names.
Usually you should have this in in config/routes.rb,
resources :commets
Then your FORM path is like:
<%= form_for #comment, :url => #comments_path %>
Your Comments Controller should have like:
def index
...
end
def show
...
end
def edit
...
end
def new
#comment = Comment.new
end
def create
#comment = Comment.create(comment_params)
if #comment.save
....
redirect_to comments_path
else
.....
end
end
# For params permit in Rails 4 ^
def comment_params
params.require(:comment).permit!
end
I created the model User and the model Profile. On my homepage I have a link in the dropmenu navigation bar that links to Edit Profile. The problem I face is "No route matches {:action=>"edit", :controller=>"profiles", :id=>nil} missing required keys: [:id]".
The route for edit page is "edit_profile_path" with verb GET and URI pattern "/profiles/:id/edit(.:format)". I am having a hard time getting the "id" inserted. Below is the code that I have on my app.
In model Profile file I have:
class Profile < ActiveRecord::Base
belongs_to :user, dependent: :destroy
end
In model User file I have:
class User < ActiveRecord::Base
has_one :profile
end
The profile has many attributes, but one of them is "user_id" which is an integer that is equal to the User's id. So User #5 with id#5 is the owner of Profile#5.
Here is the code that I have in the View file:
<li><%= link_to "Edit Profile", edit_profile_path(#profile) %></li>
With regards to the code directly above, I have tried inserting different codes inside the parenthesis, from #profile.id, #profile, #user.id, and #user. But it has not worked.
I created a profiles controller and I think (but I am not certain) that my problem is coming from the profiles_controller file. Here is the code that I have:
class ProfilesController < ApplicationController
before_action :authenticate_user!
before_action :set_profile, only: [:edit, :update]
def edit
end
def new
#profile = Profile.new
end
def create
#profile = Profile.new(profile_params)
#profile.user_id = current_user.id
if #profile.save
redirect_to welcome_path
else
render 'new'
end
end
def update
#profile.update(profile_params)
redirect_to welcome_path
end
private
def set_profile
#profile = Profile.find(params[:id])
end
end
You are getting this error because in your view, your #profile in nil.
So, you have to get the current_profile in your view so that you can go to the edit page of that profile.
If you already have access to your current_user helper method, then, in your view, you can simply do:
<li><%= link_to "Edit Profile", edit_profile_path(current_user.profile) %></li>
A few things to note (which may be the key to solving your problem).
You are having a 1 to 1 relationship, and the user can access his profile only when he is logged in. Since you already have a (presumably properly working) current_user method, use it all the time.
def new
current_user.build_profile
end
def create
current_user.build_profile(profile_params)
#etc
end
It's also a logical way to get the user's profile
private
def set_profile
#profile = current_user.profile
end
In your view:
<%= link_to edit_profile_path(current_user.profile) %>
I think this makes much more sense in your code and is much more readable. Additionally, I think such approach will save you a lot of errors such as the one you're encountering now.
Have you tried?
edit_profile_path(id: #profile.id)
Also did you put this route in your routes file?
resources :recipes do
resource :like, module: :recipes
resources :comments, only: [:new, :create, :show, :index], module: :recipes
end
recipe_comments GET /recipes/:recipe_id/comments(.:format) recipes/comments#index
POST /recipes/:recipe_id/comments(.:format) recipes/comments#create
Comments are in /recipes/:id
Recipe Controller
def show
#recipe = Recipe.find(params[:id])
#comment = #recipe.comments.new
#clean_recipe = Sanitizer.new(#recipe)
end
Recipes::CommentsController
Theres a before action that finds recipe.
def create
#comment = #recipe.comments.new(comment_params)
#comment.user_id = current_user.id
if #comment.save
redirect_to recipes_path
end
end
I've done
form_for([#article,#comment]) and form_for [#article,Comment.new]
and still the comment isn't persisted. I am wondering because of the module namespace, do I have to do something different?
Add logger.info 'I am inside create action in comments controller' inside the create action. If you see this logger message in the rails console, then the create action gets called. It means your routes are defined correctly, your form is using the right form helper.
the correct format is form_for([#recipe, #comment]) however, I had a length validation in my comment model which was failing to pass. I set too_short and too_long messages, however they didn't show up, which further confused me
I have a scenario being:
resources :magazines do
resources :articles do
resources :comments
end
end
So as to avoid nesting more than 2 levels deep I have re-factored this to be:
resources :magazines do
resources :articles
end
resources :articles do
resources :comments
end
My article show action URL is:
/magazines/3/articles/11
In this view I have a form for creating a new comment.
When a comment is saved successfully the form redirects which all works well.
When the form submission is not successful I wish to redisplay the view with validations errors displayed. I understand the correct way to do this is to render the 'articles/show' view. This also works and the view is redisplayed with the validation errors shown.
The problem is when the save fails and articles/show is rendered the URL is no longer correct and is shown as:
/articles/11/comments
class ArticlesController < ApplicationController
def show
#article = Article.find(params[:id])
#comments = #article.comments.order(created_at: :asc).page(params[:page]).per_page(5)
#comment = Comment.new
end
end
class CommentsController < ApplicationController
def create
#article = Article.find(params[:id])
#comment = #article.comments.new(discussion_params)
#comment.user_id = current_user.id
if #comment.save
redirect_to #article
else
render 'articles/show'
end
end
private
def discussion_params
params.require(:comment).permit(:content)
end
end
I solved this by changing my routes back to the way it originally was and now the article show action includes the magazine in the url.
I understand this breaks the "no more than 2 levels deep" routing rule but it's the only way I can get it to work.