Reputation:
I'm looking to render an index of all articles along with a full article via JSON in my rails app, but I'm having a little trouble figuring out how to do it.
Here is my controller now:
if params[:id]
@article = Article.find(params[:id])
else
@article = Article.published.not_draft.by_recent.first
end
respond_to do |format|
format.js { render :json => @article.to_json(
:except => [ :created_at, :updated_at, :draft, :id, :publish ],
:include => {
:comments => {
:only => [:body]
}
}),
:callback => params[:callback]}
end
What I'd like to do in the response is add an index of all articles, like so:
@index = Article.find(:all, :select => 'id, title')
The only way I've been able to do it, is put both the index and article into a hash or array and then put that to JSON.
@response = { :item => @article, :index => @index }
Full code with both:
@index = Article.find(:all, :select => 'id, title')
if params[:id]
@article = Article.find(params[:id])
else
@article = Article.published.not_draft.by_recent.first
end
@response = { :item => @article, :index => @index }
respond_to do |format|
format.js { render :json => @response.to_json(), :callback => params[:callback]}
end
This would be fine, except now I cannot specify :include
or :except
and get it to render properly.
Upvotes: 18
Views: 36019
Reputation: 2576
I would recommend overloading the attributes method to return an alternat hash that will be automatically used in to_json output.
class Article
def attributes
{ ... } # define your hash that you want to return at the '...'
end
end
To me this seems much simpler than mucking around with to_json directly.
Upvotes: 0
Reputation: 169
Thanks for the question, I am able to customize my json format for a model with several associations.
render json: @posts.to_json(
:except => [ :created_at, :updated_at, :user_id ],
:include => {
:user => {:only => [:email, :phone]},
:location => {:only => [:title, :lat, :lon, :street, :city, :state, :zipcode]},
:uploads => {:only => [:image]}
}
)
Upvotes: 0
Reputation: 2467
(Please accept an answer)
I think that the link that nirvdrum gave holds your answer. I only answer because nobody has mentioned encode_json
.
In your case you should only be dealing with as_json
. Either by building a hash (with various calls to as_json
) and sending that to render :json => ...
(without the call to to_json
) or by simply implementing as_json
on your model and letting rails do all the work. (But I suspect you'll need the former.)
If you really need some fancy js in your rendered response then you can implement encode_json
in your classes (again, not to_json
). For example:
class JsEmptyClosure
def encode_json(*args)
"jQuery[\"noop\"] || function(){}"
end
def as_json(*args) self end
end
This will now respond to to_json
with valid js (but note it's not actually json).
Upvotes: 1
Reputation: 2319
You hint at the solution in your question. You most likely want to build up a hash to render to JSON. The preferred way of doing this now is by providing an implementation for the as_json method. as_json provides a formal means of customizing to_json output by building up a hash containing the data you wish to encode.
A more thorough treatment of how as_json and to_json interact can be found on Jonathan Julian's weblog.
Upvotes: 28
Reputation: 450
to_json has a :method option that includes the result of any method you name, you could define a method on that model that returns the additional data you want in your JSON.
Upvotes: 1
Reputation:
Just to be clear the code above works with the :include and :except. And by works I mean it doesn't throw an error. The problem is it includes comments for every article in the index. I'm only looking to include comments for the :item and not any of the articles listed in the index.
I couldn't get nesting to work as a hash or an OpenStruct object.
Nesting on :include throws an error, nesting on :except doesn't throw an error, but nothing is filtered out, :created_at, etc. still show up.
...
@response = { :item => @article, :index => @index }
format.js { render :json => @response.to_json(
:except => {:item => [ :created_at, :updated_at, :draft, :id, :publish ]},
:include => { :item => {
:comments => {
:only => [:body]
}
}}),
:callback => params[:callback]}
end
Upvotes: 2
Reputation: 21950
You should be able to nest the :include
, :except
, etc. like so:
:except => {:item => [ :created_at, :updated_at, :draft, :id, :publish ]}...
If that doesn't work, make it an object (e.g. an OpenStruct) instead of a hash.
-- Markus
Upvotes: 1