By using this website, you agree to the storing of cookies on your device to enhance site navigation, analyze site usage, and assist in our marketing efforts. View our Privacy Policy for more information.

Magic Behind Rails Query Params

Ever wondered why Rails will sometimes automatically include params under the model name? Here’s how it happens.
3
min. read
Oct 7, 2021

I’ve been using Rails for a long time and never paid attention to one of its cool aspects. You probably noticed that sometimes Rails will automatically include params under the model name. But have ever wondered when and how it happens? I didn’t until I was forced to understand the whole stack the request goes through when I was investigating memory problems (something I’ll write about in another post).

Here’s a simple example:

The request is (copied from a spec):

First, as you see, we don’t wrap params in configuration. Also notice that enabled_projects is repeated twice.

It ain’t magic. It’s ActionController::ParamsWrapper at work ❤ .

It’s easy to forget, but when you scaffold your project, Rails creates config/initializers/wrap_parameters.rb with a default configuration like:

That means when ActionController is loaded, it should call wrap_parameters format: [:json] by default.

Yes, that’s correct - params are wrapped only for JSON requests by default. (In case you’re scratching your head as to why Rails sometimes wraps params and sometimes doesn’t.)

So if you submitted the same request, but for HTML, variant the wrapping would not kick in:

It's handled as:

So why wrap params in the first place?

It’s mostly for ease of use. You can then have code as simple as:

And you don’t need to worry that some random params will be put inside configuration:

The code above still produces:

ActionController::ParamsWrapper tries to guess which model the controller is responsible for, and what attributes are permitted.

You can control that with additional params, for example (in configurations_controller.rb):

Will include attribute_that_is_not_in_the_model into configuration:

Unfortunately, when using include you need to list all attributes that are allowed. You don’t extend the list as the name include would suggest.

Is that a good pattern (using params.require(:configuration).permit!)?

It has its benefits, but we currently don’t use it. We err on the safe side and have an additional security layer (filtering out unwanted params before we pass them to update):

Maybe it’s overly redundant, but it’s our current pattern. Which pattern is better for you?

You can also disable ActionController::ParamsWrapper for a controller with:

Sometimes the tools we use everyday have more features than we stop to appreciate. What’s your favorite thing about Rails?

Paweł Niewiadomski

Join us on Social Media!