Rails as_json with conditions - ruby-on-rails

In my application, :foos have many :bars, and I'm serializing each foo as JSON like so:
#foo.as_json(
except: [:created_at, :updated_at],
include: {
bars: { only: [:ip_addr, :active] }
}
)
This gives me the following:
{
"id" => 2,
"name" => "Hello World",
"bars" => [
{ "ip_addr" => "192.123.12.32", "active" => 0 },
{ "ip_addr" => "192.123.12.33", "active" => 1 }
]
}
As you can see, my serialized hash includes an inactive bar. How can I exclude inactive bars from my hash?
It would be great if I could do this:
include: { bars: { only: { :active => true }}}
Have I taken as_json as far as it will go? Do I need to switch to active model serializers now?

I think you could try add some method like active_bars which will return exactly what you need like:
def active_bars
bars.where active: true
end
or you could even add new relation:
has_many :active_bars, -> { where active: true }, class_name: '..', foreign_id: '..'
and then you will be able write:
#foo.as_json(
except: [:created_at, :updated_at],
include: {
active_bars: { only: [:ip_addr, :active] }
}
)

you use as_json with conditions for customize json response for some actions, but model serializers for default json response that you needed for the most responses.
Read these Model_Serializer VS. as_json, record-serializers-from-scratch.

Related

Using scope in assoc. records JSON response - Rails 5 [duplicate]

In my application, :foos have many :bars, and I'm serializing each foo as JSON like so:
#foo.as_json(
except: [:created_at, :updated_at],
include: {
bars: { only: [:ip_addr, :active] }
}
)
This gives me the following:
{
"id" => 2,
"name" => "Hello World",
"bars" => [
{ "ip_addr" => "192.123.12.32", "active" => 0 },
{ "ip_addr" => "192.123.12.33", "active" => 1 }
]
}
As you can see, my serialized hash includes an inactive bar. How can I exclude inactive bars from my hash?
It would be great if I could do this:
include: { bars: { only: { :active => true }}}
Have I taken as_json as far as it will go? Do I need to switch to active model serializers now?
I think you could try add some method like active_bars which will return exactly what you need like:
def active_bars
bars.where active: true
end
or you could even add new relation:
has_many :active_bars, -> { where active: true }, class_name: '..', foreign_id: '..'
and then you will be able write:
#foo.as_json(
except: [:created_at, :updated_at],
include: {
active_bars: { only: [:ip_addr, :active] }
}
)
you use as_json with conditions for customize json response for some actions, but model serializers for default json response that you needed for the most responses.
Read these Model_Serializer VS. as_json, record-serializers-from-scratch.

Custom JSON response with acts_as_taggable_on

I just implemented acts_as_taggable_on in my app and now I'm trying to trim my JSON response so it doesn't return everything for the Model in question. Here's what my as_json method looks like:
def as_json(options={})
super(:only => [:serial_number],
:include => {
:device_functions => { :only => [:can_scan, :can_brute] },
:scan_options => { :methods => :scan_ip_list}
}
)
end
Which currently returns:
{
"serial_number": "abcdefg12345",
"device_functions": [
{
"can_scan": true
}
],
"scan_options": [
{
"id": 1,
"device_id": 11,
"created_at": "2016-02-05T02:26:26.090Z",
"updated_at": "2016-02-05T02:26:26.090Z",
"scan_ip_list": [
"10.10.10.100-110",
"10.10.10.1"
]
}
]
}
I want to get rid of extra data that I don't need, such as id, device_id, created_at and updated_at now.
Also, using :only => worked find for the :device_functions response, but I had to use :methods => for :scan_options since I'm using acts_as_taggable_on... at least that's what I read and was the only option that returned something (I tried :only => and :include => as well but they returned an empty hash:
{
"serial_number": "abcdefg12345",
"device_functions": [
{
"can_scan": true
}
],
"scan_options": [
{}
]
}
You just need to add the :only option to your :scan_options hash too:
# ...
:scan_options => { :methods => :scan_ip_list, :only => :scan_ip_list }
Also, FWIW, you should probably merge into option in case you ever want to supply some of your own options, so:
# ...
super options.merge( :only => ...etc.

How to combine two as_json methods to one correctly?

In my Model I have a working as_json method as follows:
def as_json(options = {})
super(options.merge(include: [:user, comments: {include: :user}]))
end
This method is for including users in comments.
Now I need to add almost the same thing in the same model for answers:
def as_json(options = {})
super(options.merge(include: [:user, answers: {include: :user}]))
end
How do I combine these two as_json methods, so that I have one as_json method?
Don't laugh but I am struggling with this for 3 days.
This is one of the reasons why you should not use the built-in to_json to serialize ActiveRecord models.
Instead, you should delegate the task to another object called serializer. Using a serializer allows you to have illimitate representations (serializations) of the same object (useful if the object can have different variants such as with/without comments, etc) and separation of concerns.
Creating your own serializer is stupid simply, as simple as having
class ModelWithCommentsSerializer
def initialize(object)
#object = object
end
def as_json
#object.as_json(include: [:user, comments: {include: :user}]))
end
end
class ModelWithAnswersSerializer
def initialize(object)
#object = object
end
def as_json
#object.as_json(include: [:user, answers: {include: :user}]))
end
end
Of course, that's just an example. You can extract the feature to avoid duplications.
There are also gems such as ActiveModelSerializers that provides that feature, however I prefer to avoid them as they tend to provide a lot of more of what most of users really need.
Why are you trying to override core Rails functionality - not good practice unless absolutely necessary.
--
This says the following:
To include associations use :include:
user.as_json(include: :posts)
# => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
# "created_at" => "2006/08/01", "awesome" => true,
# "posts" => [ { "id" => 1, "author_id" => 1, "title" => "Welcome to the weblog" },
# { "id" => 2, "author_id" => 1, "title" => "So I was thinking" } ] }
You could call:
#answers.as_json(include :users)
--
Ohhhhhhhh:
Second level and higher order associations work as well:
user.as_json(include: { posts: {
include: { comments: {
only: :body } },
only: :title } })
# => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
# "created_at" => "2006/08/01", "awesome" => true,
# "posts" => [ { "comments" => [ { "body" => "1st post!" }, { "body" => "Second!" } ],
# "title" => "Welcome to the weblog" },
# { "comments" => [ { "body" => "Don't think too hard" } ],
# "title" => "So I was thinking" } ] }
So looks like you could call:
#answers.to_json(include: comments: { include: :users })
def as_json(other_arg, options = {})
as_json(options.merge(include: [:user, other_arg: {include: :user}]))
end
And then you can call:
MyModel.as_json(:comments)
MyModel.as_json(:answers)

Multiply association to_json

I am working on this code:
render json: User.find(params[:id]).to_json(
:include =>
{
:user_quests => { :inlcude => { :quest }, :only => { :id } },
:user_skills
},
:except =>
{
:authentication_token,
:email
}
)
It results in a SyntaxError. The only working code I currently have is:
render json: User.find(params[:id]).to_json(
:include =>
[
:user_quests,
:user_skills
],
:except =>
[
:authentication_token,
:email
]
)
But I need to pass further parameters to only one of the associations to perform a nested include. The other one (:user_skills) should be fetched the same way as in the working code. How do I do that?
This code results in a syntax error, because you don't use collections properly. An array ([]) is a list of values, whereas a hashmap (or simply hash, {}) requires key-value pairs.
In to_json's context an array of associations:
[
:user_quests,
:user_skills
]
...is roughly equivalent to a hash of associations mapped to empty option hashes:
{
:user_quests => {},
:user_skills => {}
}
Having performed that transformation, you can selectively add options wherever you like.
Here it is solution which works for me and is compatible with Rails 4:
render json: User.find(params[:id]).as_json(
include:
{
user_quests:
{
include:
{
quest:
{
only: :_id
}
}
},
user_skills:
{
}
},
except: [:authentication_token, :email]
)

Create json document from ActiveRecord and avoid N+1

I'm trying to create a json array (string actually) based on my db structure. I have the following relationship:
Country > State > City
The way I'm doing it now is very innefficient (N+1):
data = "[" + Country.all.map{ |country|
{
name: country.name,
states: country.states_data
}.to_json
}.join(",") + "]"
Then on the Country model:
def states_data
ret_states = []
states.all.each do |state|
ret_states.push name: state.name, cities: state.cities_data
end
ret_states
end
Then on the State model:
def cities_data
ret_cities = []
cities.all.each do |city|
ret_cities.push name: city.name, population: city.population
end
ret_cities
end
How can I do this more efficiently?
Eager load the states and cities. Just be careful because this could take up a lot of memory for large datasets. See documentation here http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations. Whenever possible I like using joins in addition to includes to fetch all data at once.
#to_json will also serialize Arrays for you, so you don't need to manually add bits of JSON.
Your code from above could be altered like so:
data = Country.joins(:states => :cities).includes(:states => :cities).all.map{ |country|
{
name: country.name,
states: country.states_data
}
}.to_json
But you could also remove the need for the _data methods.
data = Country.joins(:states => :cities).includes(:states => :cities).to_json(
:only => :name,
:include => {
:states => {
:only => :name,
:include => {
:cities => {
:only => [:name, :population]
}
}
}
}
)
That is pretty ugly, so you may want to look into overriding #as_json for each of your models. There is a lot of information about that available on the web.
u can provide the model to be included when converting to json.
country.to_json(:include => {:states => {:include => :cities}})
check http://apidock.com/rails/ActiveRecord/Serialization/to_json for

Resources