If I wanted to eagerly load a collection in rails and render it in json, I would have to do something like this.
#photos = #event.photos.to_json(:include =>
{:appearances => {:include => :person}}
)
What if I wanted to map this collection? As you can see it's no longer a collection, but a json string. Prior to this necessary eager loading, I was doing the following:
#photos = #event.photos.map{|photo|
photo['some_funky_stuff'] = photo.funky_calculation
photo
}
But, I can't seem to be able to do the two things together:
#event.photos.map{|photo|
photo['some_funky_stuff'] = photo.funky_calculation
photo
}.to_json(:include =>
{:appearances => {:include => :person}}
)
The above does not show 'appearances' ( the eagerly loaded join record )... How do I do these two together? Many thanks!
You may have the term "eager loading" mixed up a little bit. As previous answers have mentioned, you need to use it on the association for it to be eager loaded. However, when you use :include in the to_json call, you will still end up with the same result, no matter if it is eager or not.
But to answer your question, for the to_json method to both include the appearances and the funky_calculation you can combine it with :methods instead. Try it like this:
#photos = #event.photos.to_json(
:include => {:appearances => {:include => :person}},
:methods => [: funky_calculation]
)
And if you want increased performance (eager loading), then use include on the associations as well:
#photos = #event.photos.includes(:appearances => :person).to_json(
:include => {:appearances => {:include => :person}},
:methods => [: funky_calculation]
)
You could eager load using includes after has_many association
#photos = #event.photos.includes(:appearances => [:person]).to_json
You might want to try using joins() or includes() on photos, instead as an option to to_json().
http://guides.rubyonrails.org/active_record_querying.html#using-array-hash-of-named-associations
Related
I am using the following to output a list of JSON records:
#team.people.to_json(
:include => [:user, :statistics => {:include => :attribute}]).html_safe
However, I would like to only include statistics that have a certain type_id set on them. Essentially a left outer join with the users and the statistics, where the a type_id on the statistic equals some number.
I can think of at least a couple options:
In the Person model, override to_json (or, perhaps better yet, serializable_hash) and do your conditional there.
Instead of {:include => :attribute} do {:methods => :foo} and do your conditional in foo.
Here's an example of where I overrode serializable_hash, if it helps:
def serializable_hash(options={})
options = {
:methods => [
'client',
'services',
'products',
'has_payments',
]}.update(options)
super(options)
end
I could imagine something above options = where you set the methods array to one thing if type_id is the number you're looking for, or to something else otherwise.
In this thread know how to make a eager load in rails, but how to do this nested?
I.e:
# get category, random product and random photo
#category = Category.find(params[:id], :include => random_product, :include => random_photo)
I don't know if I explain...
Thanks in advance.
You can eager load nested associations by giving a hash to the :include option:
#category = Category.find(params[:id], :include => { :random_product => :random_photo})
In my application, Annotations are considered "accepted" if either:
They have been explicitly marked "accepted" (i.e., their state == 'accepted')
They were last updated by a user who has the "editor" role
My question is how to find all accepted explanations with a single DB query. Basically I'm looking for the database-driven version of
Annotation.all.select do |a|
a.last_updated_by.roles.map(&:name).include?('editor') or a.state == 'accepted'
end
My first attempt was
Annotation.all(:joins => {:last_updated_by => :roles}, :conditions => ['roles.name = ? or annotations.state = ?', 'editor', 'accepted'])
But this returns a bunch of duplicate records (adding a .uniq makes it work though)
Changing :joins to :include works, but this makes the query way too slow
Are the results of your first attempt just wrong or do they only need an ".uniq"?
Have you tried
:include => {:last_updated_by => [:roles]}
instead of the join?
or making two queries
#ids = Editor.all(:conditions => ["role = 'editor'"], :select => ["id"]).map{|e|e.id}
Annotation.all(:conditions => ["last_updated_by in (?) or state = ?", #ids.join(","), "accepted"]
is that any faster?
I have a fairly large model and I want to retrieve only a select set of fields for each record in order to keep the JSON string I am building small.
Using :select with find works great but my key goal is to use conditional logic with an associated model. Is the only way to do this really with a lamda in a named scope? I'm dreading that perhaps unnecessarily but I'd like to understand if there is a way to make the :select work with a condition.
This works:
#sites = Site.find :all, :select => 'id,foo,bar'
When I try this:
#sites = Site.find :all, :select => 'id,foo,bar', :include => [:relatedmodel],
:conditions => ["relatedmodel.type in (?)", params[:filters]]
The condition works but each record includes all of the Site attributes which makes my JSON string way way too large.
Thanks for any pointers!
The to_json call supports :except and :only options to exclude/include model fields during serialization.
#sites.to_json(:only => [:name, :foo, :bar])
Call above serializes the Site objects with fields name and location.
#sites.to_json(:only => [:name, :location],
:include => { :relatedmodel => {
:only => [:description]
}
}
)
Call above serializes the Site objects with fields name, and location and contained RelatedModel objects with description field.
Say I have a model Taggable has_many tags, how may I find all taggables by their associated tag's taggable_id field?
Taggable.find(:all, :joins => :tags, :conditions => {:tags => {:taggable_id => [1,2,3]}})
results in this:
SELECT `taggables`.* FROM `taggables` INNER JOIN `tags` ON tags.taggable_id = taggables.id WHERE (`tag`.`taggable_id` IN (1,2,3))
The syntax is incredible but does not fit my needs in that the resulting sql returns any taggable that has any, some or all of the tags.
How can I find taggables with related tags of field taggable_id valued 1, 2 and 3?
Thanks for any advice. :)
UPDATE:
I have found a solution which I'll post for others, should they end up here. Also though for the attention of others whose suggestions for improvement I'd happily receive. :)
Taggable.find(:all, :joins => :tags, :select => "taggables.*, tags.count tag_count", :conditions => {:tags => {:taggable_id => array_of_tag_ids}}, :group => "taggables.id having tag_count = #{array_of_tag_ids.count}"))
Your question is a bit confusing ('tags' seemed to be used quite a bit :)), but I think you want the same thing I needed here:
Taggable.find([1,2,3], :include => :tags).tags.map { |t| t.taggables }
or (if you want the results to be unique, which you probably do):
Taggable.find([1,2,3], :include => :tags).tags.map { |t| t.taggables }.flatten.uniq
This gets at all the taggables that have the same tags as the taggables that have ids 1,2, and 3. Is that what you wanted?