Rails to_json belongs_to object - ruby-on-rails

I have two models: Cabinet and Workplace.
class Cabinet < ActiveRecord::Base
def as_json(options={})
options.merge!({except: [:created_at, :updated_at]})
super(options)
end
end
class Workplace < ActiveRecord::Base
belongs_to :cabinet
def as_json(options = {})
options.merge!(:except => [:created_at, :updated_at, :cabinet_id], include: :cabinet)
super(options)
end
end
When I called Cabinet.first.to_json I get
{
id: 1,
cabinet: "100"
}
but when I called Workplace.first.to_json id get
{
name: "first workplace",
Cabinet: {
id: 1,
cabinet: "100",
created_at: "#created_at",
updated_at: "#updated_at"
}
}
Why this? Thanks and sorry for my english :)

Not sure if I am following you, but do you want to get just attributes from Workplace model, and not Cabinet data when you do Workplace.first.to_json?
I think it is because you include cabinet in as_json method configuration as explained here.
You should either remove it or do this:
Workplace.first.attributes.to_json
Let me know if I am missing something from your question.

Let's assume that your model Cabinet has :id, :cabinet, :created_at, :updated_at attributes and Workplace has :id, :name, :cabinet_id, .....
Now, if you try to fire Cabinet.first.to_json, ofcourse it will render the following:
{
id: 1,
cabinet: "100"
}
becuase that is the attributes belongs to Cabinet model. Then you also added these line of code options.merge!({except: [:created_at, :updated_at]}) that's why it only renders :id and :name attributes. And if you try to fire Workplace.first.to_json then it will render:
{
name: "first workplace",
Cabinet: {
id: 1,
cabinet: "100",
created_at: "#created_at",
updated_at: "#updated_at"
}
}
because, of these options.merge!(:except => [:created_at, :updated_at, :cabinet_id], include: :cabinet). You include the model Cabinet so it will automatically added to your json.

Related

How can I call related tables with include option by as_json

current relation is below.
class Hoge < ApplicationRecord
belongs_to :hoge_category
class HogeCategory < ApplicationRecord
belongs_to :big_hoge_category
has_many :hoges
class BigHogeCategory < ApplicationRecord
has_many :hoge_categories
has_many :hoges, through: :hoge_categories
I want to extract Hoge's data which is related HogeCategory table and BigHogeCategory table.
Like this.
HogesController
hoges = Hoge.index( params[:keyword], nil, params[:hoge_category_id], params[:offset].to_i, 10 ).as_json(include: :hoge_category)
render status: 200, json: { hoges: hoges } #send API
Hoge.rb
class Hoge < ApplicationRecord
searchkick callbacks: :async, language: "japanese", word_middle: [:title, :message]
def self.index(keyword, connect_id, hoge_category_id, offset, limit) #execute search
where = {}
if keyword.present?
hoges = Hoge.search(keyword, fields: [:title, :message],misspellings: false, where: where, order: { created_at: :desc }, limit: limit, offset: offset).results
else
hoges = Hoge.search(fields: [:title, :message],misspellings: false, where: where, order: { created_at: :desc }, limit: limit, offset: offset).results
end
return hoges
end
def search_data
{
title: title,
message: message,
created_at: created_at,
hoge_category_id: hoge_category&.id
}
end
end
From my search, it's better to use as_json with include option.
But I don't know how to write when related tables are multiple.
How can I write that?
Or if you have a better idea, let me know please..

how to use an instance of a model in another serializer

I'm stuck here and couldn't find solution to proceed my work,
I have 3 models: plans, days, and meals.
This is my Plan Controller I've managed to get the correct answer in the controller, I want it nested and inside the serializer because I'm using URL helper to retrieve my images URLs, is there a possible way to use the #plan.id inside the DaySerializer?
def meals
#plan = Plan.find(params[:id])
#days = #plan.days
#meals = Meal.where("plan_id = ? ", #plan.id)
render :json => { :plan => #plan, :days => #days,
:meals => #meals }
end
This is my Plan model
class Plan < ApplicationRecord
has_many :days
has_one_attached :image, dependent: :destroy
end
This is my Day model
class Day < ApplicationRecord
has_many :meals
has_many :plans
end
This is my Meal model
class Meal < ApplicationRecord
belongs_to :plan
belongs_to :day
has_one_attached :image, dependent: :destroy
end
I want to show all meals for a specific Plan, to do that I need to use a variable inside the daySerializer but I couldn't find how to do it.
This is my planSerializer
class PlanSerializer < ActiveModel::Serializer
attributes :id, :name, :monthly_price, :plan_days
def plan_days
object.days.map do |day|
DaySerializer.new(day, scope: scope, root: false, event: object)
end
end
end
and this is my DaySerializer which I need to use the instance of the plan inside
class DaySerializer < ActiveModel::Serializer
attributes :number, :plan_meals
def plan_meals
#how to be able to use this line in Serilizer? !important
#plan = Plan.find(params[:id])
object.meals.map do |meal|
if meal.plan_id == #plan.id
MealSerializer.new(meal, scope: scope, root: false, event: object)
end
end
end
end
target reason response :
{
id: 8,
name: "Plan1",
monthly_price: 88,
plan_days: [
{
number: 5,
plan_meals: [],
},
{
number: 4,
plan_meals: [],
},
{
number: 3,
plan_meals: [],
},
{
number: 2,
plan_meals: [],
},
{
number: 1,
plan_meals: [
{
id: 11,
name: "test meal",
calories: 32,
protein: 32,
fat: 32,
carbohydrates: 32,
plan_id: 8,
},
],
},
],
}
currently it's showing all meals that belongs to each day,
not only the meals with the plan_id = Plan.find(params[:id])
In general I think you could use something like this should work.
ActiveModel::Serializer::CollectionSerializer.new. It actually by itself allows you to pass additional information to your serializer. It does the same as your current code just you are able to explicitly pass new data.
Controller:
def meals
#plan = Plan.find(params[:id])
#days = #plan.days
#meals = Meal.where("plan_id = ? ", #plan.id)
render :json => {
:plan => #plan,
:days => ActiveModel::Serializer::CollectionSerializer.new(#days, serializer: DaySerializer, plan_id: #plan.id),
:meals => #meals
}
end
And then in DaySerializer:
class DaySerializer < ActiveModel::Serializer
attributes :number, :plan_meals
def plan_meals
object.meals.map do |meal|
if meal.plan_id == instance_options[:plan_id]
MealSerializer.new(meal, scope: scope, root: false, event: object)
end
end
end
end
So in short ActiveModel::Serializer::CollectionSerializer.new in controller and instance_options in serializer to access passed additional parameters.
UPDATED:
How about add meal serializer?
class MealSerializer < ActiveModel::Serializer
attributes :id, :name, :calories, :protein, :fat, # etc
end
class DaySerializer < ActiveModel::Serializer
attributes :number
has_many :meals, serializer: MealSerializer
end
ORIGINAL:
class PlanSerializer < ActiveModel::Serializer
attributes :id, :name, :monthly_price, :plan_days
has_many :plan_days, serializer: DaySerializer
end
something like this.

No attachment path in json, join table

Need some help..
I have user, offer and country model. The user has_one_attached :image.
Offer & User => has_and_belongs_to_many
Displaying all users grouped by country in controller:
offer = Offer.find(params[:id])
render json: offer.users.joins(:country).group_by { |t| t.country.name }
Serializer
class OfferSerializer < ActiveModel::Serializer
include Rails.application.routes.url_helpers
attributes :id, :name, :image
def image
rails_blob_path(object.image, only_path: true) if object.image.attached?
end
I do get:
{"country1": [
{
"id": 9,
"country_id": 1,
"online": true,
...
{
"id": 12,
"bundesland_id": 1,
"online": true,
...
and i need to get additional:
"header_image": "/rails/active_storag...
But I don't get the user.blob path in the json!
Any idea?
Thanks for support!

Return custom columns in Rails ActiveRecord Query

I am querying my ActiveRecords in rails with the following:
result = MyObj.where({customer: current_id}).as_json()
There are two columns returned:
result = [{id:1, name: "david", last_name: "Smith:"}]
I would like create a third column (which will not be saved to the DB) like so:
result = [{id:1, name: "David", last_name: "Smith:", full_name:"David Smith"}]
Is this possible within the WHERE query?
Add a full_name method to your MyObj model, then pass methods: :full_name to the as_json method:
class MyObj
def full_name
"{name} #{last_name}"
end
end
result = MyObj.where({customer: current_id}).as_json(methods: :full_name)
From the documentation for as_json:
To include the result of some method calls on the model use :methods:
user.as_json(methods: :permalink)
# => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
# "created_at" => "2006/08/01", "awesome" => true,
# "permalink" => "1-konata-izumi" }
Or alternately, you could override as_json on the model to include full_name by default:
class MyObj
def full_name
"{name} #{last_name}"
end
def as_json(options={})
super({methods: :full_name}.merge options)
end
end
Sure. Override the method in your model...
class MyObj < ActiveRecord::Base
def full_name
"#{name} #{last_name}"
end
def as_json options={}
{
id: id,
name: name,
last_name: last_name,
full_name: full_name
}
end
end
Quick and dirty just manipulate the results you get back
result = MyObj.where({customer: current_id})
result.map{|customer| "full_name: #{customer.first_name + customer.last_name}" }
But be careful of nil values.

How to specify a different root name for the embedded objects?

In my app I had BlogPost model and User model that are related through relation named author. To serve data from my Rails app I use active_model_serializers with definition:
class Blog::PostSerializer < ActiveModel::Serializer
embed :ids, include: true
attributes :id, :title, :text, :created_at, :updated_at
has_one :author
has_many :assets
end
When I fetch this using Ember model:
Admin.BlogPost = DS.Model.extend({
author: DS.belongsTo('User'),
title: DS.attr('string'),
text: DS.attr('string'),
createdAt: DS.attr('date'),
updatedAt: DS.attr('date')
});
There is an error:
Uncaught Error: Assertion Failed: You looked up the 'author' relationship on a 'blog.post' with id 1 but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (`DS.belongsTo({ async: true })`)
Which is caused by that my response looks like:
{
'blog_posts': [
{
id: 1,
author_id: 1
},
// …
],
'authors': [
{ id: 1, /* … */ }
]
}
Is there any way to change 'authors' in response to 'users' or use 'authors' as alias to 'users' in serializer?
From active_model_serializers 0.8 description: https://github.com/rails-api/active_model_serializers/tree/0-8-stable
You can also specify a different root for the embedded objects than the key used to reference them:
class PostSerializer < ActiveModel::Serializer
embed :ids, :include => true
attributes :id, :title, :body
has_many :comments, :key => :comment_ids, :root => :comment_objects
end
This would generate JSON that would look like this:
{"post": {
"id": 1,
"title": "New post",
"body": "A body!",
"comment_ids": [ 1 ]
},
"comment_objects": [
{ "id": 1, "body": "what a dumb post" }
]
}
Just define a method in your serializer named users and return authors in it I.e.
attributes :id, :title, :text, :created_at, :updated_at, :users
def users
object.authors
end

Resources