How to get values in show action in rails controller - ruby-on-rails

I would like to have access to the parameters in nested attributes..... Below is the code.
<%= link_to "Invoice", user_invoice_path(#user, invoice) %>
How do I access the user and invoice in the Invoice controller show action.
def show
#user = User.find(params[:user_id])
#invoice = Invoice.find(params[:id])
end
User Model:
class User < ActiveRecord::Base
has_many :invoices
end
Invoice Model:
class Invoice < ActiveRecord::Base
belongs_to :user
end
I know how it works when it is not nested.... Can anyone, please help?

If I understand correctly, you can't find the #user or #invoice in this way.
Please debug your show action and you will find params as follows,
{"action"=>"show", "controller"=>"invoices", "user_id"=>"307", "id"=>"359"}
So now write your show action something like,
def show
#invoice = Invoice.find(params[:id])
#user = User.find(params[:user_id])
end
There may be better ways to find the objects in the controller action. But this is the basic approach you will have to consider instead of what you tried.

Related

Nested resources, simple_form_for

I am trying to create an app where I have Events and each event would have many sales. When a new sale is created it automatically gets an event ID it belongs to. Could somebody please review this and tell me if I am doing something wrong, because I think the way am creating simple_form for the nested model(Sale) is a bit incorrect. Also I am not sure if it should be this way or I ve done something wrong, but when I am accessing nested children the url looks like this
.../events/4/sales/1
.../events/3/sales/1
.../events/5/sales/1
but I would expect it to be like this ?!
.../events/4/sales/1
.../events/4/sales/2
.../events/4/sales/3
Here is my controller and model for Events
class Event < ApplicationRecord
has_many :sales, dependent: :destroy
end
.
class EventsController < ApplicationController
def index
#events = Event.all
end
def new
#event = Event.new
end
def create
#event = Event.new(event_params)
if #event.save
redirect_to #event
else
redirect_to events_path
end
end
def show
#event = Event.find(params[:id])
#sales = #event.sales
end
private
def event_params
params.require(:event).permit(:name, :comment, :event_disscount)
end
end
.
Here is my controller and model for Sales
class Sale < ApplicationRecord
belongs_to :event
has_many :sale_items
accepts_nested_attributes_for :sale_items, allow_destroy: true
end
.
class SalesController < ApplicationController
def new
#sale = Sale.new(event_id: params[:event_id])
#event = Event.find_by(id: params[:event_id])
end
def create
#event = Event.find(params[:event_id])
#sale = #event.sales.create(params[:sale].permit(:receipt_email))
if #sale.save
redirect_to #event
else
redirect_to new
end
end
end
routes.rb
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
resources :events do
resources :sales
end
root 'events#index'
end
And this is how I use simple_form for sale(new)
<%= simple_form_for([#event, #sale]) do |f| %>
My main concern is the 'new' action in Sales controller, whats the best way to create a nested resource with the id of its parent, and then passing this object to the simple_form?!
Thank you in advance
Your question is too broad. Basically you're doing it all right, however, with some improvements on the code it will be easier to find possible problems.
is it correct the way I am creating new sale?
Some improvements over your SalesController:
Create private method sale_params which will sanitize input params from your form. You did it for events already - why not to do it here too?
Since that controller works in the scope of event, params[:event_id] is set for every action. So create a before_action filter which will set your #event variable.
Method create saves the model to the database, so calling save after it makes no sense.
In case of failure on saving #sale to the db redirecting to new is not reasonable. In that case everything user typed in the form will be lost, validation error won't be shown and it will look like a glitch of your app. Render new template instead with the same #sale.
This is how I would rewrite your controller:
class SalesController < ApplicationController
before_action: :set_event
def new
#sale = #event.sales.build
end
def create
#sale = #event.sales.build(sale_params)
if #sale.save
redirect_to #event
else
render action: :new
end
end
private
def sale_params
params.require(:sale).permit(:receipt_email, sale_items_attributes: [])
end
def set_event
#event = Event.find(params[:event_id])
end
end

How to add current_user to user_id to form_for in rails?

I have a form for creating materials (title, description and content - all basic). The form saves these details just fine but it doesn't save the user_id, which should be the user_id of the current_user. How do I do this? It must be easy but nothing has worked so far.
def create
#material = Material.new(params[:material])
if #material.save
flash[:success] = "Content Successfully Created"
redirect_to #material
else
render 'new'
end
end
def create
#material = Material.new(params[:material])
#material.user_id = current_user.id if current_user
if #material.save
flash[:success] = "Content Successfully Created"
redirect_to #material
else
render 'new'
end
end
There are a few different ways to do it depending on how you have your application setup. If there is a relationship between the user and materials (User has many materials), you could use that in your controller:
def create
#material = current_user.materials.new(params[:material])
# ...
end
If you don't have that relationship, I would still recommend setting it in the controller as opposed to a hidden field in the form. This will be more secure because it won't let someone tamper with the user id value:
def create
#material = Material.new(params[:material].merge(user_id: current_user))
# ...
end
Assuming you are saving the login users's object in the current_user following will work for you
#material = Material.new(params[:material])
#material.user_id = current_user.id
if #material.save
With Rails 5 and parameters needing to be permitted before objects are created, this is the simplest way to merge the current_user into the params, kudos to #Peter Brown in his answer:
def create
#discussion = current_user.materials.new(new_material_params)
# ...
end
private
def new_material_params
params.require(:material).permit(:title, :description,: content)
end
If you have nested object creation using accepts_nested_attributes_for, you need to manually merge deep into the association parameters:
class User < ApplicationRecord
has_many :discussions # Used to associate User with Discussion later
end
class Comment < ApplicationRecord
belongs_to :user
end
class Discussion < ApplicationRecord
belongs_to :user
has_many :comments
accepts_nested_attributes_for :comments
end
class DiscussionsController < ApplicationController
def create
# Merge the params[:discussion][:user_id] by using the relationship's #new
#discussion = current_user.discussion.new(new_discussion_params)
end
private
# Sanitized params for creation, not editing
def new_discussion_params
params.require(:discussion)
.permit(:title, :user_id,
comments_attributes: [:id, :content, :discussion_id, :user_id])
.tap do |discussion_params|
# Require the association parameters, and if they exist,
# set :user_id for each.
discussion_params.require(:comments_attributes).each do |i, comment|
comment.merge!(user_id: current_user.id)
end
end
end
end
Heads up: Setting (or overwriting!) what will be params[:discussion][:comments_attributes]["0"][:user_id] works fine for creation. But if you allow editing deep hierarchies in addition to creation, make sure you don't accidentally overwrite all the :user_ids with the current user.

Create Child only and select a Parent in nested form?

Instead of creating a new Parent and also creating the children. Is it possible to select from a list of Parents and then only create the children that are assigned to a current user and that specific Survey?
Lets use this example:
class Survey < ActiveRecord::Base
has_many :questions
accepts_nested_attributes_for :questions
end
class Question < ActiveRecord::Base
belongs_to :survey
belongs_to :user
end
And then in the controller:
def new
# #survey = select menu of all Surveys
3.times do
question = #survey.questions.build
end
end
def create
# Saves new questions with current user
if #survey.save
flash[:notice] = "Success"
redirect_to #survey
else
render :action => 'new'
end
end
I'm not sure what the create and new actions would turn into. Any idea?
You can call the edit action on a existing survey passing the selected survey to it:
edit_survey_path(#survey)
Then you can load the selected survey in that action:
def edit
#survey = Survey.find(params[:id])
end
In the edit view, use a nested form to add/delete questions, and then, in the update action, updating your surveys attributes will also add and delete the questions.
def update
#survey = Survey.find(params[:id])
#survey.update_attributes(params[:survey])
redirect_to ...
end
All of this will work assuming you've set accepts_nested_attributes_for :questions in the survey model.
My answer here is a summary of Ryan Bates' screencast on nested forms which I think you've already seen, based on the similarity of your example and his.
What I'd like to point out here is that you can achieve what you want using exactly the same code, however using the edit/update actions on your parent model instead of new/create on the child model.
Edit:
In order to assign the current user to a survey question, do the explicit assignment in the new and edit action:
def new
#survey = Survey.new
3.times do
question = #survey.questions.build(:user_id => current_user.id)
end
end
def edit
# find the preselected Survey...
#survey = Survey.find(params[:id])
# This adds a (one) new empty question, consider doing it via Javascript
# for adding multiple questions.
#survey.questions.build(:user_id => current_user.id)
end
In your form for questions, add:
<%= form_builder.hidden_field :user_id %>
Don't forget to replace form_builder with your actual form builder object.
Now all the new questions will be assigned to the current user because the current user was submitted by the form along with the other attributes for questions.

Is creating a new model by passing it params secure?

I have a comment model. I am creating a new instance of that model by passing it params from my view to the comment controller. Here is the comment controller:
class CommentsController < ApplicationController
def create
session[:return_to] = request.referrer
#comment = Comment.create(:user_id => current_user.id,
:issue_id => params[:issue_id],
:content => params[:content])
redirect_to session[:return_to]
end
end
Here is how I am passing the params in my view:
<%= link_to "Test Comment", comments_path(:issue_id => #issue.id,
:content => "HeLLO"),
method: :create %>
my question is - is this secure? What prevents someone from changing the params[:issue_id] and commenting on another issue? Is there a better way of doing this?
yeah, there are better ways
at first we look to your controller. to store the referrer and redirect back to it makes no sense (at least you should NOT save this in a session) rails can do this with the key :back.
at second you dont need to make a varaible with the # because you dont use the created object. and also you dont need to save the restult. just do
class CommentsController < ApplicationController
def create
Comment.create(:user=>current_user, :issue_id=>params[:issue_id],:content=> params[:content])
redirect_to :back
end
end
++ edit
actually a better way would to to it like this:
class CommentsController < ApplicationController
def create
current_user.comments.create(issue_id: params[:issue_id], content: params[:content])
redirect_to :back
end
end
just use rails associations
-- edit
and as you think, YES we can change the issue_id and write comments to any issue i want. so if you want to protect from this you have do do a helper before you crate a comment (its just an example)
class CommentsController < ApplicationController
def create
issue = Issue.find(params[:issue_id]
if issue.is_locked? || current_user.cant_write_at_issue(issue)
return redirect_to :back, :notice=>"You dont have Privilegs"
end
issue.comments.create :user=>current_user, :content=>params[:content])
redirect_to :back :notice=>"Comment was created successfully"
end
end
is_locked and cant_write_at_issue you need to define in your models. this is just a way how to protect something.
so now we can change the issue ID but you look if the user has access for doing this :-)

finding a User object in ruby on rails

Ruby on rails noob here.
User fields relevant to this question: id (prim. key). Post fields relevant to this question: id, user_id (fk). The foreign key is user_id of Posts which is connected to id of Users. Is the following the right syntax? I want to grab the User object who posted the current post:
(this is in controllers/posts_controller.rb)
#id = Post.find(params[:id]).user_id
#user = User.find(#id)
You can see the context of the code below:
def sendMessage
##user = current_user
#id = Post.find(params[:id]).user_id
#user = User.find(#id)
UserMailer.welcome_email(#user).deliver
respond_to do |format|
format.html { render :nothing => true, :status => :ok }
end
end
The reason I'm asking is because an email is not getting sent to the user who created the post. Now, I know that this isn't an email issue because, as a test, I tried commenting out the two lines in question and simply using:
#user = current_user
...and this sends an email to the user who's logged in (but this isn't what I want).
Thanks in advance,
Matt
You should have your models set up as follows:
class Post < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_many :posts
end
Then in your controller you can do:
#post = Post.find(params[:id])
#user = #post.user
You should set the proper relations in the model.
I guess 'has_many :posts' in User and 'belongs_to :user' in Post.
Then you'll be able to do:
#user = Post.find(params[:id]).user

Resources