I have a problem with passing params. in show view i have
=link_to "Send message", {:controller => "users", :action =>
"send_message", :user_id => #user.id}
and my users_controller have method
def send_message
#user = User.find(params[:user_id])
#message=Message.new
redirect_to send_message_path
end
and my show method
def show
#user = User.find(params[:id])
#post=Post.new
#posts=#user.posts.reverse.paginate(:page => params[:page],:per_page => 10)
end
but i have and error after clicking on link
Couldn't find User without an ID
in that line, in send_message method
#user = User.find(params[:user_id])
what i am doing wrong? i read a lot examples about this , but it doesnt work(
Thanks in advance!!!
=link_to "Send message", [:send_message, #user]
and in your routes:
resources :users do
post :send_message, :on => :collection
end
What happens if you try to change :user_id to :id, like this
=link_to "Send message", {:controller => "users", :action => "send_message", :id => #user.id}
def send_message
#user = User.find(params[:id])
#message=Message.new
redirect_to send_message_path
end
If that works it's because your route isn't set up to use a :user_id param. If it doesn't it would help to know more about your route.
In your routes you should write:
resources :users do
post :send_message, :on => :member
end
And in your view you write:
= link_to "Send Message", send_message_user_path(#user)
This will use id instead of user_id.
So in your controller you need to use params[:id] as well.
Imho this is cleaner than #fl00r solution, because it is a member method, not a collection.
But I am a bit confused as to why you would have that method inside the controller at all.
Because the only thing it does is redirect to the send_message_path anyway. The instance variables you set are all lost then. So instead in your view write this:
= link_to 'Send message', send_message_path(:user_id => #user.id)
And this will send you to the correct controller straightaway (where you will have to retrieve the user).
Also your show method could use some work: you should define a has_many :posts in your User model.
Related
I'm trying to insert the input from a select_tag to my controller method.
I've looked and cannot seem to resolve the issue.
This is the code I have below, nothing for the rank's selection comes up in the params at all.
<h1>hello please set <%= #user.username %>'s rank'</h1>
<%= select_tag 'rank', options_for_select(#ranks.collect{ |r| [r.rank_name] }) %>
<%= button_to "Update", :action => "set_user_rank_update", value: "#{#user.id}", method: :post %>
Update below with the controller and routes
Controller:
class Admin::RankController < ApplicationController
before_action :admin?
def new
#rank = Rank.new
end
def create
#rank = Rank.new(rank_params)
if params["rank"]["admin"].to_i == 1
#rank.toggle! :admin?
end
if #rank.save
flash[:success] = "Rank created"
redirect_to root_path
else
flash[:danger] = "Failed to create rank"
render 'new'
end
end
def set_user_rank_new
#user = User.find_by_id(params["format"])
#ranks = Rank.all
end
def set_user_rank_update
#user = User.find_by_id(params["value"])
#rank = Rank.find_by_id(params["rank"])
#rank_backup = #user.rank.first
debugger
#user.rank - #user.rank.first
#user.rank << #rank
if #user.rank.first == #rank
flash[:success] = "Set user's rank"
redirect_to root_path
else
flash[:danger] = "Failed to set user's rank"
#user.rank - #user.rank.first
#user.rank << #rank_backup
render 'set_user_rank_new'
end
end
private
def rank_params
params.require(:rank).permit(:rank_name, :rank_color)
end
end
Routes
Rails.application.routes.draw do
devise_for :users,
:controllers => { :registrations => "member/registrations" , :sessions => "member/sessions"}
scope module: 'public' do
root 'welcome#index'
end
scope module: 'member' do
get 'members/:id' => 'member#show'
end
scope module: 'admin' do
get 'rank/new' => 'rank#new'
post 'rank/create' => 'rank#create'
get 'rank/set_user_rank/new' => 'rank#set_user_rank_new'
post 'rank/set_user_rank/update' => 'rank#set_user_rank_update'
end
end
Try passing 2 element arrays to options_for_select. The way you have it looks like it would get you option text but no values, which could explain why it doesn't show up in the params.
So for example:
<%= select_tag 'rank', options_for_select(#ranks.collect{ |r|[r.rank_name, r.id] }) %>
The button_to helper creates an inline form with just the parameters included in the helper statement (in this case the user id).
You can check this by examining the resulting HTML on your page - which I think will show an input field for rank sitting outside the form tag.
To include the rank parameter, you should set up the form using a form helper and make sure the rank input is included inside the form.
For what purpose do you need it in your controller action?
With the link_to you can forwards params as well.
It would be very helpful to see your controller and also your routes.
I am implementing a like system in my rails app using David Celis gem called Recommendable. I've gotten everything to work in the console but I can't get the right routes and I'm getting the "No route matches [GET] "/categories/1/posts/1/like" error.
I have the following in my models:
class Category < ActiveRecord::Base
has_many :posts, :dependent => :destroy
extend FriendlyId
friendly_id :name, use: :slugged
end
class Post < ActiveRecord::Base
belongs_to :category
end
In my Post Controller I have:
class PostsController < ApplicationController
before_filter :authenticate_user!
before_filter :get_category
def like
#post = Post.find(params[:id])
respond_to do |format|
if current_user.like #post
else
flash[:error] = "Something went wrong! Please try again."
redirect_to show_post_path(#category, #post)
end
end
end
end
In my routes I have:
resources :categories do
resources :posts do
put :like, :on => :member
end
end
match 'categories/:category_id/posts/:id', :to => 'posts#show', :as => 'show_post'
Can someone please point at my errors? I can get the PUT to work but I dont know where the GET error is coming from as I am trying to redirect back to the post if an error occurs when the user like's a certain post. Thank you in advance.
EDIT:
In my view I have:
- title "#{#post.class}"
%p#notice= notice
%p
%b Title:
= #post.title
%p
%b Description:
= #post.description
%p
%b Likes:
= #post.liked_by.count
= link_to 'Edit', edit_category_post_path(#post)
\|
= link_to 'Back', category_posts_path
\|
= link_to 'Like', like_category_post_path(#post)
Replace:
= link_to 'Like', like_category_post_path(#post)
with:
= link_to 'Like', like_category_post_path(#category, #post), method: :put
Or, as I like it:
= link_to 'Like', [#category, #post], method: :put
I think your like has to be:
def like
#post = Post.find(params[:id])
respond_to do |format|
format.html do
if current_user.like #post
flash[:notice] = "It's ok, you liked it!"
redirect_to :back
else
flash[:error] = "Something went wrong! Please try again."
redirect_to show_post_path(#category, #post)
end
end
end
end
Your route expects a PUT request, while you're issuing a GET request.
You'll need to either access your route via a button_to with :method => :put so that your app is issuing PUT requests (the correct solution), or change your route to use GET requests (the wrong way to make requests which modify state):
get :like, :on => :member
How can you pass an error messages coming from a model --> controller to view?
= form_tag :controller => "article", :action => "create" do
/ how to retrieve error messages here?
%p
= label_tag :article, "Article"
= text_field_tag :article
= submit_tag "Submit Article"
I have this model:
class Article < ActiveRecord::Base
attr_accessible :article
validates :article, :presence => true
end
In my controller:
def create
#article = Article.new(params[:article])
if ! #article.save
# how to set errors messages?
end
end
I'm using Rails 3.0.9
The errors messages are stored in your model. You can access through the errors methods, like you can see in http://api.rubyonrails.org/classes/ActiveModel/Errors.html.
An easy way to expose the error message is including the follow line in your view:
%span= #article.errors[:article].first
But, I belive you have to change your controller to be like that:
def new
#article = Artile.new
end
def create
#article = Artile.new params[:article]
if !#article.save
render :action => :new
end
end
In the new action you don't need to try save the article, because the creation action already do that job. The new action exists, (basically) to call the new view and to provide support for validations messages.
The newmethod shouldn't save anything. create method should.
def create
#article = Article.new(params[:article])
if ! #article.save
redirect_to root_path, :error => "ops! something went wrong.."
end
end
I've a simple posts model with title:string and rating:integer and I want to add the ability to rate the posts. So far I have
#Post controller
def increase
#post = Post.find(params[:id])
#post.increment! :rating
flash[:notice] = "Thanks for your rating."
redirect_to #post
end
#Post show
<%= link_to "Rating", increase_post_path %>
#Routes
map.resources :posts, :member => { :increase => :put }
When I click on rating, I get unknown action. I'm able to increase the rating when I add #post.increment! :rating to update, but not when I create my own method. Any suggetions?
If you have a standard routes saying
map.resources :posts
you should replace it with:
map.resources :posts, :member => { :increase => :put }
And this will create the route "increase_post_path" with method "put" for your app.
You need to add the action to your routes.rb file. See this excellent guide for details.
Here's my solution, if anyone is interested. Thanks for your help guys.
#Views
<%= link_to post.rating, increase_post_path(post) %>
#Controller
def increase
#post = Post.find(params[:id]).increment!(:rating)
flash[:notice] = "Thanks for rating"
redirect_to posts_url
end
#Routes
map.resources :posts, :member => { :increase => :put }
This works if you want something just for you, something that won't be abused. Obviously adding rating to your websites where others can vote up unlimited times is just asking for trouble.
I suggest using Votefu, does ratings and even has user karma. The author was nice enough to make an example app.
I've just added a contact form to my Rails application so that site visitors can send me a message. The application has a Message resource and I've defined this custom route to make the URL nicer and more obvious:
map.contact '/contact', :controller => 'messages', :action => 'new'
How can I keep the URL as /contact when the model fails validation? At the moment the URL changes to /messages upon validation failure.
This is the create method in my messages_controller:
def create
#message = Message.new(params[:message])
if #message.save
flash[:notice] = 'Thanks for your message etc...'
redirect_to contact_path
else
render 'new', :layout => 'contact'
end
end
Thanks in advance.
One solution would be to make two conditional routes with the following code:
map.contact 'contact', :controller => 'messages', :action => 'new', :conditions => { :method => :get }
map.connect 'contact', :controller => 'messages', :action => 'create', :conditions => { :method => :post } # Notice we are using 'connect' here, not 'contact'! See bottom of answer for explanation
This will make all get request (direct requests etc.) use the 'new' action, and the post request the 'create' action. (There are two other types of requests: put and delete, but these are irrelevant here.)
Now, in the form where you are creating the message object change
<%= form_for #message do |f| %>
to
<%= form_for #message, :url => contact_url do |f| %>
(The form helper will automatically choose the post request type, because that is default when creating new objects.)
Should solve your troubles.
(This also won't cause the addressbar to flicker the other address. It never uses another address.)
.
Explanation why using connect is not a problem here
The map.name_of_route references JUST THE PATH. Therefore you don't need a new named route for the second route. You can use the original one, because the paths are the same. All the other options are used only when a new request reaches rails and it needs to know where to send it.
.
EDIT
If you think the extra routes make a bit of a mess (especially when you use it more often) you could create a special method to create them. This method isn't very beautiful (terrible variable names), but it should do the job.
def map.connect_different_actions_to_same_path(path, controller, request_types_with_actions) # Should really change the name...
first = true # There first route should be a named route
request_types_with_actions.each do |request, action|
route_name = first ? path : 'connect'
eval("map.#{route_name} '#{path}', :controller => '#{controller}', :action => '#{action}', :conditions => { :method => :#{request.to_s} }")
first = false
end
end
And then use it like this
map.connect_different_actions_to_same_path('contact', 'messages', {:get => 'new', :post => 'create'})
I prefer the original method though...
I just came up with a second solution, guided by Omar's comments on my first one.
If you write this as your resources route
map.resources :messages, :as => 'contact'
This gives (amongst others) the following routes
/contact # + GET = controller:messages action:index
/contact # + POST = controller:messages action:create
So when you move your 'new' action code into your 'index' action, you will have the same result. No flicker and an easier to read routes file. However, your controller will make no more sense.
I, however, think it is a worse solution because you'll soon forget why you put your 'new' code into the index action.
Btw. If you want to keep a kind of index action, you could do this
map.resources :messages, :as => 'contact', :collection => { :manage => :get }
This will give you the following route
manage_messages_path # = /contact/manage controller:messages action:manage
You could then move your index action code into the manage action.
I suspect you are posting to '/messages' from the form which creates the message which explains why you see that in your URL.
Any reason why this won't work:
def create
#message = Message.new(params[:message])
if #message.save
flash[:notice] = 'Thanks for your message etc...'
redirect_to contact_path
else
flash[:notice] = 'Sorry there was a problem with your message'
redirect_to contact_path
end
end
Not to my knowledge, no. Since im assuming you want to render so that you keep the #message object as is with the errors attached.
There is a horrible solution that I have which will let you do it, but, its so horrible, I wouldn't recommend it:
before_filter :find_message_in_session, :only => [:new]
def new
#message ||= Message.new
end
def create
#message = Message.new(params[:message])
if #message.save
flash[:notice] = 'Thanks for your message etc...'
redirect_to contact_path
else
flash[:notice] = 'Sorry there was a problem with your message'
store_message_in_session
redirect_to contact_path
end
end
private
def find_message_in_session
#message = session[:message]; session[:message] = nil
end
def store_message_in_session
session[:message] = #message
end