1

I have a form that I'm creating with an ENORMOUS list of questions. Rather than hard-code all these out in html.

I'm planning on storing them in a table called questions and populating a questionnaire view by looping through the table with a script that pulls both the text of the question from one column, and its corresponding parameter from another.

The answers will be stored in a Questionnaire model and table. This seems relatively straight-forward, except when dealing with strong params.

So I have the model Question, and table questions. Which contains the columns the_questions and the_parameters.

The questions contained in that table correspond to fields for the model Questionnaire. I would like to query the database to get an array of the parameters and then drop them into the strong params in the questionnaire controller.

So rather than:

def questionnaire_params
  params.require(:questionnaire).permit(:an, :enormous, :list, :of, :params.....)
end

I'd like to do something like:

def questionnaire_params
  @my_params=Questions.the_parameters
  params.require(:questionnaire).permit(@my_params)
end

Is there any way to do this? Am I way out in left field with this idea? Any guidance or admonishment would be appreciated.

3
  • what is the_parameters?? Commented May 28, 2015 at 9:00
  • @user123 the_parameters contains the corresponding parameter that I've defined for each question. I'll try to clarify that in the above. Commented May 28, 2015 at 9:03
  • Please mention method/value of the_parameters? Commented May 28, 2015 at 9:05

6 Answers 6

3

I would like to query the database to get an array of the parameters and then drop them into the strong params in the questionnaire controller.

This would mean, you'll permit all and each attribute to be massassigned.

To do so you just need to use permit! method, which will whitelist every model's attribute:

params.require(:questionnaire).permit! # I don't want to even start discussing downsides of whitelisting all attributes

If you though have some attributes, you don't want to allow to be mass assigned and still want to use array, you can define

def questionnaire_params
  @all_attributes = Model.column_names.map(&:to_sym)
  @not_permitted = [:id, :some_other_attribute]
  params.require(:questionnaire).permit(@all_attributes - @not_permitted)
end
Sign up to request clarification or add additional context in comments.

Comments

2

You may want to reconsider how you are modelling you application. Usually when you need to manipulate strong params, it is an indication of a code smell.

How are questions and answers and questionnaires related? As an alternative, consider this modelling:

  • a questionnaire has_many answers and has_many questions through answers
  • an answer belongs_to a question and belongs_to a questionnaire
  • a question has_many answers

You can get to the question through the answer. Your strong params for questionnaire would then be

params.require(:questionnaire).permit(
  answers_attributes: [:id, :question_id, :text]
)

4 Comments

I didn't see your answer before writing mine, good to see that someone else took the the reasonable approach in questioning what he was doing instead of advocating stupid ways to skirt mass assignment protection.
I think you make a good point here. Your modeling is quite correct but I believe it implies that all answers would be of a particular type, to be saved to an 'answer_text' column in the 'answers' table? Since many of the answers consist of decimals, integers, dates etc, I was planning on giving each answer its own column in the table. Is there any way to work with that strategy, given your more elegant approach/modeling? Or am I misunderstanding something?
Hmm, not faced this question before. Off the top of my head, you could model the Answer table to have different columns for different answer types - a date field, an integer/float field, a text field. In the model validate that only one of those fields is completed.
@margo fair suggestion. To be honest, the application that I'm fulfilling is a little bit more complicated than just a "questionnaire." I'm looking to process corporate law data that has over 100 different variables. It's a situation where the question set can vary for different applications but ultimately we know the answers must address no more/no less than the columns in our table. That's why I simplified the situation to a questionnaire with a whitelist. Thanks for your above answer though; there are other situations where I would do well to reference it.
1

I tried something like (Not sure if i got your question right though)

params.require(:parent).permit(ModelName.column_names)

But then you are defeating the purpose of strong params.

Try

params.require(:parent).permit(Parent.column_names - blacklist_array )

as in

params.require(:parent).permit(Parent.column_names - [:tax_num , :age])

4 Comments

Thanks for the reply. I think you're understanding my question correctly but I don't quite understand how I'm defeating the purpose of strong params (though I'm being told this in the answer below as well). What's the difference between listing out the params :question1, :question2, :question3 and grabbing them from a list that's stored in the database? It's not as though I'm white-listing anything that I wouldn't otherwise be permitting...am I?
@neanderslob Not you I am sorry..i meant the above line of code is :)
Oh! I see I see. Quite right, Parent.column_name could allow people to define things like id, etc which is not so good without the blacklist to weed them out. I get what you're saying now. One more question, did you have to add a colon to each column name to get the above to work?
@neanderslob you dont have to because in this case the arguments are treated as array anyways .. but please verify that
1
params.require(:questionnaire).permit(Questionaire.column_names.map(&:to_sym))

Check if this works for you.

Comments

0

You don't need an enormous list of different parameters to do something as simple as a questionnaire. You're just doing it wrong. And you don't want to stuff a params whitelist in the DB. It would be slow and cause some real headaches when it comes to software versioning.

A fairly standard design for a questionnaire would be something like this:

<%= form_for(:questions, controller: 'questions', action: 'create') do |form| %>
    <% @questions.each do |question| %> 
        <fieldset>
        <%= fields_for(question) do |qf| %>
          <legend><%= question.title %></legend>
          <%= qf.collection_check_boxes :answer_ids, question.answers, :id, :text %>
        <% end %>
        </fieldset>
    <% end %>
<% end %> 

The params:

params.require(:questions).allow(question: [:answer_ids])

Comments

0

ActionController::Parameters takes an array:

$ bin/rails c
> params = ActionController::Parameters.new(user: {name: 'John', age: 22})
=> {"user"=>{"name"=>"John", "age"=>22}} 
> permitted = params.require(:user).permit(:name, :age)
=> {"name"=>"John", "age"=>22} 
> permitted = params.require(:user).permit([:name, :age])
=> {"name"=>"John", "age"=>22} 
> permitted = params.require(:user).permit(*[:name, :age])
=> {"name"=>"John", "age"=>22} 
> permitted = params.require(:user).permit('name', 'age')
=> {"name"=>"John", "age"=>22}

Therefore, you can just pass your @my_params as an array into permit.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.