ActiveRecord Couldn't find Travel with 'id'= - ruby-on-rails

I search a solution on stackoverflow but there are different issues each time, so I decided to ask the question.
On my application, I have Travel, which has many posts. One user can create many travels, with many posts on one travel.
But when I try to create a post, I have this error :
ActiveRecord::RecordNotFound in PostsController#create
Couldn't find Travel with 'id'=
I don't understand why, so if someone could help me ..
Here is my Posts_controller.rb (create action):
def create
#travel = Travel.find(params[:id])
#post = #travel.posts.new(posts_params)
#post.user = current_user
if #post.save
flash[:success] = "Your post is published"
redirect_to user_path(current_user)
else
render 'new'
end
end
Here is my the models :
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :travel
geocoded_by :country
after_validation :geocode
end
class Travel < ApplicationRecord
has_many :posts
belongs_to :user
end
My routes :
# Travel
resources :travels, :shallow => true do
# Posts
resources :posts
end
And a ligne for form :
<%= form_for(#post, :html => {class: "form-horizontal", role: "form"}, :url => travel_posts_path(#travel)) do |f| %>
<div class="form-group">
<%= f.text_field :travel_id, placeholder: #travel.id %>
</div>

params[:id] will give you the post's id, and you're using it to find a travel. This could potentially not error and give you the wrong travel, but that's not what you're looking for. You specified travel_id in your form, so what you are looking for is params[:travel_id].
In the future you may want to use the debugger right at the top of the controller function and puts the params object to see what's inside. This will give you insight to if you're calling the wrong keys and what the structure looks like as things get complicated.

Related

Wrong posts showing up in other board

I current have my project set up like this:
resources :boards, :path => '' do
resources :posts, :path => 'thread' do
resources :replies
On /board1/ only posts from board1 show, same for board2. In /board1/thread/1/ it shows post 1 and the replies to it.
However in /board2/thread/1/ the post that is showing is from board1/thread/1/, and in the reverse board1/thread/2/ shows the post from board2/thread/2/.
Each post has a related board_id in the db, and each reply has the related post_id in the db.
How can I keep these separate?
class Board < ActiveRecord::Base
has_many :posts
has_many :replies, through: :posts
include FriendlyId
friendly_id :name, use: :slugged
accepts_nested_attributes_for :posts, :replies
end
class Post < ActiveRecord::Base
belongs_to :board
has_many :replies, dependent: :destroy
accepts_nested_attributes_for :replies
include FriendlyId
friendly_id :pid, use: :slugged
after_create :set_pid
def set_pid
post_max = self.board.posts.maximum(:pid)
reply_max = self.board.replies.maximum(:pid)
if post_max.to_i < reply_max.to_i
self.update_attributes(:pid => reply_max.to_i + 1)
else
self.update_attributes(:pid => post_max.to_i + 1)
end
end
end
Code to display post in /:board_id/show:
<% #board.posts.find_each do |post| %>
<%= post.subject %>
<%= post.name %>
<%= post.email %>
<%= post.created_at %>
No.<%= post.pid %>
<%= link_to "[reply]", board_posts_path(#board, #post)%>
<br>
<%= post.comment %><br><br>
<%= render "replies/replies" %>
<% end %>
Code to display post in /:board_id/thread/:id:
<p>
<%= #post.subject %>
<%= #post.name %>
<%= #post.email %>
<%= #post.created_at %>
No.<%= #post.pid %>
<br>
<%= #post.comment %>
</p>
Edit:
class RepliesController < ApplicationController
def create
#board = Board.friendly.find(params[:board_id])
#post = Post.friendly.find(params[:post_id])
#reply = #post.replies.create(reply_params)
redirect_to #board
end
private
def reply_params
params.require(:reply).permit(:name, :email, :subject, :comment, :pid)
end
end
class PostsController < ApplicationController
def show
#boards = Board.all
#replies = Reply.all
#post = Post.friendly.find(params[:id])
end
def create
#board = Board.friendly.find(params[:board_id])
#post = #board.posts.create(post_params)
if #post.save
redirect_to #board
else render #board
end
end
private
def post_params
params.require(:post).permit(:name, :email, :subject, :comment, :pid)
end
end
The missing part here is the RepliesController which is the source of the problem if I got the question correctly.
Most probably you have there something like #replies = current_post.replies which fetch all replies of the given post regardless of the current board. Scoping post by board will solve the problem:
current_post = Post.find_by(board_id: params[:board_id], id: params[:post_id])
if current_post
#replies = current_post.replies
end
On your friendly_id declaration in the Post model, you don't have the pid as globally unique. Use this form of friendly_id, instead:
friendly_id :pid, use: :scoped, scope: :board
In this way, duplicate friendly_id values for pid are kept separate by the board that they belong to. This is necessary for slugging nested resources properly. The :scoped value says that it's for nested (scoped) models, and the scope: key indicates that posts is nested within boards. Note that you may have to do this with replies, as well.
You'll also want to make sure that your indexes for your :slug are correct. Typically when the :scope is incorrect, you'll find it when you try to save the record. In this case, it looks like the indexes might not be set correctly to ensure the uniqueness of the board name/post pid combination. Check out Friendly ID 4 Using scoped module for more information.
When you have the indexes sorted out, you'll find that inserting new records will require you to have the friendly_id (based on your pid) already assigned. You may also want to look into using slug candidates to dynamically generate the proper slug at creation time. Also check out slug candidates rails 4 for some usage information.

Rails: c.id submits nil in comment.each do |c.id| block

I'm trying to implement a comment system for users in which each comment can be upvoted. I have a model UpVote which polymorphically belongs_to multiple models, including Comments, while each User has_many Comments.
UpVote Model:
class UpVote < ActiveRecord::Base
belongs_to :voteable, polymorphic: true
end
Comment Model:
class Comment < ActiveRecord::Base
belongs_to :user
has_many :up_votes, as: :voteable
end
User Model:
class User < ActiveRecord::Base
has_many :comments
has_many :up_votes, as: :voteable
end
User Show: I put asterisks around the line with the error.
<% #user.comments.each do |c| %>
<%= c.text %>
<%= c.username %>
<%= c.id %>
***<%= link_to "upvote", upvote_comment_path(c.id), method: :post, :class => "btn btn-small" %>***
<% end %>
Comment Controller:
def upvote
#comment = Comment.find(params[:id])
UpVote.create!(voteable_id: params[:id], voteable_type: 'Comment')
redirect_to root_path
end
Routes:
post 'comments/:id/upvote' => 'comments#upvote', as: 'upvote_comment'
But when I submit the UpVote for the Comment on the User page, I get the following error:
ActionController::UrlGenerationError in Users#show
No route matches {:action=>"upvote", :controller=>"comments", :id=>nil} missing required keys: [:id]
It seems like the link_to isn't accepting the c.id, which is strange because c.id on its own prints the #comment.id normally. What am I doing wrong?
ERROR IN BOUNTY MESSAGE: I meant "#comment.id", not "#comment.up_vote.count" in the bounty message, but it doesn't appear I can edit that.
I suspect that you have a comment in the #user.comments that has an id of nil because it has not yet been persisted. This can happen if you are setting up a new comment on with either #user.comments.new or #user.comments.build. Both of these methods will add the blank comment to the #user.comments array.
There are a number of ways you can avoid displaying comments that are not persisted (see this question). I think the best solution is to avoid using build or new on the association. Instead if you need to display a new comment (such as in a form) prefer Comment.new over #user.comments.build or #user.comments.new.
Well the correct way to solve the problem is to modify the routes.rb, changing action upvote to be on: :member
resources :comments do
post :upvote, on: :member
end
OR
post 'comments/:id/upvote' => 'comments#upvote', as: 'upvote_comment'
What may be happening is that you have created a static rather than a resourceful route. In this case you may have to actually specify the id explicitly in your link_to
<%= link_to "upvote", upvote_comment_path(:id => c.id), method: :post, :class => "btn btn-small" %>
You can avoid this extra markup by creating a resourceful route as in you attempted in your example
resources :comments do
member do
post 'upvote'
end
end
Not sure why the "on: :member" syntax didn't work for you though as it is functionally equivalent to the block syntax. I believe the issue may be that you are specifying the action (upvote) with a symbol instead of string.
Good luck

Following artists by users - few questions

I've made my own app basing on the railstutorial.org website, and i'm now on chapter 11. Everything's fine, i've learned a lot from this tutorial and now i'm continuing work on my app and i'm actually on model "Artists" where every user can create new artist ex.Michael Hartl ;) and add their most popular quotations. The problem is to allow users to follow their favourite artists and see quotations in feed, just like Microposts feed from railstutorial. Artist and User are two different models, and railstutorial dosn't explaing how to make the "follow system" for that. It's like subscribing channels on YouTube etc.
Can someone explain me how to get this working? What must i change in code?
Answer:
The button:
<%= form_for(current_user.userartists.build(followed_id: #artist.id)) do |f| %>
<div><%= f.hidden_field :followed_id %></div>
<%= f.submit "Follow", class: "btn btn-large btn-primary" %>
<% end %>
Controller
class UserartistsController < ApplicationController
def create
#artist = Artist.find(params[:userartist][:followed_id])
current_user.follow!(#artist)
respond_to do |format|
format.html { redirect_to #artist }
format.js
end
end
end
You should set up a Artist model and a intermediate model called UserArtist (or UserFollowsArtist) where you will store all the matches between users and artists.
class User < ActiveRecord::Base
has_many :user_artists
has_many :artists, :through => :user_artists
end
class Artist < ActiveRecord::Base
has_many :user_artists
has_many :users, :through => :user_artists
end
class UserArtist < ActiveRecord::Base
belongs_to :user
belongs_to :artist
end
Now you can call #user = User.first to get the first user, and #user.artists to get the list of artists the #user is following.
You will have to create a separate controller called UserArtistsController where you will have actions create and possibly destroy (if the user wishes to unfollow the artist).
In your routes.rb:
resources :user_artists, :only => [:create, :destroy]
I guess the follow button will be on the Artists show page so you should have something like this in your view:
<%= button_to "Follow artist", {:controller => :user_artists,
:action => 'create', :artist_id => params[:id] }, :method => :post %>
And in your controller:
class UserArtistsController < ActionController
def create
#user_artist = UserArtist.create(:user_id => current_user.id, :artist_id => params[:artist_id])
#artist = Artist.find(params[:artist_id])
if #user_artist.save
redirect_to #artist
else
flash[:alert] = "Something went wrong, please try again"
redirect_to root_path
end
end
end
Don't forget to create a migration for the Artist and UserArtist. UserArtist table should contain a user_id and a artist_id.

How do I create a numerical rating system in Rails?

I'd like to create a numerical rating system in rails where users can rate a post from 1 - 10.
I've looked on Google but I only find outdated tutorials and star rating gems which simply don't do the job for me.
Perhaps someone can point me to a gem that can help me achieve this?
Ruby Toolbox lists several, although most are DOA. Mongoid_ratings seemed to be the most recently updated, although you may not want to go the Mongo route.
https://www.ruby-toolbox.com/categories/rails_ratings
I would suggest building from scratch. Heres a quick (and probably non-functional/non-secure) hack that might help get you started:
Routes
resources :articles do
resources :ratings
end
Models
class Article < ActiveRecord::Base
has_many :ratings, :dependent => :destroy
end
class Rating < ActiveRecord::Base
belongs_to :article
validates_presence_of :article
validates_inclusion_of :value, :in => 1..10
end
Controllers
class RatingsController < ApplicationController
before_filter :set_article
def create
#rating = #article.ratings.new :value => params[:value]
if #rating.save
redirect_to article_ratings_path(#article), :notice => "Rating successful."
else
redirect_to article_ratings_path(#article), :notice => "Something went wrong."
end
end
def update
#rating = Rating.find(params[:id])
#rating.update_attribute :value, params[:value]
end
private
def set_article
#article = Article.find(parms[:article_id])
end
end
In an article view somewhere:
form_for [#article,#rating] do |f|
f.select("rating", "value", (1..10))
f.submit "Rate this Article"
end
Have a look at the Letsrate gem: https://github.com/muratguzel/letsrate
Works great for me.

In Rails, how do I use RESTful actions for a resource that is the join in a many to many relationship?

I have the following models:
class User < ActiveRecord::Base
has_many :subscriptions
end
class Subscription < ActiveRecord::Base
belongs_to :user
belongs_to :queue
end
class Queue < ActiveRecord::Base
has_many :subscriptions
end
I want to have some meta-data in the Subscription class and allow users to maintain the details of each of their subscriptions with each subscriptions meta-data. Queues produce messages, and these will be sent to users who have Subscriptions to the Queue.
As I see it the resource I want to have is a list of subscriptions, ie the user will fill in a form that has all the Queues they can subscribe to and set some metadata for each one. How can I create a RESTful Rails resource to achieve this? Have I designed my Subscription class wrong?
I presently have this in my routes.rb:
map.resources :users do |user|
user.resources :subscriptions
end
But this makes each subscription a resource and not the list of subscriptions a single resource.
Thanks.
This can be done quite easily using accepts_nested_attributes_for and fields_for:
First in the User model you do the following:
class User < ActiveRecord::Base
has_many :subscriptions
accepts_nested_attributes_for :subscriptions, :reject_if => proc { |attributes| attributes['queue_id'].to_i.zero? }
# if you hit scaling issues, optimized the following two methods
# at the moment this code is suffering from the N+1 problem
def subscription_for(queue)
subscriptions.find_or_initialize_by_queue_id queue.id
end
def subscribed_to?(queue)
subscriptions.find_by_queue_id queue.id
end
end
That will allow you to create and update child records using the subscriptions_attributes setter. For more details on the possibilities see accepts_nested_attributes_for
Now you need to set up the routes and controller to do the following:
map.resources :users do |user|
user.resource :subscriptions # notice the singular resource
end
class SubscriptionsController < ActionController::Base
def edit
#user = User.find params[:user_id]
end
def update
#user = User.find params[:user_id]
if #user.update_attributes(params[:user])
flash[:notice] = "updated subscriptions"
redirect_to account_path
else
render :action => "edit"
end
end
end
So far this is bog standard, the magic happens in the views and how you set up the params:
app/views/subscriptions/edit.html.erb
<% form_for #user, :url => user_subscription_path(#user), :method => :put do |f| %>
<% for queue in #queues %>
<% f.fields_for "subscriptions[]", #user.subscription_for(queue) do |sf| %>
<div>
<%= sf.check_box :queue_id, :value => queue.id, :checked => #user.subscribed_to?(queue) %>
<%= queue.name %>
<%= sf.text_field :random_other_data %>
</div>
<% end %>
<% end %>
<% end %>
I found this tutorial very useful, as I was trying to relate Users to Users via a Follows join table: http://railstutorial.org/chapters/following-users

Resources