Rails 4 render json nested objects - ruby-on-rails

I need to render as Json a complex structure. I have next structure working:
render :json => #booking, :include => [:paypal,
:boat_people,
:boat => {:only => :boat_model, :include => {:boat_model => {:only => :name, :include => { :boat_type => {:only => :name}}}}}]
but I´m not able to add a port attribute with some other nested attributes to :boat, such as :boat_model (at same level).
UPDATE:
Although it´s not working, I include my port attribute.
render :json => #booking, :include => [:paypal,
:boat_people,
:boat => {:only => [:boat_model => {:include => {:boat_model => {:only => :name, :include => { :boat_type => {:only => :name}}}}},
:port => {:include => :city => {:only => name}}]}]
I mean, boat_model and port are both boat attributes.
This is the model object:
class Boat < ActiveRecord::Base
attr_accessor :price
#price
attr_accessor :extrasPrice
#extrasPrice
def as_json(options = { })
h = super(options)
h[:price] = #price
h[:extrasPrice] = #extrasPrice
h
end
belongs_to :boat_model
belongs_to :port
belongs_to :state
has_many :photos
end

I got it.
render :json => #booking, :include => [:paypal,
:boat_people,
:boat => {:only => :name, :include => {:port => {:only => :name, :include => {:city => {:only => :name, :include => {:country => {:only => :name}}}}},
:boat_model => {:only => :name, :include => {:boat_type => {:only => :name}}}}}]

You are probably going to want a much more robust system for displaying JSON. The built-in Rails helpers are really designed primarily for simple additions that enable users to do most of what they would want to accomplish. However, in your case, you are trying to make it do more than it was designed for.
I would highly recommend either creating a view object or using a gem like RABL.
My preference is to use Rabl for complex JSON. It basically creates the 'view object' for you by building a domain specific language that makes it relatively easy to build complex JSON objects in rails.
RABL basically allows you to build views that format JSON instead of HTML. The DSL is extremely rich and enables you to do just about anything you would want. In your case, I think the code would look something like this:
app/views/bookings/show.rabl
object #booking
#these are attributes that exist on your booking model:
attributes :booking_attribute, :other_booking_attribute
child :paypal do
#these are attributes that exist on your paypal model:
attributes :paypay_attribute1, :other_paypal_attribute
end
child :boat_people do
#boat_people attributes that you want to include
attributes :blah_blah
end
child :boat do
#boat attributes that you want to include
attributes :boat_attribute1, :boat_attribute2
child :boat_model do
attributes :name
child :boat_type do
attributes :name
end
end
end

Related

Rails thinking sphinx includes not working

I have the code like following
Listing.search(
Riddle::Query.escape(params[:search]),
:include => params[:include],
:page => page,
:per_page => per_page,
:star => true,
:with => with,
:with_all => with_all,
:order => params[:sort]
)
params[:include] contains the value like [:listing_images, :author, :category, :origin_loc]
I don’t know what was wrong here.

accepts_nested_attributes_for hard code in Controller

I am writing a code stuff for API. Where i am assigning parameters to the class fields.
I have relationship,
event_file.rb
has_many :event_file_attachments
accepts_nested_attributes_for :event_file_attachments
event_file_attachment.rb
mount_uploader :attachment, EventFileUploader
belongs_to :event_file
Wanted to take the value from JOSN and write into nested attribute,
JOSN,
{"event_files"=>{"event_id"=>"1"}, "attachment"=>#<ActionDispatch::Http::UploadedFile:0xc16e4e4 #tempfile=#<Tempfile:/tmp/RackMultipart20141226-5521-12zezuk>, #original_filename="asset.JPG", #content_type="image/jpg", #headers="Content-Disposition: form-data; name=\"attachment\"; filename=\"asset.JPG\"\r\nContent-Type: image/jpg\r\n">}
In controller,
data = params[:event_files]
#event_file = EventFile.new(:event_member_ids => data['event_member_ids'],
:user_id => current_user.id,
:company_id => #current_company.id,
:event_id => data['event_id'],
:status => 0)
#event_file.save
#event_file_attachment = EventFileAttachment.new(:event_file_id => #event_file.id,
:status => 0,
:attachment => params[:attachment])
#event_file_attachment.save
And this is a wrong way to save the nested attribute, How to modify and optimize the code....
The association you created along with the accepts_nested_attribute_for method will create a event_file_attachments_attributes on which you can add the corresponding event file attachment attributes, here is a quick example:
In the controller:
#event_file = EventFile.new(:event_member_ids => data['event_member_ids'],
:user_id => current_user.id,
:company_id => #current_company.id,
:event_id => data['event_id'],
:status => 0,
:event_file_attachments_attributes => [{"0" => {:attr1 => "value", :attr2 => "value"}},{"1" => {:attr1 => "value", :attr2 => "value"}}])
For mor information on this you can check a really helpful railscast http://railscasts.com/episodes/196-nested-model-form-part-1

Adding conditions in rails include

going directly to my problem.
render :json => projects.to_json(:only => ['name', 'id'],:include => {:client => {:only => ['name']}, :deliverables => {:only => ['name', 'id'], :include => {:tasks => {:only => ['name','id']} } } })
This is how my controller responds for json request. Now my problem is that it lists all the deliverables & tasks that are in given project but here I want to respond with tasks & deliverables that meets certain condition like all that are created in specific month.
Thanks is advance.
Here's what I did, in project model
has_many :uncompleted_deliverables,
:class_name => 'Deliverable',
:conditions => 'completed = false'
The same applies to deliverable model
has_many :uncompleted_tsks, :class_name => 'Task', :conditions => 'completed = false'
And then responded json in following way,
format.json { render :json => projects.to_json(:only => ['name', 'id'],
:include => {:client => {:only => ['name']}, :uncompleted_deliverables => {:only => ['name', 'id'], :include => {:uncompleted_tsks => {:only => ['name','id']} } } }) }
Anyway thanks guys for your response..
Could you just include a proc? (see end of documentation here)
I guess the code could look something like that:
in you Project model:
def to_json(options={})
tasks_json = Proc.new { |options|
options[:builder].tasks do
selected_tasks = tasks.select {|t| t.created_at > 30.days.ago } # or whatever your condition is
selected_tasks.each do |t|
options[:builder].task do
options[:builder].tag!('id', t.id)
options[:builder].tag!('name', t.name)
end
end
end }
options.merge!(:procs => [tasks_json])
end
Would that work for you?

group_by using value from other model

Using ROR 2.3.8
I have this in cities_controller.rb:
#shops = Shop.published.search params[:keyword], {
:conditions => conditions,
:star => true,
:group_by => 'city_id',
:group_function => :attr,
:page => params[:page]
}.merge(:order => 'rating_average DESC')
#cities = #shops.collect { |shop| shopy.city }
How can I tell Rails to get the rating_average from City model instead of Shop model? Because the Shop model does not have rating_average. It's actually City model that gets rated.
Thank you.
UPDATES
published namescope in Shop.rb
sphinx_scope(:published) {
{:conditions => {:status => 'published'}}
}
Indexes in Shop.rb
define_index do
indexes city.name, :as => :name, :sortable => true
indexes city.duration, :as => :duration
indexes city.status, :as => :status
#has city.budget, :as => :budget
#has city(:created_at), :as => :created_at
has city(:rating_average), :as => :rating_average
has city_id
end
UPDATES 2
class City < ActiveRecord::Base
has_many :shops, :dependent => :destroy
...
end
You should use joins to acheive this:
#shops = Shop.published.search params[:keyword], {
:conditions => conditions,
:star => true,
:group_by => :city_id,
:group_function => :attr,
:page => params[:page]
}.merge(:order => :rating_average,
:sort_mode => :desc)
You should also add cities. or shops. before columns to specify which table's column.

How do I make a grouped select box grouped by a column for a given model in Formtastic for Rails?

In my Rails project I'm using Formtastic to manage my forms. I have a model, Tags, with a column, "group". The group column is just a simple hardcoded way to organize my tags. I will post my Tag model class so you can see how it's organized
class Tag < ActiveRecord::Base
class Group
BRAND = 1
SEASON = 2
OCCASION = 3
CONDITION = 4
SUBCATEGORY = 5
end
has_many :taggings, :dependent => :destroy
has_many :plaggs, :through => :taggings
has_many :monitorings, :as => :monitorizable
validates_presence_of :name, :group
validates_uniqueness_of :name, :case_sensitive => false
def self.brands(options = {})
self.all({ :conditions => { :group => Group::BRAND } }.merge(options))
end
def self.seasons(options = {})
self.all({ :conditions => { :group => Group::SEASON } }.merge(options))
end
def self.occasions(options = {})
self.all({ :conditions => { :group => Group::OCCASION } }.merge(options))
end
def self.conditions(options = {})
self.all({ :conditions => { :group => Group::CONDITION } }.merge(options))
end
def self.subcategories(options = {})
self.all({ :conditions => { :group => Group::SUBCATEGORY } }.merge(options))
end
def self.non_brands(options = {})
self.all({ :conditions => [ "`group` != ? AND `group` != ?", Tag::Group::SUBCATEGORY, Tag::Group::BRAND] }.merge(options))
end
end
My goal is to use Formtastic to provide a grouped multiselect box, grouped by the column, "group" with the tags that are returned from the non_brands method. I have tried the following:
= f.input :tags, :required => false, :as => :select, :input_html => { :multiple => true }, :collection => tags, :selected => sel_tags, :group_by => :group, :prompt => false
But I receive the following error:
(undefined method `klass' for
nil:NilClass)
Any ideas where I'm going wrong?
Thanks for looking :]
I'm not sure we support :group_by with a custom :collection. In fact, that who part of the code was a messy contribution. So, try omitting the :collection for starters, and see where you end up. If there's a bug with Formtastic, please add an issue on Github.
I'd first move your Group class out of this file, and just inherit from where you want, or use a Module in this class. This is the preferred way of getting methods an constants into a class and staying organized.

Resources