I have a model with data that should never be included when it is rendered as json. So I implemented the class' as_json method to behave appropriately. The problem is when other models with associations with this model render json, my custom as_json is not being called.
class Owner < ActiveRecord::Base has_one :dog def as_json(options={}) puts "Owner::as_json" super(options) end end class Dog < ActiveRecord::Base belongs_to :owner def as_json(options={}) puts "Dog::as_json" options[:except] = :secret super(options) end end
Loading development environment (Rails 3.0.3)
ruby-1.9.2-p136 :001 > d = Dog.first
=>#<Dog id: 1, owner_id: 1, name: "Scooby", secret: "I enjoy crapping everwhere"
>
ruby-1.9.2-p136 :002 > d.as_json
Dog::as_json
=> {"dog"=>{"id"=>1, "name"=>"Scooby", "owner_id"=>1}}
ruby-1.9.2-p136 :004 > d.owner.as_json(:include => :dog)
Owner::as_json
=> {"owner"=>{"id"=>1, "name"=>"Shaggy", :dog=>{"id"=>1, "name"=>"Scooby", "owner_id"=>1, "secret"=>"I enjoy crapping everwhere"}}}
Thanks for the help
This is a known bug in Rails. (The issue is marked closed due to the migration to Github issues from the previous bug tracker, but it's still a problem as of Rails 3.1.)
As acknowledged above, this is an issue with the Rails base. The rails patch here is not yet applied and seems at least slightly controversial, so I'm hesitant to apply it locally. Even if applied as a monkey patch it could potentially complicate future rails upgrades.
I'm still considering RABL suggested above, it looks useful. For the moment, I'd rather not add another view templating language into my app. My current needs are very small.
So here's a workaround which doesn't require a patch and work for most simple cases. This works where the association's as_json
method you'd like to have called looks like
def as_json(options={}) super( <... custom options ...> ) end
In my case I've got Schedule
model which has many Events
class Event < ActiveRecord::Base # define json options as constant, or you could return them from a method EVENT_JSON_OPTS = { :include => { :locations => { :only => [:id], :methods => [:name] } } } def as_json(options={}) super(EVENT_JSON_OPTS) end end class Schedule < ActiveRecord::Base has_many :events def as_json(options={}) super(:include => { :events => { Event::EVENT_JSON_OPTS } }) end end
If you followed the guideline that anytime you :include
an association in your as_json()
methods, you define any options you need as a constant in the model to be referenced, this would work for arbitrary levels of associations. NOTE I only needed the first level of association customized in the above example.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With