Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails Strong Parameters: How to accept both model and non-model attributes?

I have a form to create a user model with all its usual attributes, but I am also passing a lot of non-model attributes that based on which I will create further stuff in my controller action.

My question is how I can tell Strong Parameters to accept both the user data, AND this other data that is not related to the user db wise?

To illustrate, my form could like this (submit button deleted for brievity):

<%= form_for @user do |f| %>
  <%= f.text_field 'attribute1' %>
  <%= f.text_field 'attribute2' %>
  <%= f.text_field 'attribute3' %>

  <%= text_field_tag 'attribute_not_on_user_model1' %>
  <%= text_field_tag 'attribute_not_on_user_model2' %>
<% end %>

How can I use strong parameters to do this? I tried this:

params.require(:user).permit(:attribute1, :attribute2 , :attribute3, :attribute_not_on_user_model1, 
  attribute_not_on_user_model2)

And also this:

params.require(:user).permit(:attribute1, :attribute2 ,     
  :attribute3).require(:attribute_not_on_user_model1, 
  attribute_not_on_user_model2)

Both did not work. I am aware that I could do attr_accessor in the user, but I have a growing list of attributes in this form that are not related to the user model per se (but are nonetheless essential to the creation of the user model and its subsequent related models). We could debate that this is not the best way to do this (A form object comes to mind), but for the moment I want to see if Strong Parameters can help me here.

like image 769
n_x_l Avatar asked Nov 02 '14 20:11

n_x_l


2 Answers

user model attributes are stored inside the :user hash, whereas the non-user attributes are accessible directly at the params level.

If you inspect your params Hash, you will notice that it is constructed in the following way

{ user: { attribute1: "value", attribute2: value, ... }, attribute_not_on_user_model1: "value", attribute_not_on_user_model2: "value" }

Consequently, the call

params.require(:user)

will automatically ignore any other param that is not part of the user node. If you want to include also the other params, you either compose the hash, or update the view to inject the params in the form.

Injecting the parameter on the form will cause the params to be part of the same :user node. This approach normally works very well with virtual attributes (despite the concepts are not linked each other).

<%= form_for @user do |f| %>
  <%= f.text_field 'attribute1' %>
  <%= f.text_field 'attribute2' %>
  <%= f.text_field 'attribute3' %>

  <%= text_field_tag 'user[attribute_not_on_user_model1]' %>
  <%= text_field_tag 'user[attribute_not_on_user_model2]' %>
<% end %>

The other solution would be something like

def some_params
  hash = {}
  hash.merge! params.require(:user).slice(:attribute1, :attribute2, :attribute3)
  hash.merge! params.slice(:attribute_not_on_user_model1, 
  attribute_not_on_user_model2)
  hash
end

The solution, however, really depends on how you will user those params later. If all these params are sent as a single Hash, then you may want to compose the single hash, but in that case you may also want virtual attributes.

The point is that, without a real use case, the question itself is quite non-sense. StrongParameters is designed to filter a group of parameters passed to a bulk-create or bulk-update action. Generally, this means you have a model.

If you design a custom method, or if you have non-model methods, StrongParameters whitelisting may not have any sense, given you have control over the method you are writing and invoking.

like image 149
Simone Carletti Avatar answered Sep 22 '22 08:09

Simone Carletti


There are many ways to do this and one way is with accecpts_nested_attributes_for: http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

like image 21
ruby_newbie Avatar answered Sep 22 '22 08:09

ruby_newbie