I have the following relationship:
store.rb -> has_many :products
product.rb -> belongs_to :store
routes.rb
resources :stores do
resources :products
end
builds_controller.rb
def edit
#build = Build.find(params[:id])
#user = User.find(#build.user_id)
#hero = Hero.find(#build.hero_id)
#heros = Hero.order('name ASC')
#items = Item.order('name ASC')
unless current_user.id == #user.id
respond_to do |format|
format.html { redirect_to root_path, notice: 'You are only allowed to edit your own builds' }
end
end
end
For some reason, whenever I try go to the edit page for a build and try to edit it, it runs the create action instead of update.
Anyone know what might be the cause of this?
Also, I'd like the form on the edit page to be filled out with the current data of the build. How do I achieve this?
My repo: https://github.com/imjp/DotA-Items
Your problem comes from your form:
<%= semantic_form_for([current_user, current_user.builds.build]) do |f| %>
It needs to be
<%= semantic_form_for([#user, #build]) do |f| %>
Now, in your new action, you also need to prepare the required variables:
#user = User.find(params[:user_id]) #assuming you have a path like users/id/builds/new
# or #user = current_user if that's what you want
#build = #user.builds.build
ps: you should use the cancan gem to manage authorizations, instead of doing stuff like if #user.id == current_user.id etc.
Related
Why does this link_to method redirect to my root path?
Users controller:
def show
#user = User.find(params[:id])
#user.comments.build
#comment = Comment.new
#user.votes.build
#vote = Vote.new
end
def vote:
#comment = Comment.find(params[:id])
Vote.create!(voteable_id: params[:id], voteable_type: 'Comments')
#user = User.find(params[:id])
redirect_to #user
end
Users view:
<%= link_to "vote", vote_comment_path(:id => comment.id), method: :post, :class=> "btn %>
Routes.rb:
post 'comments/:id/vote' => 'comments#vote', as: 'vote_comment'
resources :users do
resources :comments
resources :ratings
resources :votes
end
I was expecting clicking on vote to redirect back to the user, but instead it goes to my home page.
Can I not use params[:id] twice under the same method?
When you call
vote_comment_path(:id => comment.id)
it passes comment.id into the method, which is retrieved as params[:id].
If the value of params[:id] is comment.id, then:
def vote
# ...
#user = User.find(params[:id]) # look up user by comment.id - BAD!
redirect_to #user # wrong user!
end
You are looking up a user by a comment ID, not a user ID.
If the current user is User 1 but the comment is Comment 5, you are being redirected to User 5, the user with the same ID as the comment.
I'm guessing there is some authorization check dumping you back to root (eg. not allowed to view User 5 while logged in as User 1). Check your logs to see where the redirect took place.
This might be related, but the proper way to build is like this:
def show
#user = User.find(params[:id])
#comment = #user.comments.build
#vote = #user.votes.build
end
I have a job and user(devise) form in the same view. When I am trying to submit with errors in the user fields it gives me an exception page with the validation messages. Submitting errors in the job fields works fine!
job_controller.rb
def new
#job = Job.new
if !current_user
#job.user = User.new
end
respond_to do |format|
format.html # new.html.erb
end
end
def create
#types = Type.all
#categories = Category.all
#job = Job.new(params[:job])
#if not logged in creates a user and sign in
if !current_user
#user = User.new(params[:job][:user_attributes])
else
#user = current_user
end
#job.user_id = #user.id
respond_to do |format|
if #job.save
if !current_user
sign_in(:user, #user)
end
format.html { redirect_to #job }
else
format.html { render action: "new" }
end
end
end
job.rb
attr_accessible :user_attributes, :description, :name ....
belongs_to :user
accepts_nested_attributes_for :user
Thanks!
That becuase you are calling, #user.save! which will generate an exception. Also doing it this way won't put the job in the same transaction as User. What you want are nested_attributes:
class Job < ActiveRecord::Base
accepts_nested_attributes_for :user
end
If the user is logged in, don't show that part of the form and filter those params.
See more in the Rails documentation here http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
EDIT:
Simplify your controller code, since you're using nested attributes you no longer need to manually create a user.
#if not logged in creates a user and sign in
if !current_user
#user = User.new(params[:job][:user_attributes]) # this is no longer needed
else
#user = current_user
end
#job.user_id = #user.id # this is redundant
To something more like:
# if logged in, manually assign the user (also you may want to reject any user attributes)
#job.user = current_user if current_user
I have Project and ProjectSetting models with following associations:
class Project < ActiveRecord::Base
has_one :project_setting
end
class ProjectSetting < ActiveRecord::Base
belongs_to :project
end
In projects_controller I have:
def show
#project = Project.find(params[:id])
#project_setting = #project.project_setting
end
So I'm using #project_setting form in #project show page and I need to update #project_setting from this page.
In project_settings_controller I have:
def update
#project = Project.find(params[:id]) #problem is here
#project_setting = #project.project_setting
if #project_setting.update_attributes(params[:project_setting])
respond_to do |format|
format.html { redirect_to project_path(#project) }
format.js
end
end
end
But #project variables in these controllers aren't the same:
In projects_controller#show it is Project with ID 26 and in project_settings_controller#update it finds Project with ID 1
So I need to pass #project variable from projects_controller#show to project_settings_controller#update.
Thanks for any help!
In your show.html.erb you can pass the variables back to any controller. For example
<%= link_to "Update project setting",
:controller => "project_settings",
:action => "update",
:project => #project %>
will send the parameter "project" filled with the #project variable.
If you are in a form tag, you can send the variable with a hidden field tag:
<% hidden_field_tag("project", #project) %>
I hope, this helps.
params[:id] in project_settings_controller contained #project_setting.id
If you want to get #project.id from params, you should to write in routes.rb nested path:
resources :projects do
resources :project_settings
end
And then project.id is available in params[:project_id].
Example in rails_guides
In my Ruby on Rails application, each group has_many :expenses. I have nested my routes, so expenses are entered only as child entities of their parent groups. Here's an excerpt from routes.rb.
resources :groups do
resources :expenses
end
I cannot figure out how to render the 'new' action in the case of an expense not saving when it is submitted through /groups/:group_id/expenses/new. In my expenses_controller.rb, here is how the create action is defined:
def create
#expense = Expense.new(params[:expense])
#expense.group_id = params[:group_id]
if #expense.save
redirect_to group_expense_path(#expense.group.id, #expense.id)
else
render 'new'
end
end
Everything works fine if I satisty expense validation and #expense.save winds up working. However, when it fails and the code tries to render 'new' I get:
undefined method `expenses_path' for #<#<Class:0x007fd408b1fd58>:0x007fd408f21ca8>
So, I am assuming I have something about my nested routing wrong. How do I return the user to the new form but still display to him/her through the flash[] params the errors with the data they originally attempted to submit?
The problem is that #group is not initialized
So in your controller just do
#expense = Expense.new(params[:expense])
#group = Group.find(params[:group_id])
#expense.group_id = #group.id
Looks like you need to explicitly specify the url for form_for in your view.
Something likeā¦
<%= form_for #expense, :url => group_expenses_path(#group.id) do |f| %>
...
<% end %>
In your <%= form_for %> you have used #group for url, because expenses belongs_to groups. But inside your create action in the controller you have not defined what is #group, so first you should define it as:
#expense = Expense.new(params[:expense])
#group = Group.find(params[:group_id])
#expense.group_id = #group.id
Also I would suggest to use respond_to in your controller:
respond_to do |format|
if #expense.save
format.html { redirect_to group_expense_path(#group.id, #expense.id), :notice => "Any msg you want" }
else
format.html { render :action => "new" }
end
end
All of these are in your create action inside the controller.
Also for different rendering methods look up: http://guides.rubyonrails.org/layouts_and_rendering.html
Hope this helps!
In routes.rb:
resources :cars do
resources :reviews
end
resources :motorcycles do
resources :reviews
end
In ReviewsController:
before_filter :find_parent
def show
#review = Review.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #review }
end
end
def edit
#review = Review.find(params[:id])
end
# ...
def find_parent
#parent = nil
if params[:car_id]
#parent = Car.find(params[:car_id])
elsif params[:motorcycle_id]
#parent = Motorcycle.find(params[:motorcycle_id])
end
end
Generating the "show" link for a Review is simply (this works):
= link_to "Show", [#parent, #review]
Similarly I would like to reference a generic edit path for a Review, something like (this does not work):
= link_to "Edit", [#parent, #review], :action => 'edit'
Does anyone know if this is possible or, if not, how this might be accomplished?
link_to 'Edit Review', [:edit, #parent, #review]
It turns out the answer I am looking for can be found with the URL helper "edit_polymorphic_path" (see: http://rubydoc.info/github/rails/rails/master/ActionDispatch/Routing/PolymorphicRoutes). In order to get the link I am attempting above I was able to accomplish this with:
edit_polymorphic_path([#parent, #review])
I think what you need here is a polymorphic assocation. Ryan Bates at Railscasts.com explains it perfectly.
http://railscasts.com/episodes/154-polymorphic-association
It will make it easy for you to have things like:
User, Manager, Note
A user can have many notes
A manager can have many notes
A note can belong to a user OR a manager
users/1/notes/edit
managers/1/notes/edit
The Railscast will explain how to do it :)
EDIT:
def edit
#reviewable= find_reviewable
#reviews= #reviewable.reviews
end
private
def find_reviewable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
Then in your link to, it would be something like:
link_to 'Edit Review', edit_review_path([#reviewable, :reviews])
^^ Not tested.