32

I'm getting the error:

<NoMethodError: undefined method `read_attribute_for_serialization' for #<Record::ActiveRecord_Relation:0x007faa7cfe3318>>

The error is on third line of the snippet.

  def artist
    @records = Record.where('artist_id = ' + params[:artist_id])
    render :json => @records, serializer: RecordSummarySerializer
  end

This is an alternate serializer for the controller RecordsContrller. The other one, 'RecordsSerializer, works fine.

A search on this error brought up a few solutions. Some were unclear as to where one would add code. Another suggested adding:

alias :read_attribute_for_serialization :send

to the class where the error occurs. It didn't work for me. I also looked through the gem documentation. I found an example of the use of serializer:. It was clear any other code was needed.

I'm using Ruby 2.3.0 and Rails 5.0.0.

Controller

class RecordsController < ApplicationController
...
  def index
    @records = Record.order('title')
    render :json => @records
  end

  def artist
    @records = Record.where('artist_id = ' + params[:artist_id])
    render :json => @records, serializer: RecordSummarySerializer
  end
...
end

Model

class Record < ActiveRecord::Base
  belongs_to :artist
  belongs_to :label
  belongs_to :style
  has_many :tracks
  has_many :credits

  validates :title, presence: true
  validates :catalog, presence: true
end

record_summary_serializer.rb

include ActiveModel::Serialization
class RecordSummarySerializer < ActiveModel::Serializer
  attributes :id, :artist_id, :label_id, :style_id, :title, :catalog,
             :recording_date, :penguin, :category
end

record_serializer.rb

class RecordSerializer < ActiveModel::Serializer
  attributes :id, :artist_id, :label_id, :style_id, :title, :catalog,     :alternate_catalog,
         :recording_date, :notes, :penguin, :category
  has_one :artist
  has_many :tracks
end

Schema for Records

  create_table "records", unsigned: true, force: :cascade, options:   "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
    t.string   "title",             limit: 50
    t.string   "catalog",           limit: 20,                          null: false
    t.string   "alternate_catalog", limit: 20
    t.integer  "artist_id",                                                          unsigned: true
    t.integer  "label_id",                                                null: false, unsigned: true
    t.integer  "style_id",                                                           unsigned: true
    t.datetime "recording_date"
    t.text     "notes",             limit: 4294967295
    t.string   "penguin",           limit: 50
    t.string   "category",          limit: 50,         default: "jazz"
    t.index ["artist_id"], name: "RecordHasOneArtist", using: :btree
    t.index ["label_id"], name: "RecordHasOneLabel", using: :btree
    t.index ["style_id"], name: "RecordHasOneStyle", using: :btree
  end

I just noticed, the primary key, id, doesn't appear in the schema. I see it in Sequel Pro when I view the table structure.

Update

I added the the code suggested by @JMazzy. I now get the error:

<NoMethodError: undefined method `id' for #<Record::ActiveRecord_Relation:0x007faa8005a9d8>\nDid you mean?  ids>
curt
  • 4,422
  • 3
  • 42
  • 61
  • 2
    Remember not to leave `Record.where('artist_id = ' + params[:artist_id])` in production, as it might open your app to SQL Injection. Use: `Record.where(artist_id: params[:artist_id])` or `Record.where('artist_id = ?', params[:artist_id])` instead. – Leonel Galán Oct 06 '16 at 14:25

6 Answers6

52

I had the same error and found changing serializer: to each_serializer: in the controller solved the issue.

Controller

class RecordsController < ApplicationController
...
  def artist
    @records = Record.where('artist_id = ' + params[:artist_id])
    render :json => @records, each_serializer: RecordSummarySerializer
  end
...
end

record_summary_serializer.rb - can remove the include line

class RecordSummarySerializer < ActiveModel::Serializer
  attributes :id, :artist_id, :label_id, :style_id, :title, :catalog,
             :recording_date, :penguin, :category
end

From active_model_serializers documentation.

Updated link. thanks Lowryder

Kem
  • 646
  • 9
  • 10
  • 6
    changing `serializer` to `each_serializer` worked for me. Thanks! – FutoRicky Nov 17 '16 at 17:00
  • 3
    Doc link is dead, here a new one: https://github.com/ggordon/active_model_serializers/blob/master/README.md#2-for-an-array-resource – Pascal Oct 01 '17 at 13:37
31

You need to add include ActiveModel::Serialization to your model. Extended serialization is not included by default in ActiveModel. See this answer on GitHub.

JMazzy
  • 442
  • 3
  • 5
  • Just where do you mean when you say to my model? I added line to record.rb. I then got a different error. See the original question for more information – curt Jul 21 '16 at 18:34
  • I have a simple class (not an ActiveRecord one) and adding `include ActiveModel::Serialization` solved the error! Thank you! – ZedTuX Sep 06 '21 at 18:57
13
<NoMethodError: undefined method `read_attribute_for_serialization'

This error can happen when the object being serialized is nil. Consider print debugging the object you are serializing to check if it is nil:

def show
  product = Product.find_by(id: params[:id])
  p "Is product nil? #{product.nil?}"
  render json: product
end

If the object (product in the snippet above) is nil, then you may see the NoMethodError reported above due to nil.read_attribute_for_serialization being called.

Eliot Sykes
  • 9,616
  • 6
  • 50
  • 64
1

I fixed my immediate problem by changing the custom serializer to RecordDetailSerializer and calling it from my show method in the controller. When I removed the include @JMazzy suggested it still worked. There is still something screwy. If I use the custom method in the index method:

def index
  @records = Record.order('title')
  render :json => @records, serializer: RecordDetailSerializer
end

I get the error on the missing id shown at the bottom of my question above. However, when I use it in my show method:

def show
  @record = Record.find(params[:id])
  render :json => @record, serializer: RecordDetailSerializer
end

If I remove the problem field, the next field in the list becomes the problem. This no a longer a problem for me as I can always limited custom serializers to and individual element.

curt
  • 4,422
  • 3
  • 42
  • 61
1

Adding serializable_hash and directly initializing the constructor, i.e.

def artist
  @records = Record.where('artist_id', params[:artist_id])
  render json: RecordSummarySerializer.new(@records).serializable_hash 
end

...solved it for me.

Phoghelius
  • 476
  • 4
  • 5
0

I got this error when trying to serialize an array of records in a view template.

I had:

InviteSerializer.new(@invites) }                   # throws errror

I needed:

@invites.map { |i| InviteSerializer.new(i) }       # works!
duhaime
  • 25,611
  • 17
  • 169
  • 224