Rails multiple params permit calls - ruby-on-rails

I am trying to use multiple permits in a single method similar to the following (psuedocode)
def index
model.create(
params.permit(:b, :c)
)
params.permit(:a)
end
This is my actual code
def create
params.permit(:create_special_categories)
balance_sheet = ::BalanceSheet.create!(
balance_sheet_params.merge(date: Time.zone.now.to_date, entity: #entity)
)
balance_sheet.create_special_categories if params[:create_special_categories]
render json: balance_sheet, serializer: ::Api::V3::BalanceSheetSerializer
end
def balance_sheet_params
params.permit(
:id,
:entity,
:entity_id,
:date,
:name
)
end
However, I get the following error...
ActionController::UnpermittedParameters:
found unpermitted parameter: :create_special_categories
UPDATE
my solution was to avoid strong parameters all together.
def create
balance_sheet = ::BalanceSheet.new(
date: Time.zone.now.to_date, entity: #entity
)
balance_sheet.name = params[:name]
balance_sheet.save!
balance_sheet.create_special_categories if params[:create_special_categories]
render json: balance_sheet, serializer: ::Api::V3::BalanceSheetSerializer
end

This line doesn't have any effect, params.permit are not chained or added to a previous permit, you must use the result, that is why it's almost always used in a separate method.
params.permit(:create_special_categories)
What you must do is use what that returns for your following statements
permitted_params = params.permit(:create_special_categories)
Model.create(permitted_params)
...however you really should outsource this to a special method like you already have. You will have to tweak this to your use-case obviously.
def balance_sheet_params
if params[:create_special_categories]
params.permit(:id,
:entity,
:entity_id,
:date,
:name,
:create_special_categories)
else
params.permit(
:id,
:entity,
:entity_id,
:date,
:name)
end
end

Related

refactor params permited to change input

at first I'm newbie in rails , I have rails application witch contains profile for each user , in profile based on each user gender front send a params with . for example "Mr . " but in my controller i could not accecpt so it return error
"#<ArgumentError: 'mr.' is not a valid title>",
i want to edit my controller to get params and change it to without dot ,I think maybe it could possible to use gsub but when i try to edit it returns error
"exception": "#<NoMethodError: undefined method `gsub' for #<Enumerator: "mr.":gsub(" ")>>",
,is it possible to refactor in params permit. it's my controller
def update
if (profile_update_params.present?)
profile_update_result = ::Api::V1::Profile::Update.call(profile_params: profile_update_params, profile: #current_user.profile, request: request)
return render json: Helpers::ErrorsHandler.view_parse(profile_update_result), status: :bad_request if profile_update_result.errors
end
if (user_update_params.present?)
user_update_result = ::Api::V1::User::Update.call(user_params: user_update_params, user: #current_user)
return render json: Helpers::ErrorsHandler.view_parse(user_update_result), status: :bad_request if user_update_result.errors
end
result = sheriff
render result.view
end
and this is my params permit
def profile_update_params
params.permit(:phone, :title, :email, :gender, :first_name, :last_name, :description, :job_title, :time_zone)
end
I found using gusb is currect but i'm using in wrong place ,I do not need to change hole params to string so I took my field out of params and change my params permit as bellow and every things is going fine
def profile_update_params
params.permit(:phone, :email, :gender, :first_name, :last_name, :description, :job_title, :time_zone).merge({title: params[:title].to_s.gsub(".", "")})
end

set instance variable equal to only specific model parameters

def place
#place = Activity.find(params[:id])
end
How would I choose specific params? We have activity.address1,2, zip state etc and just need an instance variable we can use which is equal to just the address attributes.
You could do something like:
def place
#place ||= Activity.find(params[:id])
#address_attributes = #place.attributes.slice('address_1', 'address_2', 'zip', 'state')
end
In which case you'll have an instance variable that contains a hash of the specified attributes that you might use something like:
#address_attributes['address_1']
Personally, I find typing the '' around the key values annoying and prefer using symbols. In which case you might do something like:
def place
#place ||= Activity.find(params[:id])
#address_attributes = #place.attributes.with_indifferent_access.slice(:address_1, :address_2, :zip, :state)
end
Which you would use something like:
#address_attributes[:address_1]
But, even the brackets and symbols are a little annoying. So, you might do something like:
def place
#place ||= Activity.find(params[:id])
#address_attributes = OpenStruct.new(#place.attributes.with_indifferent_access.slice(:address_1, :address_2, :zip, :state))
end
Which you could then use something like:
#address_attributes.address_1
Now, personally, I don't like that long line in the place method, so I would be tempted to do something like:
def place
#place ||= Activity.find(params[:id])
#address_attributes = get_address_attributes
end
private
def get_address_attributes
OpenStruct.new(
#place.
attributes.
with_indifferent_access.
slice(
:address_1,
:address_2,
:zip,
:state
)
)
end
Now, if this is all in a controller, and you're setting the #address_attributes variable just so that you can use it in a view, then maybe that's good enough.
But, if you're using the #address_attributes variable elsewhere in the current instance, then you might consider doing something like:
attr_accessor *%w(
address_attributes
).freeze
delegate *%w(
address_1
address_2
zip
state
), to: :address_attributes, prefix: place
def place
#place ||= Activity.find(params[:id])
#address_attributes = get_address_attributes
end
private
def get_address_attributes
OpenStruct.new(
#place.
attributes.
with_indifferent_access.
slice(
:address_1,
:address_2,
:zip,
:state
)
)
end
In which case, you could make calls like:
place_address_1
Which seems a lot nicer than something like:
#address_attributes['address_1']

Rails Controller: how do you account for optional and required parameters?

In the tutorials I have seen, create has been implemented like this:
def create
#note = Note.new(note_params)
#note.save
redirect_to #note
end
private
def note_params
params.require(:note).permit(:title, :type, :description, :dueDate)
end
I have read about the fetch method for optional parameters, but how do I mix the two methods to require some parameters and permit others? Would it be like this:
private
def note_params
params.require(:note).permit(:title, :type)
params.fetch(:note, {}).permit(:description, :dueDate)
end
In this case, could I expect to pass 0, 1, or 2 of the fetched params?
You're reading it wrong, I think. In this line
params.require(:note).permit(:title, :type, :description, :dueDate)
The required parameter is :note. And :title, :type and others are simply permitted/allowed to appear under :note. None of them are required by this syntax. If you really need :title to be there, that is best handled by presence validation on your Note model.
class Note
validates_presence_of :title
end
Now, if you don't pass params[:note][:title], #note.save will return false and you can render form with user-friendly errors (highlight missing fields, etc.). You wouldn't be able to do that (as easily) if note_params method raised an exception on missing title attribute. So that is how you handle required record attributes in rails.

Ruby on Rails new() method not storing values

I have a modified copy of https://github.com/talho/openphin/blob/master/app/controllers/admin/invitations_controller.rb
The main code is primarily the same, however our system was upgraded a few months back to Rails 4.x and the invitation system no longer works.
The method with the issue is create. I have tried swapping out:
#invitation = Invitation.new(params[:invitation])
with
#invitation = Invitation.create(invitation_params)
And creating
def invitation_params
params.require(:invitation).permit!
end
However, here is what I have:
invitation_params = {"name":"Test","subject":"test","body":"test","organization_id":"","author_id":24448}
#invitation = {"id":null,"name":null,"body":null,"organization_id":null,"author_id":null,"subject":null,"created_at":null,"updated_at":null,"lock_version":0}
Also, if I use create!, then my output error is:
E, [2015-12-14T13:03:38.664099 #24385] ERROR -- : Validation failed: Author can't be blank (ActiveRecord::RecordInvalid)
I could use any guidance/help on why everything ends up as null.
You call return what leaved the method, before you call save! in the record. Furthermore you might want to read about Strong Parameters. You might want to change your code to:
#invitation = Invitation.new(
params.require(:invitation).permit(
:name, :subject, :body, :organization_id, :author_id
)
)
#invitation.save!
render :json => { :invitation => #invitation }.as_json
return
Please note that you usually do not need to call return in controller method. And when you call save! immediately after new then create! might be an better option:
def create
invitation = Invitation.create!(invitation_params)
render json: { invitation: invitation }.as_json
end
private
def invitation_params
params.require(:invitation).permit(
:name, :subject, :body, :organization_id, :author_id
)
end

Rendering json from two models in Rails index

I would like to render json in an index method that joins data from a foreign-key related table in Rails. I have a model that has user_id as a foreign key.
class Batch < ActiveRecord::Base
belongs_to :user
I am then rendering that table and exposing the user_id field.
def index
panels = Batch.where("user_id = #{current_user.id} or (user_id <> #{current_user.id} and public_panel = true)")
render json: panels, only: [:id, :description, :name, :public_panel, :updated_at, :user_id], root: false
end
What I would like to do is to somehow expose the users name, which is on the Users model. ie: user_id.user_name
EDIT
From reading documentation, I think I need to use include but would also like to alias one of the fields. I have a field called name in both tables.
Something is wrong with this include
def index
panels = Batch.include([:users]).where("user_id = #{current_user.id} or (user_id <> #{current_user.id} and public_panel = true)")
render json: panels, only: [:id, :name, :description, :public_panel, :updated_at, :user_id], root: false
end
EDIT2
Thank you #Paritosh Piplewar ... I am getting some errors with the syntax. To complicate matters the field I am after is user.name, not user.user_name. This will conflict with batches.name, so I need to alias it.
Started GET "/api/batches" for 10.0.2.2 at 2014-08-13 15:39:03 +0200
SyntaxError (/home/assay/assay/app/controllers/api/batches_controller.rb:11: syntax error, unexpected tLABEL
user: { only: :first_name },
^
/home/assay/assay/app/controllers/api/batches_controller.rb:11: syntax error, unexpected ',', expecting keyword_end
user: { only: :first_name },
^
/home/assay/assay/app/controllers/api/batches_controller.rb:12: syntax error, unexpected ',', expecting keyword_end):
EDIT 3
The original question has been answered, but the json returned is like this
data: [{"id"=>1306, "updated_at"=>Wed, 13 Aug 2014 12:37:23 UTC +00:00, "description"=>"asc", "user_id"=>1, "public_panel"=>true, "user"=>{"name"=>"Joe Bloggs"}}, {"id"=>1307,
This bit is causing problems for my Angular.js front-end.
"user"=>{"name"=>"Joe Bloggs"}}
i assume you mean user.user_name
this is how you can do it
def index
panels = Batch.where("user_id = #{current_user.id} or (user_id <> #{current_user.id} and public_panel = true)")
data = panels.as_json(include:
{user: { only: :name }},
only: [:id, :description, :public_panel, :updated_at, :user_id],
root: false)
render json: data
end
You can define custom method and do it. Something like this
class Batch < ActiveRecord::Base
belongs_to :user
def user_name
user.name
end
end
Now, in controller you can simply do this
def index
panels = Batch.where("user_id = ? or (user_id <> ? and public_panel = true)", current_user.id, current_user.id)
data = panels.as_json(methods: :user_name,
only: [:id, :description, :public_panel, :updated_at, :user_id, :user_name],
root: false)
render json: data
end
For more complex json views than just redering a single model, I'd tell you to use jbuilder. Jbuilder is now part of the rails framework.
It's as easy as
Remove render line and make panels an instance variavle (#panels)
Create a index.json.jbuilder file under app/views/api/batches
Create the view you want
json.array #panels do |panel|
panel.(panel,
:id,
:description,
:public_panel,
:user_id,
:updated_at
)
json.user panel.user.user_name
end

Resources