rails render json nested two levels - ruby-on-rails

How can I expand associations more than one level deep? Right now I can expand reviews but am not sure how to also expand the patient_profile_id?
class Review
belongs_to :patient_profile
end
render json: doctors.to_json(
:include => {:reviews => {:include => :patient_profile_id }}
)

I'd highly suggest you check out the jbuilder gem. There's a great railscast that explains it's usage.
Basically, you will have to add a jbuilder file into your views, that gives you allot more control over your json.
For your specific use case you'd use something like this:
doctors/index.json.jbuilder
json.doctors #doctors do |json, doctor|
json.(doctor, :id, :name)
json.reviews doctor.reviews do |json, review|
json.(review, :id, :rating, :patient_profile_id)
json.patient_profile review.patient_profile do |json, profile|
json.(profile, :id, :name, ... ) # attributes of the profile
end
end
end

Try to use something like this:
render json: doctors.to_json(
:include => {:reviews => {:include => :patient_profile }}
)
Here you can find detail information how to serialize nested objects.

Check for overriding as_json method

Related

Rails 4, create multiple objects - how to permit it?

How to permit this parameters:
contacts: [
{:value => 'value', :contacts_type => 'contact_type'},
{:value => 'value', :contacts_type => 'contact_type'},
]
To create many objects by controller action in one JSON request?
Like below, contacts will be an array of resources with specific attributes value and contacts_type:
params.permit(contacts: [:value, :contacts_type])
If you get params like the following:--
:params=>{:xyz => {:contacts => [{:value => 'value', :contacts_type => 'type'}, ..]}}
Then do the folowing:--
params.require(:xyz).permit(contacts: [:value, :contacts_type])
And add attr_accessor :contacts to your model if contacts is just a form field name part.
Work around for this should be
def contact_params
new_params = params.permit(contacts: [:value, :contacts_type])
new_params[:contacts] if new_params
end
Please suggest alternate solution if any

Rails RABL display collection as object and child in this object

I have such index.rabl:
collection #exchangers, :root => "bank", :object_root => false
extends "exchanger_lists/show"
and such show.rabl:
object #exchanger
attributes :id, :name, :address, :location_id, :latitude, :longitude, :exchanger_type_id
node(:location_name) {|exchanger_list| exchanger_list.location.name }
node(:exchanger_type_name) {"normal" }
child #currencies do
attribute :value, :direction_of_exchange_id, :exchanger_list_id
end
my contoller is such:
def index
#exchangers = ExchangerList.all
end
def show
#exchanger = ExchangerList.find(params[:id])
#currency_list = CurrencyList.all
#currencies = []
#currency_list.each do |c|
#currencies << CurrencyValue.find(:all, :conditions => {:currency_list_id => c.id, :exchanger_list_id => #exchanger.id}, :order => :updated_at).last(2)
end
#currencies.flatten!
end
if i call in browser show method, i see child #currencies and it's data, but if i call index i see all (also i see nodes) but child i didn't see.... What's wrong? what i do bad?
Your architecture is a little bit messed up because in the show action you not only display an #exchanger but also the complete list of #currencies being nil when you render show in the index template. In general I would suggest you to think about the whole app architecture.
When I should give you a simple solution for you current problem I would extract the #currencies code from the show action into helper method in app/helpers/currencies_helper.rb and access it from the show template.
module CurrenciesHelper
def currencies(exchanger)
currencies = CurrencyList.all.map do |c|
CurrencyValue.find(:all, :conditions => {:currency_list_id => c.id, :exchanger_list_id => exchanger.id}, :order => :updated_at).last(2)
end
currencies.flatten!
end
end
By the way I replaced the each method with map because it suits better in this case.
Change the currencies part in the show template to
child currencies(#exchanger) do
attribute :value, :direction_of_exchange_id, :exchanger_list_id
end

Show foreign key values with a json answer

I have one model which has a foreign key :
class Hotel < ActiveRecord::Base
belongs_to :country
scope :country, lambda { |country_id|
self.scoped.where('country_id IN ( ? )', country_id) unless country_id.blank?
}
end
And in my controller, i do this :
def filter
#hotels = Hotel.scoped
#hotels = #hotels.country(params[:country_id]) unless params[:country_id].blank?
count = #hotels.count
render :json => ['hotels' => #hotels, 'count' => count ]
end
But my json answer has the value country_id but not my contry entity, how can I force that?
Thank you.
You are using "country" as if it were scope, calling it on all Hotels. This isn't correct. I assume you are trying to get all Hotels that belong to country_id. You can do that like this:
#country = Country.find(params[:country_id])
render :json => ['hotels' => #country.hotels, 'country' => #country]
Does that solve your problem? Your question is a little confusing.
I have my answer, I have to use in my controller the :include parameter :
render :json => ['hotels' => #hotels, 'count' => count ], :include=> [:country, :city]
This will add my city and country models to my json answer.
Thank for help !!

Add virtual attribute to json output

Let's say I have an app that handles a TODO list. The list has finished and unfinished items. Now I want to add two virtual attributes to the list object; the count of finished and unfinished items in the list. I also need these to be displayed in the json output.
I have two methods in my model which fetches the unfinished/finished items:
def unfinished_items
self.items.where("status = ?", false)
end
def finished_items
self.items.where("status = ?", true)
end
So, how can I get the count of these two methods in my json output?
I'm using Rails 3.1
The serialization of objects in Rails has two steps:
First, as_json is called to convert the object to a simplified Hash.
Then, to_json is called on the as_json return value to get the final JSON string.
You generally want to leave to_json alone so all you need to do is add your own as_json implementation sort of like this:
def as_json(options = { })
# just in case someone says as_json(nil) and bypasses
# our default...
super((options || { }).merge({
:methods => [:finished_items, :unfinished_items]
}))
end
You could also do it like this:
def as_json(options = { })
h = super(options)
h[:finished] = finished_items
h[:unfinished] = unfinished_items
h
end
if you wanted to use different names for the method-backed values.
If you care about XML and JSON, have a look at serializable_hash.
With Rails 4, you can do the following -
render json: #my_object.to_json(:methods => [:finished_items, :unfinished_items])
Hope this helps somebody who is on the later / latest version
Another way to do this is add this to your model:
def attributes
super.merge({'unfinished' => unfinished_items, 'finished' => finished_items})
end
This would also automatically work for xml serialization.
http://api.rubyonrails.org/classes/ActiveModel/Serialization.html
Be aware though, you might want use strings for the keys, since the method can not deal with symbols when sorting the keys in rails 3. But it is not sorted in rails 4, so there shouldn't be a problem anymore.
just close all of your data into one hash, like
render json: {items: items, finished: finished, unfinished: unfinished}
I just thought I'd provide this answer for anyone like myself, who was trying to integrate this into an existing as_json block:
def as_json(options={})
super(:only => [:id, :longitude, :latitude],
:include => {
:users => {:only => [:id]}
}
).merge({:premium => premium?})
Just tack .merge({}) on to the end of your super()
This will do, without having to do some ugly overridings. If you got a model List for example, you can put this in your controller:
render json: list.attributes.merge({
finished_items: list.finished_items,
unfinished_items: list.unfinished_items
})
As Aswin listed above, :methods will enable you to return a specific model's method/function as a json attribute, in case you have complex assosiations this will do the trick since it will add functions to the existing model/assossiations :D it will work like a charm if you dont want to redefine as_json
Check this code, and please notice how i'm using :methods as well as :include [N+Query is not even an option ;)]
render json: #YOUR_MODEL.to_json(:methods => [:method_1, :method_2], :include => [:company, :surveys, :customer => {:include => [:user]}])
Overwritting as_json function will be way harder in this scenario (specially because you have to add the :include assossiations manually :/
def as_json(options = { })
end
If you want to render an array of objects with their virtual attributes, you can use
render json: many_users.as_json(methods: [:first_name, :last_name])
where first_name and last_name are virtual attributes defined on your model

Adding further attributes to a ActiveRecord serialization?

I have my json serialization working fine
render :json => "#{current_object.serialize(:json, :attributes => [:id, :name])}
But I also want to add further data to the json before it gets set back to the client. Mainly the auth_token.
Googled around like crazy but I can not find what option serialize will take to allow me to append/merge my other data into the JSON.
Hopting to find something like this...
current_object.serialize(:json, :attriubtes => [:id, name], :magic_option => {:form_authenticity_token => "#{form_authenticity_token}"})
You want the :methods key, which works like :attributes, but will include the results of the methods given. In your case:
current_object.to_json(
:attributes => [:id, :name],
:methods => [:form_authenticity_token]
)
For what it's worth, in a recent Rails I hacked together what you want like this:
sr = ActiveRecord::Serialization::Serializer.new(your_object, some_serialization_options).serializable_record
sr['extra'] = my_extra_calculation(some_parameters)
format.json { render :json => sr }
Where your_object is what you want to serialize, some_serialization_options are your standard :include, :only, etc parameters, and my_extra_calculation is whatever you want to do to set the value.
Jimmy
Hacked it in and moving on...
current_object.serialize(:json, :attributes => [:id, :name]).gsub(/\}$/, ", \"form_authenticity_token\": \"#{form_authenticity_token}\"}")

Resources