Polymorphic rails relations in Angularjs application - ruby-on-rails

Many tutorials cover the simple nesting in Rails model when working with AngularJS.
But I spent almost a week trying to implement polymorphic relations into angular controllers.
I have organization with polymorphic phones, emails, etc. I try to save new Organization.
Here is my controller:
angular.module('GarageCRM').controller 'NewOrganizationsCtrl', ($scope, $location, Organization) ->
$scope.organization = {}
$scope.organization.phones_attributes = [{number: null}]
$scope.create = ->
Organization.save(
{}
, organization:
title: $scope.organization.title
description: $scope.organization.description
phones:
[number: $scope.phone.number]
# Success
, (response) ->
$location.path "/organizations"
# Error
, (response) ->
)
I have
accepts_nested_attributes_for :phones
in my rails model and
params.require(:organization).permit(:id, :title, :description, phones_attributes:[:id, :number])
in controller. While saving I have response from console:
Processing by OrganizationsController#create as JSON Parameters:
{"organization"=>{"title"=>"test212",
"phones"=>[{"number"=>"32323"}]}} Unpermitted parameters: phones
Any idea how to fix it?

Your problem is that you are permitting "phones_attributes" in your back-end controller:
... phones_attributes:[:id, :number] ...
But you are sending "phones" in your front-end controller:
... phones: [number: $scope.phone.number] ...
So you see the "phones" attribute in the JSON the server receives:
JSON Parameters: ... "phones"=>[{"number"=>"32323"}] ...
And it correctly tells you it isn't permitted:
Unpermitted parameters: phones
The best way to fix this is to make the front-end controller send "phones_attributes" instead of "phones":
... phones_attributes: [number: $scope.phone.number] ...

This could be a strong paramaters issue from rails 4. In the rails controller for organization you should have a method like the follwing
private
def org_params
params.require(:organization).permit(:id, :name, :somethingelse, :photos_attributes[:id, :name, :size ])
end
And then in the create method you should:
def create
respond_with Organization.create(org_params)
end

Related

Unpermitted parameter: :images in Rails

I'm new to Rails and have started building my first api; I'm attempting to send an array of strings down as one of the parameters in my api request, like this:
{
"name": "doot doot",
"plans": "",
"sketches": "",
"images": ["foo.png", "bar.png"]
}
Originally, images was a string but I ran a migration to alter it to allow for an array of strings instead, like this:
change_column :projects, :images, "varchar[] USING (string_to_array(images, ','))"
In the controller I've defined the create function as:
def create
project = Project.create(project_params)
render json: project
end
def project_params
params.require(:project).permit(:name, :plans, :sketches, :images)
end
but I still get the following error:
Unpermitted parameter: :images. Context: { controller: ProjectsController, action: create, request: #<ActionDispatch::Request:0x00007fb6f4e50e90>, params: {"name"=>"Simple Box", "plans"=>"", "sketches"=>"", "images"=>["foo.png", "bar.png"], "controller"=>"projects", "action"=>"create", "project"=>{"name"=>"Simple Box", "plans"=>"", "sketches"=>"", "images"=>["foo.png", "bar.png"]}} }
I consulted this question here but the solutions didn't work; any suggestions?
You need to specify that images is an array.
params.require(:project).permit(:name, :plans, :sketches, images: [])
See Permitted Scalar Values in the Rails Guides.

How to permit array of hashes in Rails 5.2

I am trying to create multiple "Absence"s by posting:
Parameters: {"absences"=>[{"user_id"=>1, "lesson_id"=>25,
"excused"=>true}, {"user_id"=>2, "lesson_id"=>25, "excused"=>true}]}
However, I am not able to whitelist this format in the controller. I attempted to follow the solution from "How to use strong parameters with an objects array in Rails".
In my case:
def absence_params
params.permit(absences: [:user_id, :lesson_id, :excused])
end
I get
ActiveModel::UnknownAttributeError (unknown attribute 'absences' for Absence.):
Then I tried:
Parameters: {"absence"=>[{"user_id"=>1, "lesson_id"=>25,
"excused"=>true}, {"user_id"=>2, "lesson_id"=>25, "excused"=>true}]}
def absence_params
params.permit(:absence, array: [:user_id, :lesson_id, :excused])
end
and got:
Unpermitted parameters: :absence, :format
---- Resolved ----
The gem 'cancancan' was not allowing me to create using an array.
If you have an issue permitting an array in the strong params, try
params.require(:absences).map do |p|
p.permit(:user_id, :lesson_id, :excused)
end
Your parameters permit code is correct:
require "bundler/inline"
gemfile(ENV['INSTALL'] == '1') do
source "https://rubygems.org"
gem "actionpack", "6.0.2.2"
gem "activesupport", "6.0.2.2"
end
require "active_support/core_ext"
require "action_controller/metal/strong_parameters"
require "minitest/autorun"
class BugTest < Minitest::Test
def test_stuff
params = ActionController::Parameters.new({
"absences"=>[
{"user_id"=>1, "unpermitted_param" => 123, "lesson_id"=>25, "excused"=>true},
{"user_id"=>2, "lesson_id"=>25, "excused"=>true}
]
})
assert_equal(
{
"absences"=>[
{"user_id"=>1, "lesson_id"=>25, "excused"=>true},
{"user_id"=>2, "lesson_id"=>25, "excused"=>true}
]
},
params.permit(absences: [:user_id, :lesson_id, :excused]).to_h
)
end
end
The error comes from some other place, most likely you're trying to do something like Absence.create(absence_params), which will only work for single records.
To create an array at once you should adjust other relevant code accordingly, for example:
Manually handle the array like:
#absenses = params["absences"].map do |raw_absense_params|
Absense.create!(raw_absense_params.permit(:user_id, :lesson_id, :excused))
end
Employ accepts_nested_attrubutes_for :absenses for the parent model if you have any (probably Lesson). The code for this will be cleaner, as Rails will handle most things for you, like cases when not all instances can be saved because of validation, etc.

Rails API: Cannot whitelist JSON field attribute

I'm building a rails API with a model containing an attribute data of JSON type. (PSQL)
But when I try to post something like this
{ model: { name: 'Hello', data: { a: 1, b: 2 } } }
Rails thinks a and b are the attributes of a nested data association... It considers then they are unpermitted params.
The thing is, { a: 1, b: 2 } is the value of my field data.
How to provide JSON value to an attribute ?
-
Edit:
The error displayed is:
Unpermitted parameters: name, provider, confidence, location_type, formatted_address, place_id, types, locality, ...
The value of the data attribute is { name: 'Name', provider: 'Provider', ... }
Like I said Rails thinks they are the attributes of a nested data association.
-
Log:
Pastebin
if the keys are unknown in advance this could be a workaround:
def model_params
data_keys = params[:model].try(:fetch, :data, {}).keys
params.require(:model).permit(data: data_keys)
end
Credit goes to aliibrahim, read the discussion https://github.com/rails/rails/issues/9454 (P.S seems like strong parameters will support this use case in Rails 5.1)
When you post something, you have to make sure that your json have the same parameters that your controller.
Example rails api:
def example
#model = Model.new(params)
#model.save
render(json: model.to_json, status: :ok)
end
def params
params.permit(:name, :provider, {:data => [:a, :b]})
end
Example front-end json for post:
var body = {
name: 'myName',
provider: 'provider',
data: {
a: 'something',
b: 'otherthing',
}
};
For some reason rails doesnt recognize a nested json, so you need to write into params.permit that data will be a json with that syntax, if where a array, the [] should be empty.

How to associate two objects in a HABTM relationship via an AJAX POST request

I'm unable to associate one object with another object in a HABTM relationship, only when creating a new object via JSON POST (the relationship works as expected when using the Rails console).
To reproduce: I created a sample project with Videos (has title and URL) and Playlists (has name):
rails new videotest
cd videotest
rails g scaffold video title:string url:string
rails g scaffold playlist name:string
rails g migration create_playlists_videos playlist_id:integer video_id:integer
rake db:migrate
I set the has_and_belongs_to_many relationships between the models:
class Playlist < ActiveRecord::Base
has_and_belongs_to_many :videos
end
class Video < ActiveRecord::Base
has_and_belongs_to_many :playlists
end
In the video controller I white-list the playlist_ids parameter:
def video_params
params.require(:video).permit(:title, :url, playlist_ids: [])
end
Then create some sample objects in a rails console:
Video.create!(title:"video1", url:"http://youtube.com/123")
Video.create!(title:"video2", url:"http://youtube.com/456")
#playlist = Playlist.create(name:"playlist1")
#playlist.videos.append([Video.first, Video.second])
Playlist.first.videos.count returns 2 as expected, showing that the HABTM relationship is correctly configured.
I can successfully create a new Video object using an AJAX POST request, where I've also hardcoded the ID of the first playlist:
data = JSON.stringify({
"title": "test video",
"url": "http://www.youtube.com/987",
"playlist_ids": [1]
});
$.ajax({
url: "http://" + window.location.host + "/videos.json",
type:"POST",
contentType:"application/json; charset=utf-8",
dataType:"json",
data: data,
success: function(msg) {
console.log("added " + msg.title)
}
});
Problem: the playlist_id isn't used and the resulting Video object isn't associated with Playlist 1.
The create method includes:
def create
#video = Video.new(video_params)
I notice that video_params doesn't include playlist_id, but params does:
(byebug) video_params
{"title"=>"test video", "url"=>"http://www.youtube.com/987"}
(byebug) params[:playlist_ids]
[1]
Why isn't playlist_ids getting through the whitelist?
If those associations are set up properly, your Playlist model should have a video_ids attribute, and your Video model should have a playlist_ids attribute.
You can pass these through the form, and rails should create the corresponding records in your playlists_videos link table.
So in your VideosController set up your whitelist like so:
def video_params
params.require(:video).permit(:title, :url, playlist_ids: [])
end
Now tweak your ajax call to pass through an array of playlist_ids like so:
JSON.stringify({ "video": {"title": title, "url": "http://www.youtube.com", "playlist_ids": [1,2]}})
Remember to nest the parameters inside the "video" node.
So in this case, you are going to associate the video with both playlist_id 1 and playlist_id 2, for example.
When you use habtm, you get several methods which you should use:
Specifically, if you want to set the playlist_id of the video at create, you'll be best populating the playlist_ids attribute:
$.ajax({
url: "http://" + window.location.host + "/videos.json",
type:"POST",
contentType:"application/json; charset=utf-8",
dataType:"json",
data: JSON.stringify({"title": title, "url": "http://www.youtube.com", "playlist_ids": [1]}),
success: function(msg) {
console.log("added " + msg.title)
}
});
#app/controllers/videos_controllers.rb
class VideosController < ApplicationController
def create
#video = Video.new video_params
#video.save
end
private
def video_params
params.require(:video).permit(:title, :url, playlist_ids: [])
end
end
This will override the current playlists your video is associated with; meaning if you want to add/remove the video to a playlist, you'd be best using the << and .delete methods.
I can explain about how to do this if required.

strong_params removing id of accepts_nested_attributes_for models

I have the follow strong_params statement:
def product_grid_params
params.require(:product_grid).permit(:name,
product_grid_locations_attributes: [:id, :grid_index, :item_id, :item_type, :short_name, :long_name]
).merge({ venue_id: params[:venue_id] })
end
But my params and product_grid_params look like this:
(byebug) params
{"product_grid"=>{"product_grid_locations_attributes"=>[{"id"=>"5560d1f7a15a416719000007", "short_name"=>"shrt", "long_name"=>"Whiskey Ginger", "grid_index"=>73, "item_type"=>"product", "item_id"=>"9b97aa28-1349-4f60-a359-3907c8ac9a74"}]}, "id"=>"5560d1f7a15a416719000006", "venue_id"=>"5560d1f7a15a416719000005", "format"=>"json", "controller"=>"api/v2/manager/product_grids", "action"=>"update"}
(byebug) product_grid_params
{"product_grid_locations_attributes"=>[{"grid_index"=>73, "item_id"=>"9b97aa28-1349-4f60-a359-3907c8ac9a74", "item_type"=>"product", "short_name"=>"shrt", "long_name"=>"Whiskey Ginger"}], "venue_id"=>"5560d1f7a15a416719000005"}
You'll notice that in the params, the product_grid_location's id is present, but it gets filtered out in product_grid_params. What gives? I need that id there to update nested attributes.
Looks like this was because of an issue with Mongoid. The id I was passing in was a Moped::BSON::ObjectId, which strong_params refused to parse. I converted it to a string and everything was fine after that:
params[:product_grid][:product_grid_locations_attributes].each { |location| location[:id] = location[:id].to_str }

Resources