2
votes

There are several questions for strong params, but I couldn't find any answer for achieving my goal. Please excuse any duplicates (and maybe point me in the right direction). I'm using strong params in a model that has several 'has_one' associations and nested attributes with 'accepts_attributes_for'.

In my routes I have: (updated for better understanding)

  resources :organisations do
    resources :contact_details
  end

So, i.e. for one associated model I have to use

def organisation_params
  params.require(:organisation).permit(:org_reference, :supplier_reference, :org_type, :name, :org_members, :business, :contact_person, contact_detail_attributes: [:id, :contactable_id, :contactable_type, :phone, :fax, :mail, :state, :province, :zip_code, :street, :po_box, :salutation, :title, :last_name, :first_name, :description])
end

This works, but I have to retype all my permitted params for each associated model. When I modify my permitted attributes for contact_details , I have to change it in several locations (every model that has the polymorphic association).

Is there a way to get the parameter whitelist of contact_details and include it into the parent whitelist?

Something like:

def organisation_params
  my_params = [:org_reference, :supplier_reference, :org_type, :name, :org_members, :business, :contact_person]
  contact_params = #get permitted params, that are defined in contact_details_controller
  params.require(:organisation).permit(my_params, contact_params)
end

I don't want to workaround security, but I had already defined the permitted attributes for the contact_details and don't want to repeat it in every associated "parent" model (because it's exhausting and very prone to stupid mistakes like omitting one attribute in one of several parent models).

2
What do you mean with "nested model"?mdesantis
I have one "parent" model, that accepts attributes for another model ("nested model"). Perhaps nested resource is a better term?mrong
Oh ok; I think is better "associated model", or "associated record"mdesantis
Ok, I changed it to associated model, hope it clarifies the scenariomrong

2 Answers

1
votes

Use a method defined inside ApplicationController, or a shared module:

  1. ApplicationController:

    class ApplicationController
      def contact_details_permitted_attributes
        [:id, :contactable_id, :contactable_type, ...]
      end
    end
    
    class ContactDetailsController < ApplicationController
      def contact_details_params
        params
          .require(contact_details)
          .permit(*contact_details_permitted_attributes)
      end
    end
    
    class OrganisationsController < ApplicationController
      def organisation_params
        params
          .require(:organisation)
          .permit(:org_reference, ..., 
                  contact_detail_attributes: contact_details_permitted_attributes)
      end
    end
    
  2. Shared module:

    module ContactDetailsPermittedAttributes
      def contact_details_permitted_attributes
        [:id, :contactable_id, :contactable_type, ...]
      end
    end
    
    class ContactDetailsController < ApplicationController
      include ContactDetailsPermittedAttributes
    
      def contact_details_params
        params
          .require(contact_details)
          .permit(*contact_details_permitted_attributes)
      end
    end
    
    class OrganisationsController < ApplicationController
      include ContactDetailsPermittedAttributes
    
      def organisation_params
        params
          .require(:organisation)
          .permit(:org_reference, ..., 
                  contact_detail_attributes: contact_details_permitted_attributes)
      end
    end
    

Rails has even dedicated directories for shared modules, concerns inside app/controllers and app/models; indeed, in your case you should use app/controllers/concerns

0
votes

I don't see why not. In your ApplicationController you could have

def contact_attributes
  [:id, :contactable_id, :contactable_type, :phone, :fax, 
   :mail, :state, :province, :zip_code, :street, :po_box, 
   :salutation, :title, :last_name, :first_name, :description]
end

Then in your organisation_params

def organisation_params
  my_params = [:org_reference, :supplier_reference, :org_type, :name, :org_members, :business, :contact_person]

  params.require(:organisation).permit(*my_params, contact_detail_attributes: contact_attributes)
end

In some other location you might do...

def contact_params
params.require(:contact).permit(*contact_attributes)
end