10

I'm running into an issue where I'm working with the as_json method, and how to efficiently return the object in JSON AND it's belongs_to object as JSON as well, where the belongs_to object has its own belongs_to object. Code would probably explain it better.

The Not-Working Way

Alert class

class Alert < ActiveRecord::Base
    belongs_to :message
    # for json rendering
    def as_json(options={})
        super(:include => :message)
    end
end

Message class

 def as_json(options={})
    super( methods: [:timestamp, :num_photos, :first_photo_url, :tag_names],
           include: { camera: { only: [:id, :name] },
                      position: { only: [:id, :name, :address, :default_threat_level ]},
                      images: { only: [:id, :photo_url, :is_hidden]} })
  end

The problem with this first set up is that when I have an Alert object and call

alert.as_json()

I get all the attributes from Alert and all the attributes from Message, but none of the other attributes from Message that I want, like Camera, Position, etc.

Here's the "It's Working, But Probably Not Proper Design Way"

Alert Class

class Alert < ActiveRecord::Base

    belongs_to :message

    # for json rendering
    def as_json(options={})
        super().merge(:message => message.as_json)
    end
end

Messages Class

  # for json rendering
  def as_json(options={})
    super( methods: [:timestamp, :num_photos, :first_photo_url, :tag_names])
          .merge(:camera => camera.as_json)
          .merge(:position => position.as_json)
          .merge(:images => images.as_json)
  end

In this 2nd setup, I get all of Messages's nested attributes like I want.

My question, am I missing some Rails Convention to do this properly? It seems like there would/should be an easier way.

3 Answers 3

7

The best answer for me was using serializable_hash. @kikito touched on this in his comment, but there was a typo that prevented it from working. It's not serialized_hash, it's serializable_hash.

Literally just find + replace as_json with serializable_hash and this bug goes away. (It's still not fixed in today's Rails 4.0.2). You also get the benefit of having an easier time implementing an XML API later (some people still use those!).

Sign up to request clarification or add additional context in comments.

Comments

3

Which version of Rails are you using? This is a known bug in older versions of Rails, supposedly fixed with this pull request. Your syntax looks right to me, so perhaps this is your problem?

As an aside, you may also want to checkout the new active_model_serializers from Jose Valim (Rails core member). It may at least enable you to work around your issue in a more elegant manner.

3 Comments

Well it looks like they fixed it with the pull request, but never pulled it into a version. Thanks for the link, it cleared up a few issues.
For the record, what I got from that PR is: 1) try to use a specific gem for the json api (I favor Active Model Serializers) 2) If you don't want/ can't do that, override serialized_hash instead of as_json in all your models. Just replace as_json by serialized_hash. Your models will correctly render included dependencies.
thanks kikito, minor correction, it's serializable_hash
1

I would recommend you to take a look at RABL (stands for Ruby API Builder Language) gem (railscast, github). It offers you a DSL for defining the structure of your JSON (and also XML) response in templates (like Haml or CoffeeScript does). It also supports partials.

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.