Y.A.N (yet another newbie)
Using rails, I have a 'students' controller and a 'contacts' controller and of course, a student model and contact model. contact belongs_to student and student has_many contacts. I have an index page of students that lists each student with the option to click "Add Contact" for each student. I'm losing it when I try to call the contacts' 'new' action and subsequently the "new" view for contacts. How/where do i initialize the student and/or contact so the contact knows the student_id. Right now, I'm passing the student to the new_contact_path but then I have to refer to the student_Id as params(:format) inside the contact controller in order to get it to work. this is obviously not the best way.
Any ideas Pieces of code below:
ContactsController:
def new
#contact = Contact.new
#student = Student.find(params[:format])
end
students index:
<% #students.each do |student| %>
<tr>
<td><%= link_to 'Contacts', new_contact_path(student) %></td>
</tr>
<% end %>
you could pass student_id in the new_contact_path link.
<%= link_to 'Contacts', new_contact_path(student, :student_id => student.id) %>
and in the controller
class ContactsController
def new
#student = Student.find(params[:student_id])
end
end
When you need to find the student with
#student = Student.find(params[:format])
there is definitely something wrong with your routes.
Your routes should be something like:
resources :students do
resources :contacts
end
And your code should be:
#student = Student.find(params[:student_id])
The index view you posted should work fine provided you initialised #students properly.
Related
I have an app where users can ask questions and bookmark certain questions. I'm done with the users, questions, and answers, so I've added a BookmarkController & Bookmarks model. At first, I considered using associations, but my app has a few associations already so I'm (or I've attempted at) using query parameters such as user_id and question_id to fetch bookmarks.
The structure is a bit like StackOverflow. A user navigates to a single question view and bookmarks it on that page. This creates a new bookmark model containing the user_id of current_user and the question_id. The user can go to his profile to view all the questions he bookmarked, fetched using his user_id. (Answers cannot be bookmarked. Only questions.)
I've been getting a 'param is missing or the value is empty: bookmark' error, although I have followed similar steps I did for my QuestionsController. It would be great if someone could help me out in identifying what's wrong/bad about my code!
rake routes (first part omitted)
bookmark_question PUT /questions/:id/bookmark(.:format) questions#bookmark
questions GET /questions(.:format) questions#index
POST /questions(.:format) questions#create
new_question GET /questions/new(.:format) questions#new
edit_question GET /questions/:id/edit(.:format) questions#edit
question GET /questions/:id(.:format) questions#show
PATCH /questions/:id(.:format) questions#update
PUT /questions/:id(.:format) questions#update
DELETE /questions/:id(.:format) questions#destroy
route.rb (excerpt)
# Questions
get '/questions/:id' => 'bookmarks#create'
show.html.erb (questions#show)
<% if current_user %>
<%= link_to "Bookmark", :controller => 'bookmarks', :action => 'create' %>
<% end %>
BookmarksController
class BookmarksController < ApplicationController
def new
#bookmark = Bookmark.new
end
def create
#question = Question.find(params[:id]) # when I delete this line, I get a new error - "undefined local variable 'params'"
#bookmark = Bookmark.new(bookmark_params)
#bookmark.user_id = current_user.id
#bookmark.question_id = #question.id
#bookmark.save
redirect_to #question
end
def destroy
end
private
def bookmark_params
params.require(:bookmark).permit(:user_id, :question_id)
end
end
Bookmark model
class Bookmark < ApplicationRecord
validates :user_id, presence: true
validates :question_id, presence: true
end
QuestionsController
(at the moment, contains no reference to Bookmarks. I thought so because I did the routing, but this might be where I'm going wrong)
class QuestionsController < ApplicationController
def index
#questions = Question.all
end
def show
#question = Question.find(params[:id])
#answers = Answer.all
# Delete only appears when no answers
#deletable = (current_user== User.find(#question.user_id)) && (#question.answers.all.size==0)
end
def new
#question = Question.new
end
def create
if logged_in?
#question = Question.new(question_params)
#question.user_id = current_user.id
#question.save
redirect_to #question
else
redirect_to login_path
end
end
def destroy
#question = Question.find(params[:id])
#question.destroy
redirect_to root_path
end
private
def question_params
params.require(:question).permit(:picture_url, :country, :educational_level, :topic)
end
end
profile index.html.erb (just for ref)
<% if (#bookmarks.count == 0) %>
///
<% else %>
<%= #bookmarks.each do |bookmark| %>
<!-- Show bookmark content here like Question.find(bookmark.question_id) etc -->
<% end %>
<% end %>
I have looked a the previous qns that have the same error as me. But they were all using associations. I hope to not use associations as the bookmark model only needs to keep a record of the user id and qn id.
UPDATE
So, referring to the answers given, I updated my erb to:
<% if logged_in? %>
<%= link_to "Bookmark", :controller => 'bookmarks', :action => 'create', bookmark: {user_id: current_user.id, question_id: #question.id} %>
<% end %>
hence specifying the controller and action (and the params) that need to be directed. But rails sends an error:
No route matches {:action=>"create", :bookmark=>{:user_id=>2, :question_id=>4}, :controller=>"bookmarks", :id=>"4"}
So I assume it was a routing problem. As Pavan suggested, I did consider nesting my resources, but the nesting is already one level deep, as such:
resources :questions do
resources :answers
end
And I reckon doing something like:
resources :questions do
resources :bookmarks # or resources :bookmarks, only: create
resources :answers
end
won't work. (And it didn't :( )
I'm not so sure how to get this routing problem fixed (tried Googling). Thanks.
param is missing or the value is empty: bookmark
The reason for the error is bookmark_params expects a :bookmark key to be present in the params hash, which in your case is missing since you are not passing any.
Change link_to like below:
<% if current_user %>
<%= link_to "Bookmark", :controller => 'bookmarks', :action => 'create', bookmark: {user_id: current_user.id, question_id: #question.id} %>
<% end %>
Also, the route get '/questions/:id' => 'bookmarks#create' isn't right and would conflict with this route question GET /questions/:id(.:format) questions#show. I would instead recommend building nested routes
resources :users do
resources :questions do
resources :bookmarks, only: [:create]
end
end
Update:
Along with the above, you should change #question = Question.find(params[:id]) to #question = Question.find(params[:bookmark][:question_id])
'param is missing or the value is empty: bookmark, this error means that, there is no bookmark key present in your params object, but you defined your bookmark_params to have one:
def bookmark_params
params.require(:bookmark).permit(:user_id, :question_id)
end
That's why it's throwing the above error message.
You should make sure you send the user_id and question_id key/value pairs under the bookmark key. Something like this:
bookmark: { user_id: 1, question_id: 2}.
So, your code should look something like this (adding the bookmark to params):
<%= link_to "Bookmark", :controller => 'bookmarks', :action => 'create', bookmark: {user_id: current_user.id, question_id: #question.id} %>
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 have a model of payments and visits. I have associated them in the model shown below. A visit can only have one payment.
I have it set up that a user fills out a visit form and then once completed is redirected to the payment form. What I would like to happen, is the for the visit id to be automatically passed into a hidden_field in the visit_id form on the next page.
class Visit < ActiveRecord::Base
has_one :payment
end
class Payment < ActiveRecord::Base
belongs_to :visit
end
It seems fairly basic, I just can't seem to wrap my mind around the associations correctly. I've searched around and seen a few people trying to explain it but whatever I try is not working correctly. Thanks in advance!
One way to do it would be, in your VisitsController
def create
#visit = Visit.create(visits_params)
if !#visit.save
render :new, error: "Something went wrong"
else
#payment = #visit.build_payment
end
end
Then in your visits/create.html.erb, simply put
<%= form_for #payment do |f| %>
<%= f.hidden_field :visit_id %>
<% end %>
It should work but it doesn't feel right from a RESTFUL perspective. A better way to do it would be to have, in your config/routes.rb
resources :visits do
resources :payments
end
That will generate the following route:
GET /visits/:visit_id/payments/new
Then in the VisitsController
def create
#visit = Visit.create(visit_params)
if !#visit.save
render :new, error: "Something went wrong"
else
redirect_to new_payment_path(visit_id: #visit.id)
end
end
And in your PaymentsController
def new
#visit = Visit.find(params[:visit_id])
#payment = #visit.build_payment
end
In your payments/new.html.erb don't forget to put
<%= form_for #payment do |f| %>
<%= f.hidden_field :visit_id %>
<% end %>
And there you have it... Let me know if that doesn't make any sense.
Rails 3 ruby 1.9
I am trying to pass a "products" id to a "Details" page and get error "Couldn't find Product without an ID"
This is the link in my browser address bar:
http://localhost:3000/performance_details?product_id=8
My Controller
class ProductsController < ApplicationController
def performance_details
#title = "Performance Details"
#products = Product.find(params[:id])
end
The view that's passing the object ID
<%=link_to 'Details', details_path(product_id: product) %>
The view receiving the object ID
<%#products.each do |product| %>
Do some stuff with products.....%>
<%end%>
Routes File
match 'details' => "products#details"
Your parameter name is product_id, not just id. Here is how your controller should look like:
class ProductsController < ApplicationController
def performance_details
#title = "Performance Details"
#products = Product.find(params[:product_id])
end
That's probably how you should do it:
# routes
resources :products do
member do
get :performance_details
end
end
-
# view
# your url will be /products/:id/performance_details
<%=link_to 'Details', performance_details_product_path(product) %>
-
# controller
class ProductsController < ApplicationController
def performance_details
#title = "Performance Details"
#product = Product.find(params[:id])
end
end
Change the #product line in your products controller to the following:
#product = Product.find(params[:product_id])
From your comment below, you're only finding one product (note the change from plural #products to singular #product, so you don't want to use each. Change this:
<%#products.each do |product| %>
Do some stuff with products.....%>
<%end%>
To just do things with #product, rather than product. So get rid of the outer block, and only use #product. I'd overall recommend the refactoring given in Robin's answer.
You don't have id in your params. According to your URL /performance_details?product_id=8, your id is in a variable called product_id.
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.