What builds nested parameters in Rails? - ruby-on-rails

I am spiking an AngularJS 1.0.8 application, which is doing POST requests to a Rails 3.2.13 server. I did something very similar earlier today with slightly different results.
For the sake of example, the main resource will be Person(name:string).
In the earlier attempt, the submitted attributes for creating a new Person instance were processed by Rails as follows: {"name"=>"John", "person"=>{"name"=>"John"}}
In the later attempt, the submitted attributes were processed as follows: {"name"=>"John", "person"=>{}}
In the outbound request from my browser, the request bodies look identical: {"name":"John"}
I would like to know which part of Rails decides what to use for nested parameters, so that I may figure out why these two implementations seem to differ.

It turns out that the attr_accessor fields on the newer version of the model did not correspond with the fields being sent with the client POST. Those fields are being used by Rails to populate the nested parameters on such a request.
class Person
attr_accessor :name
end
# request body for a POST to /people
{"name":"John","age":"23"}
# request parsed and nested in Rails route
{"name"=>"John", "age"=>"23", "person"=>{"name":"John"}}
My assumption, based on what I have read in related nested-param questions and docs, is that fields and associations on ActiveRecord models are used to decide the structure of nested parameters.

Related

Rails 5 Params / Strong Params issue

I have a RESTful API (ActionController::API) service that accepts a list of params. The RESTful service takes a JSON body request (with Content-Type of application/json). When I debug the controller/action (right after the action def) and take a peak at the params list, it appears as follows:
<ActionController::Parameters {"given_name"=>"Mark", "subdomain"=>"development", "controller"=>"user", "action"=>"create", "user"=>{"given_name"=>"Mark"}} permitted: false>
EDIT
All the request has in it (when it is passed to the controller/action -- using POSTman):
{"given_name":"Mark"}
Notice that the object contains the given_name params twice. Is this normal behavior? I did not pass a "user" object (json object) to the controller/action? What is the permitted flag?
When I try to use (right now I'm just testing RESTful call and assigning any values that the user object will except... no validations have been programmed yet):
user = User.new(params)
I get the error:
#<ActiveModel::ForbiddenAttributesError: ActiveModel::ForbiddenAttributesError>
So, I've looked everywhere for the reasoning behind this (why is there a "user" key in the params list? What is the purpose of the permitted flag? Why am I getting an error when I try to assign params)?
EDIT
After doing some testing, I change the controller name from "user_controller" to "tester_controller" and setup the routes to point to the renamed controller.
It seems the "user" object in the params list above has changed to "tester". So why does the param list contain an "object" with all the passed params with the name of the controller? If that's the case, why is it needed?
Any help would be greatly appreciated.
By default Rails in API mode wraps JSON request parameters into a hash guessing its name from controller class. That's why changing the controller from User to Tester changes "object" name. You can read details here.
So if you don't need this "object" in your params just remove :json from :format array in config\initializers\wrap_parameters.rb. Or you can use fine-grained control at the controller level as described above.
You need to specify which attributes are acceptable for mass-assignment in your controller.
def create
#user = User.new(params.require(:user).permit(:given_name))
end
This prevents malicious users from making request posts that alter attributes internal to your application, like role in the case of a user object.
As mentioned above, a better explanation can be found in the guide referring to strong parameters.
You can't pass params to constructor, because params always contain :action and :controller keys. Attributes for new objects should be put in hash under key that identify model you want to create e.g. :user. I suggest you to consult rails guides, especially chapter 7 of "Form Helpers" guide.
If you want to learn more about strong parameters there is a chapter in rails guides for that too :)

PUT or POST when creating and updating records at the same time?

I have a mobile app that accesses a server running a Rails 3.x app. The data is exchanged in JSON format.
I have a rather complicated issue, that I going to try and simplify here.
In one scenario, the mobile app posts a new sale record to the database which includes an update to an existing order record. To achieve this as a single HTTP request, it passes the order record attributes update as part of the new sale record via nested attributes.
The Rails model for Sales includes the following:
attr_accessible :product_id, :order_id, :order_attributes
accepts_nested_attributes_for :order, :update_only => true
The Rails routings are standard.
As the http request to post the sale will create a new record (i.e. it does not include an existing sale_id), I use HTTP POST.
I can post a new sale record (without the order record update) just fine.
However, when I include the nested attributes to update the order record, I get 404 'not found'.
I suspect that this is because the nested update of the existing order includes an 'id' and therefore should actually be a PUT (to route correctly).
To help diagnose the issue, I've tried changing existing working non-nested POSTs of sales (that don't include an id) to be PUT requests, and in that case, it does break the functionality, and I do get a 404. This suggests to me that the server would indeed report PUT/POST confusion as a 404.
To be clear I can successfully post updates using nested attributes in other circumstances using the same code, so I'm confident that it's not the actual attribute encoding that is the problem.
I'd appreciate thoughts on whether this could be the issue, and if so what I can do about it.
Thank you.

assign params hash with javascript variable

Is it possible to assign the params hash with javascript variables in the views side.
because i would be doing a complicated structure in my params hash that involves nesting and arrays.
You can post JSON data to the rails server and Rails will make it available in params hash, provided the JSON response data has the correct headers. Refer this thread and this thread for more details.
The only way I can think of is to use JS to add new fields to your form, giving them IDs and names adhering to how Rails will parse the elements' names into params when the form is submitted (something along the lines of id=model_assoc_attributes_N_attr and name=model[assoc_attributes][N][attr], which I think is the case when Model accepts_nested_attributes_for Assoc).

Thoughts regarding model ids in rails routes and validation

I am new to RoR and started working on a typical 'has_many' association (ie. a user has many friends). I have everything working correctly, but I don't like having the ids exposed in the url. I find that I need to add extra validation in my controller to make sure the ids represent valid associations in case the user manually entered different ids.
Personally I would like to see the ids out of the url and passed via some other means but that is not always possible. Shallow nesting of resources will help reduce the number of ids I need to validate at least.
What is the RoR philosophy on this? I have not seen anything specific to this issue.
Thanks
the URL has parameters if it is a GET url.
Try using POST parameters, which means your url will no longer be cluttered. Note that a malicious user can still send a made-up POST request using curl.
My approach to this is implementing proper authorization. If the user requests information for an object he is not permitted to read, this should be handled by an authorization framework.
With CanCan or Declarative Authorization you can define rules that replace your "manual" (and error-prone) checks in controllers.
I like the IDs being in the URL. That is what REST is about. Getting information for specific Resources, which have to be identified with an ID.
You can use Friendly ID in order to replace the integer ID by a slug (e.g. users/tollbooth instead of users/42).
basically ror routes by default takes id as key to generate urls. If you are not fan of id based urls then you can always override urls by using to_param inside model.
def to_param
# make sure this field is always present & unique
username
end
then by default you will start seeing username instead of id inside urls
How to find object inside controller actions
User.find_by_username(params[:id])
If you dont want to do this manually make use of slug gems like friendly id

Rails Form Validations for Multi-Model Forms

I am trying to build a Rails app with multiple models in a single form, and multiple forms on a single page. To make that work (according to my limited knowledge), I have to drop out of the scaffold code and the "form_for :model" helper and use "form_tag" instead. However when I do that, I lose the ability to automatically catch and report form validation errors in the view (with the error message in the flash[:error] and have the invalid fields highlighted.
If I have a controller method for a form that has to validate data from multiple models, how to I pass the validation errors back to the form? What do I have to do to get the invalid fields highlighted?
(For the longest time I didn't "get" Rails forms, because I thought they were useless Ruby wrappers for HTML code. Now that I am working in a non-Rails environment, I realize how much hard work they save because validation is tied to ActiveRecord validaions, and if a validation fails, the form can be reposted with the invalid fields hightlighted and a useful message in flash[:error]).
To add multiples models to a simple form, after rails 2.3 you just have to add accepts_nested_attributes_for in your model, the model that will be connected with your controllers and views, change the views to support information from another models (with field_for) and maybe build the reference objects in your controllers. Check these links:
http://ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes.
http://github.com/alloy/complex-form-examples

Resources