Rails Serializer Association - ruby-on-rails

I have a Component class that can be either a Section or Question. A Section can have many Component (i.e. Question).
I'm trying to get the serializer to send back the Component(Section) and its associated Component(Question). Right now, based on my code below, I'm getting back the Question and the Section object that it's associated to (see below). How can I get the serializer to return what I'm expecting?
Response (current):
{"data":
[{"id": 1,
"type": "Section",
"content": "ABC",
"section_id": null,
"section": null
},
{"id": 2,
"type": "Question",
"content": "Q1",
"section_id": 1,
"section":
{"id": 1,
"type": "Section",
"content": "ABC",
"section_id": null
}
},
{"id": 3,
"type": "Question",
"content": "Q2",
"section_id": 1,
"section":
{"id": 1,
"type": "Section",
"content": "ABC",
"section_id": null
}
}]
}
Response (expected):
{"data":
[{"id": 1,
"type": "Section",
"content": "ABC",
"section_id": null,
"section":
{"id": 2,
"type": "Question",
"content": "Q1",
"section_id": 1
},
{"id": 3,
"type": "Question",
"content": "Q2",
"section_id": 1
}
}]
}
ComponentSerializer:
class ComponentSerializer < ActiveModel::Serializer
belongs_to :section
attributes :id,
:type,
:content,
:section_id
end
SectionSerializer:
class ComponentSerializer < ActiveModel::Serializer
has_many :components
attributes :id,
:type,
:content,
:section_id
end
component.rb (model):
class Component < ApplicationRecord
# == Associations ==========================
belongs_to :project
belongs_to :section,
class_name: 'Section',
foreign_key: :section_id,
optional: true
end
section.rb (model):
class Section < Component
# == Associations ==========================
has_many :components,
class_name: 'Component',
foreign_key: :component_id,
dependent: :destroy
end
component_controller.rb:
before_action :load_project
before_action :load_scope
def index
components = paginate #scope, per_page: per_page
data = ActiveModelSerializers::SerializableResource.new(
components,
each_serializer: ComponentSerializer
)
render_response(:ok, { data: data })
rescue ActiveRecord::RecordNotFound
render_response(:not_found, { resource: "Project/Lesson" })
end
def load_scope
#scope = #project.components
#scope = #scope.order("position")
end
def load_project
#project = Project.find(params[:project_id])
rescue ActiveRecord::RecordNotFound
render_response(:not_found, { resource: "Project/Lesson" })
end

Related

How to include a nested resource that has a polymorphic association with ActiveRecordSerializer?

I'm using Netflix's jsonapi-rails gem to serialize my API. I need to build a response.json object that includes the associated comments for a post.
Post model:
class Post < ApplicationRecord
has_many :comments, as: :commentable
end
Polymorphic Comment model
class Comment < ApplicationRecord
belongs_to :commentable, polymorphic: true
end
PostSerializer
class PostSerializer
include FastJsonapi::ObjectSerializer
attributes :body
has_many :comments, serializer: CommentSerializer, polymorphic: true
end
CommentSerializer
class CommentSerializer
include FastJsonapi::ObjectSerializer
attributes :body, :id, :created_at
belongs_to :post
end
Posts#index
class PostsController < ApplicationController
def index
#posts = Post.all
hash = PostSerializer.new(#posts).serialized_json
render json: hash
end
end
What I've got so far only gives me the comment type and id, but I NEED the comment's body as well.
Please help!
Thanks in advance~!
Although not very intuitive this behaviour is there by design. According to the JSON API relationship data and actual related resource data belong in different objects of the structure
You can read more here:
Fetching Relationships
Inclusion of Related Resources
To include the body of the Comments your serializers would have to be:
class PostSerializer
include FastJsonapi::ObjectSerializer
attributes :body, :created_at
has_many :comments
end
class CommentSerializer
include FastJsonapi::ObjectSerializer
attributes :body, :created_at
end
and your controller code:
class HomeController < ApplicationController
def index
#posts = Post.all
options = {include: [:comments]}
hash = PostSerializer.new(#posts, options).serialized_json
render json: hash
end
end
The response for a single Post would look something like this:
{
"data": [
{
"attributes": {
"body": "A test post!"
},
"id": "1",
"relationships": {
"comments": {
"data": [
{
"id": "1",
"type": "comment"
},
{
"id": "2",
"type": "comment"
}
]
}
},
"type": "post"
}
],
"included": [
{
"attributes": {
"body": "This is a comment 1 body!",
"created_at": "2018-05-06 22:41:53 UTC"
},
"id": "1",
"type": "comment"
},
{
"attributes": {
"body": "This is a comment 2 body!",
"created_at": "2018-05-06 22:41:59 UTC"
},
"id": "2",
"type": "comment"
}
]
}

How to show all associated items in json_response in Rails

I'm working on an API based Rails application, I don't know how to show all associated items in one request like my models
todo.rb
class Todo < ApplicationRecord
has_many :items, dependent: :destroy
validates_presence_of :title, :created_by
end
item.rb
class Item < ApplicationRecord
belongs_to :todo
validates_presence_of :name
end
in the controller
def show
json_response(#todo)
end
private
def set_todo
#todo = Todo.find(params[:id])
end
The endpoit for single todo
https://example.com/todos/1
and it showing like this
{
"id": 1,
"title": "Hello World!",
"created_by": "2",
"created_at": "2018-04-26T11:19:31.433Z",
"updated_at": "2018-04-26T11:19:31.433Z"
}
My question is how do I show all items which created by this todo in same end point request.
Try using ActiveModel::Serializers::JSON#as_json
For e.g.
def show
render json: #user.as_json(include: :posts), status: 200
end
That should return a response like
{
"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"
}
]
}
def show
render json: {"todos" => {"todo" => #todo, "items" => #todo.items }}, status:200
end
Alternatively you can use serializers for more filters and modifications of response datas

How can show has_many :through Association with ActiveModel::Serializer

I have many to many relation between projects and project_category through project_by_category.
My models:
Project Model
class Project < ApplicationRecord
# Relationships
has_many :project_by_categories
has_many :project_categories, through: :project_by_categories
ProjectCategory Model
class ProjectCategory < ApplicationRecord
has_many :project_by_categories
has_many :projects, through: :project_by_categories
ProjectByCategory Model
class ProjectByCategory < ApplicationRecord
# Relationships
belongs_to :project
belongs_to :project_category
My serializers:
Project Serializer
class ProjectSerializer < ActiveModel::Serializer
attributes :id, :image, :name, :description
Project Category Serializer
class ProjectCategorySerializer < ActiveModel::Serializer
attributes :id, :name
has_many :projects
The expected result:
{
"data": [
{
"id": "1",
"type": "project-categories",
"attributes": {
"name": "3D design basics"
},
"relationships": {
"projects": {
"data": [
{
"id": "1",
"image": "",
"name": "",
"description": "",
"type": "projects"
},
{
"id": "2",
"image": "",
"name": "",
"description": "",
"type": "projects"
}
]
}
}
},
But this is the result:
{
"data": [
{
"id": "1",
"type": "project-categories",
"attributes": {
"name": "3D design basics"
},
"relationships": {
"projects": {
"data": [
{
"id": "1",
"type": "projects"
},
{
"id": "2",
"type": "projects"
}
]
}
}
},
In the project relationship only show me the id and the type.
By last this is my controller
class Api::V1::CategoriesController < ApiController
def index
#categories = ProjectCategory.all
render json: #categories
end
Thank you for your answers!!
Your joins table appears to be setup correctly (and you seem to be grabbing the expected objects from your database), but you are just missing the additional serialized attributes.
Have you tried passing the serializer: or each_serializer: arguments to the respective serializer and controller? For example, your Api::V1::CategoriesController might look like this:
class Api::V1::CategoriesController < ApiController
def index
#categories = ProjectCategory.all
render json: #categories, each_serializer: ProjectCategorySerializer
end
end
Similarly, your ProjectCategorySerializer would contain:
class ProjectCategorySerializer < ActiveModel::Serializer
attributes :id, :name
has_many :projects, each_serializer: ProjectSerializer
I'm not sure if ActiveModel::Serializers are now a part of ActiveSupport, but the library seems to suggest that development has tapered off. I'd recommend switching a a different library if possible.
Hope this helps!

Rails ActiveModel Serializer : Retrieving Deeply Nested ActiveRecord Association

I'm using ActiveModel::Serializer to serialize my json data.
I have three models as follows
class Invoice < ApplicationRecord
has_many :invoiceDetails, inverse_of: :invoice
belongs_to :customer
accepts_nested_attributes_for :invoiceDetails
end
class InvoiceDetail < ApplicationRecord
belongs_to :invoice
belongs_to :product
end
class Product < ApplicationRecord
belongs_to :company
belongs_to :category
belongs_to :user
has_many :invoice_details
end
The serializers are as follows :
class InvoiceSerializer < ActiveModel::Serializer
attributes :id, :total_amount, :balance_amount, :created_at
belongs_to :customer
has_many :invoiceDetails
end
class InvoiceDetailSerializer < ActiveModel::Serializer
attributes :id, :quantity
belongs_to :product
belongs_to :invoice
end
class ProductSerializer < ActiveModel::Serializer
attributes :id, :name, :mrp, :sp, :cp, :stocks, :isPublished
has_one :category
end
When I retrieve an invoice I get the attributes from the associated invoiceDetails model and customer model but the attributes from the product model associated with the invoiceDetails model are missing.
For example if I retrieve an invoice, this is the output :
[
{
"id": 3,
"total_amount": 450,
"balance_amount": 350,
"created_at": "2017-06-27T17:02:20.000Z",
"customer": {
"id": 4,
"company_id": 1,
"name": "vivek",
"isActive": true,
"created_at": "2017-06-27T14:35:50.000Z",
"updated_at": "2017-06-27T14:35:50.000Z",
"mobile": "12345678",
"address": "test",
"pan_number": null,
"tin_number": null,
"party_name": "vipul jwelers"
},
"invoiceDetails": [
{
"id": 4,
"quantity": 1
},
{
"id": 5,
"quantity": 1
}
]
}
]
However if I retrieve invoiceDetail directly I get the associated model attributes.
**[
{
"id": 6,
"quantity": 5,
"product": {
"id": 4,
"name": "Test Prod",
"mrp": 150,
"sp": 130,
"cp": 100,
"stocks": 100,
"isPublished": true
},
"invoice": {
"id": 4,
"total_amount": 3903,
"balance_amount": 3,
"created_at": "2017-07-01T07:45:02.000Z"
}
},
{
"id": 7,
"quantity": 10,
"product": {
"id": 5,
"name": "Test Prod 2",
"mrp": 300,
"sp": 250,
"cp": 200,
"stocks": 10,
"isPublished": true
},
"invoice": {
"id": 4,
"total_amount": 3903,
"balance_amount": 3,
"created_at": "2017-07-01T07:45:02.000Z"
}
}
]**
So for retrieving the nested attributes directly from invoice, do I need to change the relationship among my models?
Has someone encountered the same problems, or any work around you can suggest?
If someone is stuck over this problem, here is what I did :
Whenever I want to retrieve the deeply nested association I add the "include **" keyword in the controller during response.
For example :
def show
cust_id = params[:customer_id]
invoice_id = params[:id]
if cust_id && invoice_id
invoice = Invoice.where(:id => invoice_id, :customer_id => cust_id)
render json: invoice, include: '**', status: 200
else
render json: { errors: "Customer ID or Invoice ID is NULL" }, status: 422
end
end
The include * * will retrieve all the nested attributes for the invoice model using the serializer if defined for the respective models.

Active Model Serializer not working with json_api adapter

I am trying to use custom serializers for the relationships in a serializer and the json_api adapter enabled. However the relationships are not serialized correctly (or, better, not at all displayed/serialized).
PostController.rb
def index
render json: Post.all, each_serializer: Serializers::PostSerializer
end
Serializer
module Api
module V1
module Serializers
class PostSerializer < ActiveModel::Serializer
attributes :title, :id
belongs_to :author, serializer: UserSerializer
has_many :post_sections, serializer: PostSectionSerializer
end
end
end
end
JSON output:
{
"data": [
{
"attributes": {
"title": "Test Title"
},
"id": "1",
"relationships": {
"author": {
"data": {
"id": "1",
"type": "users"
}
},
"post_sections": {
"data": [
{
"id": "1",
"type": "post_sections"
}
]
}
},
"type": "posts"
}
]
}
As you can see, the relationships are not fulfilled, which happens only if I specify a custom serializer for the relationships!!
If I do something like this:
module Api
module V1
module Serializers
class PostSerializer < ActiveModel::Serializer
attributes :title, :id
belongs_to :author # no custom serializer!
has_many :post_sections # no custom serializer!
end
end
end
end
The relationships are shown correctly, but not using a custom serializer...
What's the issue here ?
EDIT
According to the json API 1.0 Format, what I am getting back is the so-called resource identifier object.
The following primary data is a single resource identifier object that
references the same resource:
{ "data": {
"type": "articles",
"id": "1" } }
Is there a way to prevent getting resource identifier objects, and get the actual data instead ?
Relationships only returns id and type according to json-api exmaples. If you need to return more information about this relation you should add include option on your render action.
Ex.
PostController.rb
class PostsController < ApplicationController
def show
render json: #post, include: 'comments'
end
end
Serializers
class PostSerializer < ActiveModel::Serializer
attributes :id, :title, :content
has_many :comment, serializer: CommentSerializer
end
class CommentSerializer < ActiveModel::Serializer
attributes :id, :title, :content
end
JSON output:
{
"data": {
"id": "1",
"type": "post",
"attributes": {
"title": "bla",
"content": "bla"
},
"relationships": {
"comment": {
"data": [
{
"type": "comments",
"id": "1"
}
]
}
}
},
"included": {
{
"id": "1",
"type": "comments",
"attributes": {
"title": "test",
"content": "test"
}
}
]
}
Just to add to #Bruno Bacarini's answer, you may also include chained associations by using:
render #posts, include: ['authors.profile', 'comments']
source: joaomdmoura's comment on github

Resources