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.
Related
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])
I currently have a create action in my sales controller that looks like this:
def create
#sale = Sale.new(params[:sale].except(:vehicles_attributes))
if #sale.save
redirect_to #sale, :notice => "Successfully created sale."
else
render :action => 'new'
end
end
The intention is to exclude a couple of attributes that are used only to populate linked selects, and should not be submitted (there are no columns for them).
With the controller code above, I am finding that the parameters still includes "sale"=>{"vehicles_attributes"=>{"0"=>{"make"=>"","model"=>""}}} so it seems that I have missed something in the controller code.
EDIT: After some more digging around, I have found that the mass_assignment exception is firing before my except code gets a chance to remove the params that shouldn't be sent by the form, so I am back to square one.
How can I ensure that I remove the fields that shouldn't be sent by the form before I get the mass_assignment error?
As far as I know the mass_assignment error should occur during the new call, so your way should work. Although I never used the except method. Have you tried using the reject! method?
def create
params[:sale].reject! { |k, v| k == :vehicles_attributes }
#sale = Sale.new(params[:sale])
if #sale.save
redirect_to #sale, :notice => "Successfully created sale."
else
render :action => 'new'
end
end
If you need to keep the :vehicles_attributes you can also use the reject method (without the bang) which gives you a copy instead of removing it from the original hash.
We are using ActiveAdmin in our Rails3 application for the default models. Now we needed to overwrite the show action. The OrderProcess model is a transient (tableless) model, which means that all fields are aggregated from other data. We use an internal module that provides the necessary methods to mock the MetaSearch methods ActiveAdmin is depending on. The following is how we overwrite the show action:
ActiveAdmin.register OrderProcess do
member_action :show, :method => :get do
#order_process = OrderProcess.all_orders_for_deal(params['id'])
end
end
That gives us an error complaining about a missing template "Missing template admin/order_processes/show with ..."
We also tried to call
render renderer_for(:show)
but that produced an error about a missing method model_name which may be due to our model being tableless and the regarding module.
How can we use ActiveAdmins built in rendering methods to display our model? Any help is appreciated.
Just ran into this... Grant's comment is correct, active_admin_template doesn't exist any more (I'm on 1.0.0-pre2).
I ended up going with:
render :action => :edit, :layout => false
which seems to work, although you will have to supply a label for the header, which displays as "translation missing: en.active_admin.[your_action]_model"
The solution mentioned at this other stackoverflow post worked:
render active_admin_template('edit.html.arb'), :layout => false
I had a similar issue where I needed to override the default active admin controller behavior for the update action. I got it to work like this:
controller do
def update
#model = Model.find(params[:id])
# do stuff
if #model.save
redirect_to admin_model_path(#model)
else
render :edit
end
end
end
The key was just render :edit which will render the default edit page already defined by active admin.
The other solution using
render active_admin_template('edit.html.arb'), :layout => false
did not work for me or any other combination of render renderer_for(:edit).
I have the same problem :(
I'm trying to override an update action and trying to render the 'edit action'
member_action :update, :method => :post do
if params[:user][:password].blank?
[:password, :password_confirmation, :current_password].collect{|p| params[:user].delete(p) }
end
#user = User.find(params[:id])
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to([:admin, #user]) }
else
format.html { render renderer_for(:edit) }
end
end
end
The activeadmin docs are very light on the specifics of how to override a standard controller action, which is frustrating given how opaque the source code is. Many of the internals in the gem seem to have changed a ton with version 1.0, which renders a lot of older Stack Overflow answers unusable.
Anyway, here's how I was above to override the #create action in my activeadmin controller (on Rails 4.2.x):
controller do
def create
#user = User.create_from_admin(permitted_params[:user])
if #user.persisted?
redirect_to resource_path(#user), notice: I18n.t("admin.user.create.notice")
else
render :action => :new
end
end
end
It's worth noting that activeadmin expects, if your model is User, for the create action to have a populated model instance as #user before it can render action => :new.
I wrote the internals of my custom create method as a class method on my model so I could unit-test it and bury as little code as possible in my activeadmin code.
For context, I needed to override this action because I'm using Devise and I wanted to let my admins create user accounts with a temporary password and a custom welcome email rather than the built-in :confirmable email for self-created accounts.
Here's that User class method:
def self.create_from_admin(params)
generated_password = Devise.friendly_token.first(8)
#user = User.new(params)
#user.password = generated_password
#user.skip_confirmation!
if #user.save
# Code to send custom email with temp password
end
#user
end
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.
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])