1
votes

I am working on an application where I send a post request to my rails server with parameters stored in JSON format. Let's say my application routes the request to the create function on my cats_controller. The cat model has three fields :name, :hunger, :mood. Now each cat has_many kittens. My Kitten model has two fields :cat_id (referring to which cat owns it, because my kitten belongs_to a cat) and :cute. Now every time i create a cat on my application I want to do it with a single call, so I call /cats.json from my app with a POST request. The parameters are stored in JSON format and they include the following fields {name:"",hunger:"", mood:"",cute:""}. Now the controller takes these params creates a new cat, and then creates a new kitten assigned to this cat using cat.kittens.build(). The kitten then just needs to use the last parameter I sent "mood:" to get created properly.

Now the question is this, when I print the params variable from this controller I get the following hash: {name:"", hunger:"", mood:"", cute:"", cat:{name:"", hunger:"", mood:""}}. Why does this happen? How does Rails parse the POST request params and take me from

{name:"",hunger:"", mood:"",cute:""} to {name:"", hunger:"", mood:"", cute:"", cat:{name:"", hunger:"", mood:""}}

How is this "cat" hash generated, when, and what rules does it follow?

Then my followup question would be, since rails 4 forces you to whitelist parameters before you use them. I am doing:

params.require(:cat).permit(:name,:hunger,:mood)

How do I also permit the :cute value?

3
You can add the cute param by simply modifying the code you already have that accepts params params.require(:cat).permit(:name,:hunger,:mood,:cute)HolyMoly
it is helpful if you include your code in your question as it makes it easier for folks to see what is going on. I suggest you post your controller, and the form (if you have one) as well as any other relevant code. i do not know how you are inputing the values for the cats when you create them, or how (via a form) but i suspect the problem is in there, probably syntax.HolyMoly
So the solution I found was to remove the require() portion from the params.require(:cat).permit(:name,:hunger,:mood), and simply add :cute to the permit function. My question still remains, why does the cat: attribute get generated in this situation. When I start with the message attributes: {name:"",hunger:"", mood:"",cute:""} why does rails convert them to: {name:"", hunger:"", mood:"", cute:"", cat:{name:"", hunger:"", mood:""}} where does the "cat:{name:"", hunger:"", mood:""}" come from?Curse
More than trying to find a solution to a specific piece of software, I was looking to understand the way Rails 4 generates Parameter objects. haha sorry if I did not make it clear in my question, it's my first time posting on here.Curse
because there is something in your code that is giving you this unexpected result, which is why it would benefit you to post the actual code. it is really hard to see what is going on simply from you explaining the result you are getting.HolyMoly

3 Answers

1
votes

This is down to something called parameter wrapping

It's there as a convenience so that you don't have to submit the root (ie in your case put everything in the user element) but still get to use params[:user]

By default if you have parameter wrapping on for the request format then CatsController will do this for any parameter that matches Cat.attribute_names. You can customise how parameter wrapping is done for a controller (or turn it off, on control which content types trigger it) with the wrap_parameters method, for example

class CatsController < ActionController::Base
  wrap_parameters :cat, include: [:cute]
end

To also include the cute in the list of parameters to wrap and you can then do

params.require(:cat).permit(:name, :hunger, :mood, :cute)
0
votes

You'll be best reading up on HTTP - Difference between GET and POST.


It's not Rails which sends the request, it's plain old HTML.

The difference you're looking at is how your server picks up the POST params, considering GET params are passed through the url...

Note that query strings (name/value pairs) is sent in the URL of a GET request:

Note that query strings (name/value pairs) is sent in the HTTP message body of a POST request

Thus, the base level answer to your question is that you need to append your POST params to the message body of the request. The best example I know of how to do this is with JQuery ajax:

$.ajax({
   url: ...,
   data: {your: key, value: pairs}
});

To answer your other questions, here's the structure you should use:

#app/models/cat.rb
class Cat < ActiveRecord::Base
   #columns id | name | mood | created_at | updated_at
   has_and_belongs_to_many :kittens
          class_name: "Cat", 
          join_table: :kittens, 
          foreign_key: :cat_id, 
          association_foreign_key: :kitten_id
   alias_attribute :born, :created_at #-> allows you to call @cat.born
end

#kittens table
#columns cat_id | kitten_id

You can read up about the self join here: Rails: self join scheme with has_and_belongs_to_many?

This should give you the ability to create a cat, and have him/her assigned as a kitten of another cat:

#app/controllers/cats_controller.rb
class CatsController < ApplicationController
   def new
      @cat = Cat.new
   end
   def create
      @cat = Cat.new cat_params
      @cat.save
   end

   private

   def cat_params
      params.require(:cat).permit(:name, :mood, :kittens)
   end
end

This will give you the ability to have the following:

#app/views/cats/new.html.erb
<%= form_for @cat do |f| %>
   <%= f.text_field :name %>
   <%= f.text_field :mood %>
   <%= f.collection_select :kittens, Cat.all, :id, :name %>
   <%= f.submit %>
<% end %>

This will also allow you to call:

@cat = Cat.find params[:id]
@cat.kittens.each do |kitten|  
   kitten.mood #-> "cute"
0
votes

@Frederick Cheung: thank you so much for the link. Like @Curse, I have been trying to figure out why I get two copies (ie: duplicates) of my JSON HTTP POST params printed in the rails log. Adding

wrap_parameters format: []

at the top of my controller definition gave me

{name:"",hunger:"", mood:"",cute:""}

in my log output instead of

{name:"", hunger:"", mood:"", cute:"", cat:{name:"", hunger:"", mood:""}}

because I realized I had

wrap_parameters format: [:json]

in config/initializers/wrap_parameters.rb. Having wrap_parameters enabled can almost double log size if large JSON documents are being POSTed to a Rails 3 server...