I can't get a like system working for my Rails discussion forum:
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :comment
end
class Post < ActiveRecord::Base
belongs_to :user
has_many :comments
has_many :likes, through: :comments
end
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :user
has_many :likes
end
routes.rb:
resources :posts do
resources :comments do
resources :likes do
put "/create", to: "likes#create"
end
end
end
likes_controller.rb:
class LikesController < ApplicationController
def create
#post = Post.find(params[:post_id])
#comment = Comment.find(params[:comment_id])
#like = Like.find(params[:like_id])
#user = current_user.id
Like.create(like_id: #like, post_id: #post, comment_id: #comment, user_id: #user)
respond_to do |format|
format.html { redirect_to post_path(#post) }
end
end
end
rake routes:
post_comment_like_create PUT /posts/:post_id/comments/:comment_id/likes/:like_id/create(.:format) likes#create
post_comment_likes GET /posts/:post_id/comments/:comment_id/likes(.:format) likes#index
POST /posts/:post_id/comments/:comment_id/likes(.:format) likes#create
etc
The problem I keep running into is that it's missing like_id:
No route matches {:action=>"create", :comment_id=>"218", :controller=>"likes", :like_id=>nil, :post_id=>"30"} missing required keys: [:like_id]
What am I doing wrong? I assume most of the code is correct, as the only error it's giving me is a missing ID, I just don't understand how or where to fetch that like_id.
EDIT:
This is the action I'm trying to use:
= link_to post_comment_like_create_path(#post, comment, #like)
The issue is partly in your routes, partly in your controller and partly in the view.
By using the line resources :likes, you are creating routes for the 7 CRUD actions automatically. This means you do not need to manually declare a create action in your routes (which should be a POST rather than PUT). I'd suggest reading the Rails Guide on Routing.
In your controller, you are attempting to create an object called #like by finding a Like based on an ID. But you are creating this for the first time, so there is nothing to find.
In your view, you shouldn't be using a link_to for anything that affects the database but rather a button_to and the path you are using is also part of the problem.
Make the following changes:
routes.rb
resources :posts do
resources :comments do
resources :likes
end
end
likes_controller.rb
class LikesController < ApplicationController
def create
#post = Post.find(params[:post_id])
#comment = Comment.find(params[:comment_id])
#user = current_user.id
Like.create(post_id: #post, comment_id: #comment, user_id: #user)
respond_to do |format|
format.html { redirect_to post_path(#post) }
end
end
end
view
= button_to post_comment_likes_path(#post, comment)
I would suggest you look at the guides around nesting of routes. Nesting this deep can easily become cumbersome.
Related
I am looking to create a new record within a nested resource in rails as according to http://guides.rubyonrails.org/routing.html.
My model is:
class Entry < ApplicationRecord
belongs_to :user
belongs_to :event
class User < ApplicationRecord
has_many :entries, dependent: :destroy
class Event < ApplicationRecord
has_many :entries, dependent: :destroy
And I have declared my route as
Rails.application.routes.draw do
resources :users
resources :events do
resources :entries
end
What is the syntax for the entries controller for me to be able to create an entry on a link like events/2/entries/new. This is what I was trying:
class EntriesController < ApplicationController
def new
#entry = Entry.new
end
def create
#entry = Entry.new(params[:entry])
if #entry.save
redirect_to #user
flash.now[:info] = "Event Created"
else
render '/create'
flash.now[:danger] = "Somthing went wrong"
end
end
def entry_params
params.require(:event_id).permit(:siCard, :course)
end
end
On by new.html.erb I am using
<%= form_for(new_event_entry_path) do |f| %>.
But I cant get it to work as No route matches [POST] "/events/1/entries/new"
Many thanks
The route you've provided in form_for does not accept POSTs. You've provided the route that renders the new page, not the route that accepts new records, which is the create route. For that, you'll want to use event_entries_path(#event) route helper.
With nested resources, the form_for's first argument should be an array: <%= form_for([#event, #entry], .... Rails will intelligently choose the route you need based on whether the #entry is persisted or not.
So I'm pretty new to Rails; I have an assignment I'm trying to finish for my coursework and I'm stuck. I already have Topics and Posts for a reddit clone that the assignment is on. But now I'm supposed to add one summary for each post and after diving into that for awhile, I am stuck.
Here are the assignment requirements:
Create a Summary model which references posts.
Modify Post so that it has_one summary.
Create the necessary routes to create and show post summaries.
Create Summary views to create and show post summaries.
The error I'm hitting when I try to follow a 'Summary' link I've created to sit next to a post title:
ActionController::UrlGenerationError in Topics#show
No route matches {:action=>"show", :controller=>"summaries", :id=>nil, :post_id=>nil, :topic_id=>"1"} missing required keys: [:id, :post_id]
It's pointing me to the Topics#show method which looks like this:
def show
#topic = Topic.find(params[:id])
#posts = #topic.posts
authorize #topic
end
And my SummariesController looks like this:
class SummariesController < ApplicationController
def new
#topic = Topic.find(params[:topic_id])
#post = #topic.posts
#summary = Summary.new
authorize #summary
end
def create
#topic = Topic.find(params[:topic_id])
#post = #topic.posts
#summary = Summary.new(params.require(:summary).permit(:description))
authorize #summary
if #summary.save
flash[:notice] = "Your post summary was saved"
redirect_to [#topic, #post]
else
flash[:error] = "Sorry. There was an error saving your summary. Please try again"
render :new
end
end
def show
#summary = Summary.find(params[:id])
#post = Post.find(params[:post_id])
#topic = Topic.find(params[:topic_id])
authorize #summary
end
end
And here are my models:
Post:
class Post < ActiveRecord::Base
has_many :comments
has_one :summary
belongs_to :user
belongs_to :topic
default_scope { order('created_at DESC') }
end
Topic:
class Topic < ActiveRecord::Base
has_many :posts
end
Summary:
class Summary < ActiveRecord::Base
belongs_to :post
end
Lastly here is my routes file and the html.erb link:
Rails.application.routes.draw do
devise_for :users
resources :topics do
resources :posts, except: [:index] do
resources :summaries, except: [:index, :edit]
end
end
get 'about' => 'welcome#about'
root to: 'welcome#index'
end
html.erb summary Link:
<small>
<%= link_to "Summary", topic_post_summary_path(#topic, #post, #summary)%>
</small>
I'm not sure where I'm going wrong here.
I have an application with 3 models user company and post.
I'm having trouble with figuring out the logic
A user can have many companies and a company can have many posts.
here are my models
class user < ActiveRecord::Base
has_many :companies
end
class company < ActiveRecord::Base
belongs_to :user
has_many :posts
end
class post < ActiveRecord::Base
belongs_to :company
end
Note: company has user_id, post has company_id in table
how do I make sure when creating a post the company_id is automatically recorded
extra information: for routes it will be resources :jobs and resources :companies
--update--
post controller
def new
Post.new(post_params)
end
def create
Post.new(post_params)
if #post.save
redirect_to #post
else
render 'new'
end
end
routes
resources :companies
resources :posts
I think I would probably nest posts within companies, so you would have available to you the company_id in the parameters.
Note your path names will change if you do this.
Routes.rb
resources :companies do
resources :posts
end
posts_controller.rb
def create
#company = Company.find(params[:company_id])
#post = #company.posts.build(post_params)
...
end
And since you will want to find the company on each of these actions in the controller, you can refactor it a bit.
class PostsController
before_action :set_company
def index
#post = #company.posts
end
def create
#post = #company.posts.build(post_params)
...
end
...
...
private
def set_company
#company = Company.find(params[:company_id])
end
end
2 options come to mind.
You can create a hidden company_id field in your posts#new view, so it would be sent automatically along with your params and hence be set in the model accordingly (provided that you permit it). But in that case you'll need to populate that field in the controller when creating the #post. Just like this:
#post = Post.new(company_id: some_company_id)
Use nested resources.First of all change your routes to this:
resources :companies do
resoures :posts
end
That way when you look at the rake routes output you'll see the :company_id param being set in the request url. So when you go on creating a post, you can do #post.company_id = params[:company_id] and you're done.
I'm new to Rails. I'm building my first app - simple blog. I have User and Post models, where each user can write many posts. Now I want to add Comment model, where each post can have many comments, and also each user can comment on any post created by any other user.
In Comment model I have
id \ body \ user_id \ post_id
columns.
Model associations:
user.rb
has_many :posts, dependent: :destroy
has_many :comments
post.rb
has_many :comments, dependent: :destroy
belongs_to :user
comment.rb
belongs_to :user
belongs_to :post
So how do I correctly define create action in CommentsController?
Thank you.
UPDATE:
routes.rb
resources :posts do
resources :comments
end
comments_controller.rb
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(comment_params)
if #comment.save
redirect_to #post
else
flash.now[:danger] = "error"
end
end
The result is
--- !ruby/hash:ActionController::Parameters
utf8: ✓
authenticity_token: rDjSn1FW3lSBlx9o/pf4yoxlg3s74SziayHdi3WAwMs=
comment: !ruby/hash:ActionController::Parameters
body: test
action: create
controller: comments
post_id: '57'
As we can see it doesnt send user_id and works only if I delete validates :user_id, presence: true string from comment.rb
Any suggestions?
In your way you should put this:
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(comment_params)
#comment.user_id = current_user.id #or whatever is you session name
if #comment.save
redirect_to #post
else
flash.now[:danger] = "error"
end
end
And also you should remove user_id from comment_params as strong parameters .
Hope this will help you .
Associations
To give you a definition of what's happening here, you have to remember whenever you create a record, you are basically populating a database. Your associations are defined with foreign_keys
When you ask how to "add comments to User and Post model" - the bottom line is you don't; you add a comment to the Comment model, and can associate it with a User and Post:
#app/models/comment.rb
Class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :post
end
This prompts Rails to look for user_id and post_id in the Comment model by default.
This means if you wanted to create a comment directly, you can associate it to either of these associations by simply populating the foreign_keys as you wish (or use Rails objects to populate them)
So when you want to save a comment, you can do this:
#app/controllers/comments_controller.rb
Class CommentsController < ApplicationController
def create
#comment = Comment.new(comment_params)
end
private
def comment_params
params.require(:comment).permit(:user_id, :post_id, :etc)
end
end
Conversely, you can handle it by using standard Rails objects (as the accepted answer has specified)
Class CommentsController < ApplicationController
before_action :set_user
before_action :set_post
def create
#comment = #post.comments.create(comment_params)
if #comment.save
redirect_to #post
else
flash.now[:danger] = "error"
end
end
private
set_post
#post = User.posts.find(params[:post_id])
end
set_user
#user = User.find(params[:user_id])
end
comment_params
params[:comment].permit()
end
How can you limit the records that are available to the show action? The problem I'm having is that you can manually change the ID in the URL and look at projects that do not belong to the company.
My Routes Look like this:
/companies/:id/projects/:id
This is the show action
projects_controller.rb
def show
#project = Project.find(params[:id])
#company = Company.find(params[:company_id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #project }
end
end
routes.rb
resources :companies do
resources :projects
resources :employees
resources :requests do
put 'accept', :on => :member
end
end
project.rb
class Project < ActiveRecord::Base
attr_accessible :title
belongs_to :company
validates :title, presence: true
end
company.rb
class Company < ActiveRecord::Base
attr_accessible :name
has_many :projects
end
Assuming you have a has_many relationship between Company and Project I would change your controller code to this:
def show
#company = Company.find(params[:company_id])
#project = #company.projects.find(params[:id])
end
Keep in mind though that this does not really solve your problem as people can still change the company_id and view other companies easily. What you need is a more solid authorization framework like CanCan that prevents unauthorized access to resources.
May be, you should use smth like this:
#project = #company.projects.find(params[:id])
Check this for details.
Try to change the action to this
def show
#company = Company.find(params[:company_id])
#project = #company.projects.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #project }
end
end