So I'm using the excellent Ancestry gem But while the documentation seems very complete I don't understand how to pass the parameter of my element which I want to be the parent of my newly created element. Firstly, do I want to do it in the new or create action... allow me to explain. For example: (with some actions removed for brevity)
class PeopleController < ApplicationController
#...
def new
#person = Person.new
end
def create
#user = User.new(params[:user])
if #user.save
flash[:notice] = "Registration Successful."
redirect_to root_url
else
render :action => 'new'
end
end
end
So namely I don't know where to create the ancestry, the docs say:
...You can use the parent attribute to organise your records into a tree. If you have the id of the record you want to use as a parent and don’t want to fetch it, you can also use parent_id. Like any virtual model attributes, parent and parent_id can be set using parent= and parent_id= on a record or by including them in the hash passed to new, create, create!, update_attributes and update_attributes!. For example:
TreeNode.create! :name => 'Stinky', :parent => TreeNode.create!(:name => 'Squeeky')
I want to know what my controller show look like to allow me to set the parent of the #person when I create them.
So otherwise I'm stuck, I don't know what else to do here... but anyhow, I do know that this gem is similar to the more popular acts_as_tree, any help is super appreciated!
Updated
I think I almost have it but when I try this for my create action
def create
#parent = Recipe.find(params[:parent])
#recipe = Recipe.new(params[:recipe], :parent => #parent.id) do |recipe|
recipe.user_id = current_user.id
end
if #recipe.save
current_user.has_role!(:owner, #recipe)
redirect_to #recipe
else
render :action => 'new'
end
end
I get:
Couldn't find Recipe without an ID
Updated
My view has a link to the new action that looks like this <%= link_to "fork this recipe", {:controller => "recipes", :action => "new", :parent => #recipe} %>
That seems to look fine to me, also the url reads fine when you get to the form, recipes/new?parent=112, but I still get that error, there has to be a way for that parameter to be passed as the parent of the newly created object.
If it works like acts_as_tree then I'll assume that you can do something like this:
#parent.children.create(attributes)
Which will create a new child object with the parent set, regardless of what the attributes say.
According to the docs that you pasted you can do:
#...
#user = User.new(params[:user])
#user.parent_id = #parent_user.id
#user.save
#...
You can also include it in the params hash for the user -- your form submission would need to have params[:user][:parent_id]:
#user = User.create(params[:user])
Related
I am building a database and I am getting an issue with my create action. My code for the create action is:
def create
#skills = Skill.new(params[:skill])
if #skills.save
redirect_to :action => 'index'
else
#skills = Skill.find(:all)
render :action => 'new'
end
end
and my error message is this:
ActiveModel::ForbiddenAttributesError in SkillsController#create
ActiveModel::ForbiddenAttributesError
I assume there is a problem and I am not saving all the needed params but I am not sure. Thanks anyone who knows what might be going wrong and I will keep messing around with it myself. Thanks again.
In your controller add something like this:
private
def skill_params
params.require(:skill).permit(:attribute_1, :attribute_2, :attribute_3)
end
then change create to:
def create
#skills = Skill.new(skill_params)
if #skills.save
redirect_to :action => 'index'
else
#skills = Skill.find(:all)
render :action => 'new'
end
end
This is specific to Rails 4: this version of ruby on rails fordids direct usage of the params to instanciate a new Model object.
It makes your application more safe by protecting it against some vulnerabilities: for example, imagine if someone requests your application to create a new user and passes admin: true as parameter. The old way may create an admin user. On the contrary, the Rails 4 way force you to filter the parameters.
I have the following link_to in my index.html.erb. Whenever I click on the link, it will create a new record in retrieval_requests table but for some reason user_id and package_id was not saved.
I'm not sure on how to go about this. I would appreciate any help.
Thanks.
index.html.erb
.
.
<%= link_to "Retrieve this package", retrieval_requests_path(user_id: current_user.id, package_id: item.package.id), :method => :post %>
.
.
retrieval_requests_controller.rb
class RetrievalRequestsController < ApplicationController
def index
#items = Item.where(:user_id => current_user.id)
end
def create
#retrieval_request = RetrievalRequest.new(params[:retrieval_request])
if #retrieval_request.save
redirect_to retrieval_requests_path, notice: "Successfully created retrieval request."
else
render :new
end
end
end
Since you're using a link and not a form, you're going to have to create the record with the individual params.
So instead of using params[:retrieval_request] (which don't exist), use params[:user_id] and params[:package_id].
When you pass parameters to a link helper, such as:
retrieval_requests_path(user_id: 3, package_id: 26)
It will create a URL that looks something like the following, depending on how you set up your routes:
"/retrieval_requests/3/26"
# or with no user_id or package_id set in routes:
"/retrieval_requests?user_id=3&package_id=26"
Then in your controller you need to grab those params separately.
I have a form for creating a ticket, which needs an id of a project. This works but not when it comes to validation. If validation won't pass 'render :new' is executed and the project_id doesn't come with it.
I have tried 'redirect_to new_ticket_path(:project_id => params[:ticket][:project_id]) which renders the form again, but the error messages won't show up so it seems that I need to use 'render :new'.
How can I pass the project_id back to the form or reach project_id from the form without passing it?
def new
#ticket = Ticket.new
#id = params[:project_id]
#project = Project.find(#id)
end
def create
#ticket = Ticket.new(params[:ticket].merge(:user_id => current_user.id))
if #ticket.save
redirect_to #ticket
else
render :new <--- will render without the project_id
end
end
That will render just the view for 'new', but will not run the controller action. You'd need to set up your variables for the 'new' view in your 'create' action.
From http://guides.rubyonrails.org/layouts_and_rendering.html#using-render
Using render with :action is a frequent source of confusion for Rails
newcomers. The specified action is used to determine which view to
render, but Rails does not run any of the code for that action in the
controller. Any instance variables that you require in the view must
be set up in the current action before calling render.
The easiest way around this is to change 'new':
def new
#ticket = Ticket.new(:project_id => params[:project_id])
end
and change any references to #project in your 'new' form to #ticket.project. At that point, you shouldn't have to add anything to your 'create' action as long as your form includes a hidden field for the ticket's project id.
The easiest way to get this working (and I would do this anyway) is to nest the task resource under projects. That way you will always have project_id available in params.
# config/routes.rb
resources :projects do
resources :tasks
end
The urls will look like projects/123/tasks/new etc. Take a look at rake routes.
Write project id into a hidden field in your form and you will okay. And don't forget to initialize #id in your create action
def new
#ticket = Ticket.new
#id = params[:project_id]
#project = Project.find(#id)
end
def create
#ticket = Ticket.new(params[:ticket].merge(:user_id => current_user.id))
#id = params[:project_id] # but make sure it is under this key in params
if #ticket.save
redirect_to #ticket
else
render :new <--- will render without the project_id
end
end
and in the form add
<%= hidden_field :project_id, '', value: #id %>
Why don't you use:
flash[:alert] = #ticket.errors.inspect
redirect_to new_ticket_path(:project_id => params[:ticket][:project_id])
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 most of the examples I see, I notice that in the controller, the new method simply has:
#object = Object.new
In the create method, you'll see something like:
#object = Object.find(params[:object])
if #object.save
flash[:success] = "This object has been added."
redirect_to objects_path
else
render :action => 'new'
end
This works fine, but the only thing I run into (pretty common), is when the validations fail using some built in helper methods:
validates_presence_of :value
the "render :action => 'new' will be called. So then rails bypasses the controller and goes straight to the action for new, which attempts to render the form.
That bypass of the controller kills me, because sometimes I'm loading values into the form using values I've defined in my controller.
I'll end up getting errors because of nil values, because rails bypassed the controller and the values were never set when loading "render :action => 'new'".
My question: What's the best way to redirect a form (or best practice in general) when validating a form that has variables assigned in it that is defined the in controller? I want to avoid those nil value errors, so I'm thinking I'm just doing it wrong to begin with.
You could move your custom bit of code that loads your various values into a before_filter, a bit like:
before_filter :get_values, :only => [:new, :create]
def new
# your code here
end
def create
#object = Object.new params[:object
if #object.save
flash[:success] = "This object has been added."
redirect_to objects_path
else
render :action => 'new'
end
end
private
def get_values
# your code here
end
One way: render hidden fields for all those variables, so your object will already have them in create action.
Another way: create a before_filter for those two actions.
Personally I'd go with the second option.