Search code examples
ruby-on-railsruby-on-rails-4serializationnested-attributesactive-model-serializers

rails ams, Nested models undefined method `' for nil:NilClass


I have the following models:

class Appeal < ActiveRecord::Base
  belongs_to :applicant, :autosave => true
  belongs_to :appealer, :autosave => true
end

class Appealer < ActiveRecord::Base
  has_many :appeals, :autosave => true
end

class Applicant < ActiveRecord::Base
  has_many :appeals
end

What I want is for every appealer to hold a reference to the applicant of his last appeal

so I modified Appealer model to:

class Appealer < ActiveRecord::Base
  has_many :appeals, :autosave => true

  def last_applicant
    return self.appeals.last.applicant
  end
end

but I get the error:

undefined method `applicant' for nil:NilClass

what is strange that if I debug this (via RubyMine - Evaluate Expression) I can get the applicant.

If I try to get the last appeal:

class Appealer < ActiveRecord::Base
  has_many :appeals, :autosave => true

  def last_appeal
    return self.appeals.last
  end
end

everything works.

Im working with active-model-serializer, tried to do it also in the serializer (I actually need this value on a specific call - not allover the model) but it also didnt worked with the same errors.

the AMS code:

class AppealerTableSerializer < ActiveModel::Serializer
  attributes :id, :appealer_id, :first_name, :last_name, :city
  has_many :appeals, serializer: AppealMiniSerializer

  def city
    object.appeals.last.appealer.city
  end

end

MY QUESTION: How can I get the nested object attributes in my JSON? What am I doing wrong?

EDIT: My controller call:

class AppealersController < ApplicationController
  def index
    appealers = Appealer.all
    render json: appealers, each_serializer: AppealerTableSerializer, include: 'appeal,applicant'
  end
end

I've tried with and without the include, still not works


Solution

  • Maybe I am missing something, because this looks like you have Appealer record that doesn't have any appeals yet.

    In such case, this code

    def last_appeal
      return self.appeals.last
    end
    

    Will return nil, which won't raise any errors. But if you call this

    def last_applicant
      return self.appeals.last.applicant
    end
    

    return self.appeals.last is nil and you try to call applicant method on nil object instead of Appeal object.

    To fix it just add check for nil

    class Appealer < ActiveRecord::Base
      has_many :appeals, :autosave => true
    
      def last_applicant
        last = self.appeals.last
    
        if last.nil?
          return nil
        else
          return last.applicant
        end
      end
    end