Strong params when POSTing multiple items to Rails API - ruby-on-rails

I am trying to create multiple records via a POST request to the Rails API but am having issues with the params side of things.
What is happening is the item is not being created as the callbacks in the model are looking for values that are not present. I believe this has something to do with the params.
Here is my code:
def batch_create
params[:product].each do |p|
product = Product.new(batch_create_product_params)
product.save
end
respond_to do |format|
format.json { render json: #product }
end
end
def batch_create_product_params
params.permit(:name, :link, :category_old, :image_url, :price, :interest, :revenue, :end_date, :company, :country, :above_average_revenue)
end
The raw contents of my json call are:
{
"product": [
{
"name": "Strap On SoftRack Roof Rack by Otium Acupressure",
"link": "https://things.com/89902997836713867011111211111121/Strap-On-SoftRack-Roof-Rack-by-Otium",
"category_old": "",
"image_url": "https://thingd-media-ec1.com/default/899029978367138670_42120cf10765.jpg",
"price": "160",
"interest": "9999",
"company": "ACME",
"country": "USA",
"revenue": "999999"
},
{
"name": "Strap On SoftRack Roof Rack by Otium Acupressure 2",
"link": "https://things.com/Strap-On-SoftRack-Roof-Rack-by-Otium",
"category_old": "",
"image_url": "https://thingd-media-ec1.com/default/899029978367138670_42120cf10765.jpg",
"price": "160",
"interest": "9999",
"company": "ACME",
"country": "USA",
"revenue": "999999"
}
]
}
The resulting params are then as follows:
The total parameters when the call is first made.
Parameters: {"product"=>[{"name"=>"Strap On SoftRack Roof Rack by Otium Acupressure", "link"=>"https://fancy.com/things/89902997836713867011111211111121/Strap-On-SoftRack-Roof-Rack-by-Otium", "category_
old"=>"", "image_url"=>"https://thingd-media-ec1.thefancy.com/default/899029978367138670_42120cf10765.jpg", "price"=>"160", "interest"=>"9999", "company"=>"Fancy", "country"=>"USA", "revenue"=>"999999"},
{"name"=>"Strap On SoftRack Roof Rack by Otium Acupressure 2", "link"=>"https://fancy.com/things/Strap-On-SoftRack-Roof-Rack-by-Otium", "category_old"=>"", "image_url"=>"https://thingd-media-ec1.thefancy.
com/default/899029978367138670_42120cf10765.jpg", "price"=>"160", "interest"=>"9999", "company"=>"Fancy", "country"=>"USA", "revenue"=>"999999"}], "import"=>{}}
The params once they are in the loop.
{"name"=>"Strap On SoftRack Roof Rack by Otium Acupressure", "link"=>"https://fancy.com/things/89902997836713867011111211111121/Strap-On-SoftRack-Roof-Rack-by-Otium", "category_old"=>"", "i
mage_url"=>"https://thingd-media-ec1.thefancy.com/default/899029978367138670_42120cf10765.jpg", "price"=>"160", "interest"=>"9999", "company"=>"Fancy", "country"=>"USA", "revenue"=>"999999"}

I believe what you're looking for is more like this. Placing the root product key in the permit call tells rails that this is going to be an array of objects, containing the keys noted.
def batch_create
batch_create_product_params[:product].each do |product_params|
product = Product.new(product_params)
product.save
end
respond_to do |format|
format.json { render json: #product }
end
end
def batch_create_product_params
params.permit(product: [:name, :link, :category_old, :image_url, :price, :interest, :revenue, :end_date, :company, :country, :above_average_revenue])
end

I don't think new takes an array. I think you would need to do this - use the p param in the strong parameters.
def batch_create
params[:product].each do |p|
product = Product.new(batch_create_product_params(p)) # Pass p
product.save
end
respond_to do |format|
format.json { render json: #product }
end
end
def batch_create_product_params(p) # Accept params
p.permit(:name, :link, :category_old, :image_url, :price, :interest, :revenue, :end_date, :company, :country, :above_average_revenue)
end
create does take an array. Maybe this would do in your case.
def batch_create
product = Product.create(batch_create_product_params[:product])
respond_to do |format|
format.json { render json: #product }
end
end
def batch_create_product_params
params.permit(product: [:name, :link, :category_old, :image_url, :price, :interest, :revenue, :end_date, :company, :country, :above_average_revenue])
end

Related

Rails API: how translate json request in spanish if my model is in english?

Model convention parameters need to be in English, but the input JSON request keys need to send it in Spanish, how is the best practice for rails to accept parameters in Spanish and save in database?
MODEL:
class Player < ApplicationRecord
validates :name, :level, :goals, :salary, :bonus, :team, presence: true
end
INPUT:
{
"jugadores" : [
{
"nombre":"Snow",
"nivel":"C",
"goles":10,
"sueldo":50000,
"bono":25000,
"sueldo_completo":null,
"equipo":"rojo"
},
{
"nombre":"JC",
"nivel":"A",
"goles":30,
"sueldo":100000,
"bono":30000,
"sueldo_completo":null,
"equipo":"azul"
}
]
}
Controller:
...
def create
#player = Player.new(player_params)
if #player.save
render json: #player, status: :created, location: #player
else
render json: #player.errors, status: :unprocessable_entity
end
end
...
def player_params
params.permit( jugadores: [ :nombre, :nivel, :goles, :salario, :bono, :salario_completo, :equipo ] )
end
...
thnx
Ill tried to translate with I18n, but I can't solve translate ActiveController
$"translation missing: en.{"jugadores"=>{"name"=>"Irving"}}"

I want whole model return I have 2 models by using pg search in rails

I want this form
"data": [
{
"id": 2,
"searchable_type": "User",
"email": "abc",
"first_name": "abc",
"last_name": "xyz",
"created_at": "2022-08-05T09:40:18.986Z",
"updated_at": "2022-08-05T09:40:18.986Z"
},
{
"id": 3,
"searchable_type": "blog",
"tittle": "user",
"created_at": "2022-08-05T09:40:18.986Z",
"updated_at": "2022-08-05T09:40:18.986Z"
}
]
I want to return whole object for each model respectively by using Pg Search multi search it just return a content ,searchable_type and searchable_id as attached images belowIt is my postman image for blog returnIt is my postman image for user return
[My search controller, User and blog models]
class Api::V1::SearchController < Api::V1::ApiController
def index
#query = params[:value]
#results = PgSearch.multisearch(#query)
render json: { data: #results}, status: :ok
end
end
class Blog < ApplicationRecord
include PgSearch::Model
multisearchable against: [:id, :title, :created_at, :updated_at ]
end
class User < ApplicationRecord
include PgSearch::Model
multisearchable against: [:id, :email, :first_name, :last_name ]
end
Change
#results = PgSearch.multisearch(#query)
to
#results = PgSearch.multisearch(#query).includes(:searchable).map(&:searchable)

Rails 5.1.: strong params for JSON POST request

I have JS where data are posted with Ajax and in terminal my params look like this:
Started POST "/strongbolt/user_groups" for 10.0.2.2 at 2017-06-27 16:27:23 +0000
Processing by Strongbolt::UserGroupsController#create as JSON
Parameters: {"strongbolt_user_group"=>{"name"=>"Some test group",
"description"=>"Some test description", "user_ids"=>{"0"=>{"id"=>"3"},
"1"=>{"id"=>"2"}, "2"=>{"id"=>"5"}}, "role_ids"=>{"0"=>{"id"=>"1"},
"1"=>{"id"=>"2"}}}}
My Create action looks like this:
def create
user_roles #Helper method
#user_group = Strongbolt::UserGroup.create!(user_group_params)
respond_to do |format|
format.js { flash.now[:notice] = "User group #{#user_group.name} created!" }
format.json { render json: {
data: #user_group.as_json(only: [:id, :name, :description], include: {
users: { only: [:id, :name] }, roles: {only: [:id, :name] }}),
}
}
end
end
private
def user_group_params
params.require(:strongbolt_user_group)
.permit(:name, :description, {user_ids: []}, {role_ids: []})
end
In my terminal I can see it create name and description, but does not insert user_ids and role_ids. So far I've been trying differently with params, but no luck - can't make them to be saved.
I get this error: Unpermitted parameters: :user_ids, :role_ids
How do I make all params to be saved, please? Thank you!
instead of
{
"strongbolt_user_group"=>{
"name"=>"Some test group",
"description"=>"Some test description",
"user_ids"=>{"0"=>{"id"=>"3"}, "1"=>{"id"=>"2"}, "2"=>{"id"=>"5"}},
"role_ids"=>{"0"=>{"id"=>"1"}, "1"=>{"id"=>"2"}}
}
}
you should send
{
"strongbolt_user_group"=>{
"name"=>"Some test group",
"description"=>"Some test description",
"user_ids"=>["3", "2", "5"],
"role_ids"=>["1", "2"]
}
}
EDIT
If you can't change the format of received params, you could do something like the following:
private
def user_group_params
modified_params.require(:strongbolt_user_group)
.permit(:name, :description, {user_ids: []}, {role_ids: []})
end
def modified_params
user_ids = params[:strongbolt_user_group][:user_ids].values.map(&:values).flatten
role_ids = params[:strongbolt_user_group][:role_ids].values.map(&:values).flatten
ActionController::Parameters.new({
strongbolt_user_group: params[:strongbolt_user_group].except(:user_ids, role_ids).merge(user_ids: user_ids, role_ids: role_ids)
})
end

Rails - 5: How do I pass an array (has_many association) to the controller action

I have a model event and another model event_rule
class Event < ApplicationRecord
has_many :event_rules
end
class EventRule < ApplicationRecord
belongs_to :event
end
I have written an api event#create for saving an event. Here's the body of the POST request:
{
"name": "asd",
"code": "ad",
"isActive": true,
"description": "asd",
"notes": "",
"goalAmount": 0,
"exportId": "",
"defaultCurrency": 1,
"eventStartDate": "2017-04-25T18:30:00.000Z",
"eventEndDate": "2017-04-27T18:30:00.000Z",
"eventRules": [
{
"extraInformation": "{}",
"lookupKeyValuePairId": 40
}
]
}
Here's params hash:
Parameters: {"name"=>"asd", "code"=>"ad", "is_active"=>true, "description"=>"asd", "notes"=>"", "goal_amount"=>0, "export_id"=>"", "default_currency"=>1, "event_start_date"=>"2017-04-25T18:30:00.000Z", "event_end_date"=>"2017-04-27T18:30:00.000Z", "event_rules"=>[{"extra_information"=>"{}", "lookup_key_value_pair_id"=>40}], "client_id"=>"0", "event"=>{"name"=>"asd", "code"=>"ad", "description"=>"asd", "is_active"=>true, "goal_amount"=>0, "export_id"=>"", "event_start_date"=>"2017-04-25T18:30:00.000Z", "event_end_date"=>"2017-04-27T18:30:00.000Z", "default_currency"=>1, "notes"=>""}}
I want the 'event_rules' to be included INSIDE the event. How can do this?
def create
# initialize Event object with `event_params`
event = Event.new(event_params)
# initialize EventRule object per each `event_rule_params`, and associate the EventRule as part of `event.event_rules`
event_rules_params.each do |event_rule_params|
event.event_rules << EventRule.new(event_rule_params)
end
if event.save
# SUCCESS
else
# FAILURE
end
end
private
def event_params
params.require(:event).permit(:name, :code, :is_active, :description, :notes, :goal_amount, :export_id, :default_currency, :event_start_date, :event_end_date, :notes)
end
def event_rules_params
params.require(:event).fetch(:event_rules, []).permit(:extra_information, :lookup_key_value_pair_id)
end
Alternative Rails-way Solution:
if you have control over the parameters that get sent, reformat your request into something like the following (take note of changing event_rules into event_rules_attributes -- Rails Standard) (More Info Here)
Parameters: {
"event"=>{
"name"=>"asd",
"code"=>"ad",
"description"=>"asd",
"is_active"=>true,
"goal_amount"=>0,
"export_id"=>"",
"event_start_date"=>"2017-04-25T18:30:00.000Z",
"event_end_date"=>"2017-04-27T18:30:00.000Z",
"default_currency"=>1,
"notes"=>"",
"event_rules_attributes"=>[
{
"extra_information"=>"{}",
"lookup_key_value_pair_id"=>40
}
]
}
}
# controllers/events_controller.rb
def create
event = Event.new(event_params)
if event.save
# SUCCESS
else
# FAILURE
end
end
private
def event_params
params.require(:event).permit(:name, :code, :is_active, :description, :notes, :goal_amount, :export_id, :default_currency, :event_start_date, :event_end_date, :notes, event_rules_attributes: [:extra_information, :lookup_key_value_pair_id])
end
# models/event.rb
accepts_nested_attributes_for :event_rules

How to use nested_attributes when processing JSON?

I'm trying to write an update method that processes JSON. The JSON looks like this:
{
"organization": {
"id": 1,
"nodes": [
{
"id": 1,
"title": "Hello",
"description": "My description."
},
{
"id": 101,
"title": "fdhgh",
"description": "My description."
}
]
}
}
Organization model:
has_many :nodes
accepts_nested_attributes_for :nodes, reject_if: :new_record?
Organization serializer:
attributes :id
has_many :nodes
Node serializer:
attributes :id, :title, :description
Update method in the organizations controller:
def update
organization = Organization.find(params[:id])
if organization.update_attributes(nodes_attributes: node_params.except(:id))
render json: organization, status: :ok
else
render json: organization, status: :failed
end
end
private
def node_params
params.require(:organization).permit(nodes: [:id, :title, :description])
end
I also tried adding accepts_nested_attributes_for to the organization serializer, but that does not seem to be correct as it generated an error (undefined method 'accepts_nested_attributes_for'), so I've only added accepts_nested_attributes_for to the model and not to the serializer.
The code above generates the error below, referring to the update_attributes line in the update method. What am I doing wrong?
no implicit conversion of String into Integer
In debugger node_params returns:
Unpermitted parameters: id
{"nodes"=>[{"id"=>101, "title"=>"gsdgdsfgsdg.", "description"=>"dgdsfgd."}, {"id"=>1, "title"=>"ertret.", "description"=>"etewtete."}]}
Update: Got it to work using the following:
def update
organization = Organization.find(params[:id])
if organization.update_attributes(nodes_params)
render json: organization, status: :ok
else
render json: organization, status: :failed
end
end
private
def node_params
params.require(:organization).permit(:id, nodes_attributes: [:id, :title, :description])
end
To the serializer I added root: :nodes_attributes.
It now all works, but I'm concerned about including the id in node_params. Is that safe? Wouldn't it now be possible to edit the id of the organization and node (which shouldn't be allowed)? Would the following be a proper solution to not allowing it to update the id's:
if organization.update_attributes(nodes_params.except(:id, nodes_attributes: [:id]))
looks super close.
Your json child object 'nodes' need to be 'nodes_attributes'.
{
"organization": {
"id": 1,
"nodes_attributes": [
{
"id": 1,
"title": "Hello",
"description": "My description."
},
{
"id": 101,
"title": "fdhgh",
"description": "My description."
}
]
}
}
You can do this sort of thing. Put this in your controller.
before_action do
if params[:organization]
params[:organization][:nodes_attributes] ||= params[:organization].delete :nodes
end
end
It will set the correct attribute in params and still use all the accepts_nested_attributes features.

Resources