I am using the Self Joins to add reference to its own table.
class Employee < ApplicationRecord
belongs_to :manager, class_name: "Employee", optional: true
has_many :customers
end
class Customer
belongs_to :employee
end
I am fetching the employee data based. One employee will have one employee_head and employee_head will have a boss.
In the serializer i have
class EmployeeSerializer < ApplicationSerializer
# include FastJsonapi::ObjectSerializer
attributes :id, :name, :manager
def manager
ActiveModel::SerializableResource.new(object.manager)
end
end
when i query form employee, i am expecting:
{
employee:
{
id: 1,
name: "employee name",
customer_attributes: [
{ id: 8, name: customer_name }
],
manager: {
id: 2,
name: "employee head name",
customer_attributes: [
{ id: 8, name: customer_name }
],
manager: {
id: 3,
name: "boss name",
customer_attributes: [
{ id: 8, name: customer_name }
],
}
}
}
}
I am getting the expected data using:
Employee.includes(:customers).where(name: "employee name", employee_type: employee)
but the problem is when i hit the query, the customer information is fetched from the database multiple time. I am confuse where to use includes to avoid N+1 query to DB.
Thanks in advance
Related
I have model Transactions that embeds many Events
class Transaction
embeds_many :events
end
Model Events has fields :name and :execute_at
class Event
field :name, type: String
field :execute_at, type: Date
embedded_in :transaction, inverse_of: :events
end
What I need to do is to sort Transactions by execute_at field of Events with specific name (lets say 'Name1'). Events are unique within each Transaction so there is no issue here.
Example:
{
amount: '123',
events: [
{
name: 'Name1',
execute_at: someday
},
{
name: 'Name5',
execute_at: someotherday
}
}
Transaction2
{
amount: '124',
events: [
{
name: 'Name1',
execute_at: someotherday
},
{
name: 'Name11',
execute_at: somerday
}
}
Basically sort these 2 transactions only taking data for sorting from events with name: 'Name1'
sorted_trans = Transaction.where('events.name' => 'Name1').order_by(:'events.execute_at'.asc)
or
sorted_trans = Transaction.where('events.name' => 'Name1').order_by(:'events.execute_at'.desc)
Below is my json after rendering '#products'. As you can see, there are 2 other models nested (vendor_products and vendors). The association between product and vendor models is many-to-many with 'vendor_products' being connecting tables. What I wanted to achieve here is - instead of having both 'vendor_products' and 'vendors' models being nested I just want to add 'vendor name' as another attribute inside the vendor_products model.
{
id: 1,
barcode: 3045320001525,
name: "xyz",
size: "370 g",
brand: "abc",
img_url: "http://xyx"
vendor_products: [
{
id: 1,
v_item: "JAM101",
vendor_id: 1,
case_price: 72
},
{
id: 2,
v_item: "1001",
vendor_id: 2,
case_price: 65
}
],
vendors: [
{
name: "vendor_xyz"
},
{
name: "vendor_123"
}
]
},
Below is the format of json I wanted:
{
id: 1,
barcode: 3045320001525,
name: "xyz",
size: "370 g",
brand: "abc",
img_url: "http://xyx"
vendor_products: [
{
id: 1,
v_item: "JAM101",
vendor_id: 1,
vendor_name: "vendor_xyz",
case_price: 72
},
{
id: 2,
v_item: "1001",
vendor_id: 2,
vendor_name: "vendor_abc",
case_price: 65
}
],
Here are my serializer classes:
class ProductSerializer < ActiveModel::Serializer
attributes :id, :barcode, :name, :size, :brand, :img_url
has_many :vendor_products
has_many :vendors
end
class VendorProductSerializer < ActiveModel::Serializer
attributes :id, :v_item, :vendor_id, :case_price
belongs_to :product
belongs_to :vendor
end
class VendorSerializer < ActiveModel::Serializer
attributes :name
has_many :products
has_many :vendor_products
end
Try adding a custom attribute in vendor_products serializer,
class VendorProductSerializer < ActiveModel::Serializer
attributes :id, :v_item, :vendor_id, :case_price, :vendor_name
belongs_to :product
belongs_to :vendor
def vendor_name
object.vendor.name #object is current vendor_product object get name from that
end
end
Before using fast_jsonapi gem I was doing this:
render json: school.to_json(include: [classroom: [:students]])
My SchoolSerializer looks like:
class SchoolSerializer
include FastJsonapi::ObjectSerializer
attributes :name, :description, :classroom
end
How would I get the students included in the JSON result?
Also, the classroom association is including but it is displaying all the properties, is there a way to map the classroom property to a ClassroomSerializer ?
class School < ApplicationRecord
belongs_to :classroom
end
class Classroom < ApplicationRecord
has_many :students
end
class SchoolSerializer
include FastJsonapi::ObjectSerializer
attributes :name, :description
belongs_to :classroom
end
# /serializers/classroom_serializer.rb
class ClassroomSerializer
include FastJsonapi::ObjectSerializer
attributes :.... #attributes you want to show
end
Also you can add additional association to your School model, to access Students.
like this
has_many :students, through: :classroom
and then include it in School serializer directly.
Update: also please note that you can directly point to serializer class you need. (if you want to use class with different name from model as example).
class SchoolSerializer
include FastJsonapi::ObjectSerializer
attributes :name, :description
belongs_to :classroom, serializer: ClassroomSerializer
end
render json: SchoolSerializer.new(school, include: "classrooms.students")
The difference being the use of "include" when rendering the serializer. This tells the Serializer to add a key "included" to the returned JSON object.
class SchoolSerializer
include FastJsonapi::ObjectSerializer
belongs_to :classroom
has_many :students, through: :classroom
attributes :school_name, :description
end
StudentSerializer
include FastJsonapi::ObjectSerializer
belongs_to :classroom
belongs_to :school
attributes :student_name
end
render json: SchoolSerializer.new(school).serialized_json
will return a series of students with only the top level identifiers in the form
data: {
id: "123"
type: "school"
attributes: {
school_name: "Best school for Girls",
description: "Great school!"
...
},
relationships: {
students: [
{
id: "1234",
type: "student"
},
{
id: "5678",
type: "student"
}
]
}
}
whereas the include: "classroom.students" will return the full serialized Student Records in the form:
data: {
id: "123"
type: "school"
attributes: {
school_name: "Best school for Girls"
...
},
relationships: {
classroom: {
data: {
id: "456",
type: "classroom"
}
},
students: [
{
data: {
id: "1234",
type: "student"
}
},
{
data: {
id: "5678",
type: "student"
}
}
]
},
included: {
students: {
data {
id: "1234",
type: "student",
attributes: {
student_name: "Ralph Wiggum",
...
},
relationships: {
school: {
id: "123",
type: "school"
},
classroom: {
id: "456",
type: "classroom"
}
}
},
data: {
id: "5678",
type: "student",
attributes: {
student_name: "Lisa Simpson",
...
},
relationships: {
school: {
id: "123",
type: "school"
},
classroom: {
id: "456",
type: "classroom"
}
}
}
},
classroom: {
// Effectively
// ClassroomSerializer.new(school.classroom).serialized_json
},
}
}
I have a Rails object Product:
{
id: 1
name: 'soup'
}
And a Customer object:
{
id: 20
name: 'Ryans'
}
They are linked via:
class Product < ActiveRecord::Base
belongs_to :customer
When I call Product.to_json(methods: [:customer]), I get:
{
id: 1
name: 'soup',
customer: {
id: 20
name: 'Ryans'
}
}
But I need this to be in the format:
{
id: 1
name: 'soup',
customer_name: 'Ryans'
}
Is this possible? I'm using Rails v4.1.7
You can delegate nameto your Customer class
class Product < ActiveRecord::Base
belongs_to :customer
delegate :name, to: :customer, prefix: true
You can then do
Product.to_json(methods: [:customer_name])
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