user3382176
user3382176

Reputation: 43

Rails Parameters are not passed to 'params' variable

I'm making a application by using Rails 4.

Basically, I have two models, Card and CardCollection. CardCollections has_many Cards. I implemented this relationship with nested attributes.

class Card < ActiveRecord::Base
  belongs_to :card_collection
end

class CardCollection < ActiveRecord::Base
  has_many :uploaded_cards, class_name: 'Card', dependent: :destroy
  accepts_nested_attributes_for :uploaded_cards, allow_destroy: true
end

And form is like below.

<%= form_for @card_collection, options do |f| %>
  <%= f.fields_for :uploaded_cards do |ff| %>
    <%= render 'cards/card_form', f: ff %>
  <% end %>
<% end %>

When I put some data in the form and press the submit button, I can see that parameters are passing correctly to the server. (According to the server log)

Parameters: {"utf8"=>"✓", "authenticity_token"=>"YFn7RsQzR4Sq1bjR+mOhCASWmeqAxw9B30oAHCL
9qzk=", "card_collection"=>{"collectible_id"=>"16", "collectible_type"=>"Scrapbook", "uplo
aded_cards_attributes"=>{"0"=>{"_destroy"=>"false", "image_url"=>"https://s3-ap-northeast-
1.amazonaws.com/bucketplace/uploads/image_content/src/2833/2b67142422.jpg", "place"=>"1",
"style"=>"0", "description"=>"생각을 짓다......", "id"=>"97"}, "1"=>{"image_url"=>"http://
hodoli.s3.amazonaws.com/uploads%2F14_06_02_17_17_56_62276930_468703135.jpg", "place"=>"7",
 "style"=>"1", "description"=>"123"}}}, "project_cover_image_url"=>"", "commit"=>"확인", "
id"=>"16"}

But when I really print 'params' variable in program code, params[:card_collection][:uploaded_cards_attributes] hash(nested attributes part) is suddenly missing. So the printed params value is like below.

"params=  {\"utf8\"=>\"✓\", \"_method\"=>\"patch\", \"authenticity_token\"=>\"YFn7RsQzR4Sq
1bjR+mOhCASWmeqAxw9B30oAHCL9qzk=\", \"card_collection\"=>{\"collectible_id\"=>16, \"collec
tible_type\"=>\"Scrapbook\"}, \"project_cover_image_url\"=>\"\", \"commit\"=>\"확인\", \"a
ction\"=>\"update\", \"controller\"=>\"card_collections\", \"id\"=>\"16\"}"
Unpermitted parameters: collectible_id, collectible_type

I don't know why this happen. I think it doesn't have relationship with 'Strong Parameter' because problem is in the Rails-side parameter parsing part. (as I think) Please help me to find the exact problem and solution.

EDIT

this is my controller code

class CardCollectionsController < ApplicationController
before_action :set_card_collection, only: [:do_upload, :edit, :update]
before_action :authenticate_user!, only: [:upload, :do_upload, :edit, :update]
before_action :authenticate_card_collection_owner!, only: [:do_upload, :edit, :update]

# GET /card_collections/upload_select
def upload_select
end

# GET /card_collections/upload
def upload
    @card_collection = CardCollection.new
end

def do_upload
    respond_to do |format|
        p card_collection_params
        if @card_collection.update(card_collection_params)
            format.html do
                @card_collection.collectible.update(cover_image_url: params[:project_cover_image_url]) unless params[:project_cover_image_url].blank?
                redirect_to @card_collection.collectible, notice: 'CardCollection was successfully updated.'
            end
            format.json { head :no_content }
        else
            @card_collection = CardCollection.new(card_collection_params)
            format.html { render 'upload', alert: '에러' }
            format.json { render json: @card_collection.errors, status: :unprocessable_entity }
        end
    end
end

# GET /card_collections/1/edit
def edit
end

# PATCH/PUT /card_collections/1
# PATCH/PUT /card_collections/1.json
def update
    respond_to do |format|
        if @card_collection.update!(card_collection_params)
            format.html do
                @card_collection.collectible.update(cover_image_url: params[:project_cover_image_url]) unless params[:project_cover_image_url].blank?
                redirect_to @card_collection.collectible, notice: 'CardCollection was successfully updated.'
            end
            format.json { head :no_content }
        else
            format.html { render 'edit', alert: '에러' }
            format.json { render json: @card_collection.errors, status: :unprocessable_entity }
        end
    end
end

private

# Use callbacks to share common setup or constraints between actions.
def set_card_collection
    if params[:id]
        @card_collection = CardCollection.find(params[:id])
        params[:card_collection] = {collectible_id: @card_collection.collectible_id, collectible_type: @card_collection.collectible_type}
    elsif params[:card_collection][:collectible_id].blank?
        @card_collection = CardCollection.new(card_collection_params)
        flash.now[:alert] = '스크랩북을 선택해 주세요'
        render 'upload'
    else
        @card_collection = CardCollection.find_by_collectible_id_and_collectible_type(params[:card_collection][:collectible_id], params[:card_collection][:collectible_type])
    end
end

# Never trust parameters from the scary internet, only allow the white list through.
def card_collection_params
    params.require(:card_collection).permit(CardCollection.param_names + [uploaded_cards_attributes: Card.param_names])
end

def authenticate_card_collection_owner!
    redirect_to previous_url if current_user != @card_collection.user
end
end

class CardCollection < ActiveRecord::Base
def self.param_names
    [:id, :name, :description]
end
end

class Card < ActiveRecord::Base
    def self.param_names
    [:id, :image_url, :description, :place, :style, :source, :width, :height]
end
end

Solved Finally, I solved the problem thanks to @Rich Peck. The real problem was about route part of Rails. I was identifying specific record by two ways in one controller as you can see in 'set_card_collection' method.

resources :card_collection, only: [:edit, :update] do
  get :upload, on: :collection
  patch :do_upload, on: :collection 
end

Original route.rb file was look like above. I structured my route file (do not specify CardCollection id when user connect to upload page) like that to give flexibility to user choosing CardCollection dinamically in upload page. But to connect edit page, user had to specify record id such as /card_collection/1/edit because it is Rails's default behavior.

Then I customized 'set_card_collection' method in the controller.

if params[:id]
        @card_collection = CardCollection.find(params[:id])
        params[:card_collection] = {collectible_id: @card_collection.collectible_id, collectible_type: @card_collection.collectible_type}
    elsif params[:card_collection][:collectible_id].blank?
        @card_collection = CardCollection.new(card_collection_params)
        flash.now[:alert] = '스크랩북을 선택해 주세요'
        render 'upload'
    else
        @card_collection = CardCollection.find_by_collectible_id_and_collectible_type(params[:card_collection][:collectible_id], params[:card_collection][:collectible_type])

Still, I don't know the EXACT cause, but using default :edit and :update route setting caused the problem that nested parameters would not be passed correctly. So after I changed my route.rb file like below as unifying the way identifying a record, the problem was solved.

resources :card_collections, only: [] do
    get :upload_select, on: :collection
    get :upload, on: :collection
    post :upload, on: :collection, to: :do_upload
    get :edit, on: :collection
    patch :update, on: :collection
    put :update, on: :collection
end

Upvotes: 4

Views: 8426

Answers (2)

morgler
morgler

Reputation: 1809

In my case I had to add the MIME type in order to see all params in Rails. The Rails logs were kind of warning me about this by making me aware, that the request came in as "application/vnd.api+json".

I needed to add the following line to my config/initializers/mime_types.rb:

Mime::Type.register "application/vnd.api+json", :json

See https://github.com/rails/rails/issues/28564 for this issue.

Upvotes: 0

Richard Peck
Richard Peck

Reputation: 76774

There are a number of issues which could cause this

--

Error

I see your system is correctly generating the uploaded_cards_attributes, which is the biggest hurdle to cross

This means the problem is either going to be with your strong params (which I doubt because the params hash is available in its entirety in your controller (it's the model where you want to restrict access); or how you're managing the params inside the controller

I don't know how to fix your problem, but it's likely not going to be a Rails issue

--

Remedy

I would initially look at how you're passing the params

Looks like you're using the edit / update method (by use of the PATCH verb). This could be a problem, as Rails may not pass the data for an already-created object.

I would initially test using a new form - to see if you can create a new object with nested attributes. If you can do this, I would then test using nested resources in your routes & edit the CardCollection object directly:

#config/routes.rb
resources :card_collections do
   resources :card_uploads
end

This will allow you to do something like:

#app/controllers/card_collections_controller.rb
class CardCollectionsController < AppicationController
   def edit
       @card_collection = CardCollection.find params[:card_collection_id]
       @card_upload = @card_collectiom.new #-> might need to change
   end
end

#app/views/card_collections/edit.html.erb
<%= form_for @card_upload do |f| %>
    <%=  f.file_field :image %>
    <%=  f.submit %>
<% end %>

Bear in mind, that is a test - so I will not use that in production; I just want to see why you're not receiving the params.

--

Attributes

btw we use the attribute_names method to make our Strong Params dry:

def your_params
    parameters = Model.attribute_names - %w(id created_at updated_at)
    params.require(:parent).permit(parameters)
end

Upvotes: 1

Related Questions