I'm using active_model_serializers from an api in ruby on rails, and I have a controller method in which i save an invoice and its nested items with some calculations, the problem is that after include serializer, the nested attributes are restricted and I can't access to them.
I have the code in this way according to some documentation, but it doesn't works
#Controller
def invoice_params
params.require(:invoice).permit(:person_id, :date, sales_attributes: [:reference_id, :quantity, :price])
end
#Model
class Invoice < ApplicationRecord
belongs_to :person
has_many :sales
accepts_nested_attributes_for :sales, allow_destroy: true
end
#Serializer
class InvoiceSerializer < ActiveModel::Serializer
attributes :id, :date, :total, :profit, :consecutive, :person_id
has_many :sales, root: :sales_attributes
belongs_to :person
end
The json request that I'm sending is:
{
"person_id": 4,
"date": "2019-03-20",
"sales": [
{
"reference_id":1,
"quantity": 90000,
"price": 240
},
{
"reference_id":1,
"quantity": 50000,
"price": 240
}
]
}
Some one knows what happen?, before of include the serializer gem it was working fine.
Thanks in advance!
change
def invoice_params
params.require(:invoice).permit(:person_id, :date, sales_attributes: [:reference_id, :quantity, :price])
end
to
def invoice_params
params.require(:invoice).permit(:person_id, :date, sales_attributes: [:id, :reference_id, :quantity, :price])
end
and
{
"person_id": 4,
"date": "2019-03-20",
"sales": [
{
"reference_id":1,
"quantity": 90000,
"price": 240
},
{
"reference_id":1,
"quantity": 50000,
"price": 240
}
]
}
to
{
"person_id": 4,
"date": "2019-03-20",
"sales_attributes": [
{
"reference_id":1,
"quantity": 90000,
"price": 240
},
{
"reference_id":1,
"quantity": 50000,
"price": 240
}
]
}
Related
I have a model Category that use awesome_nested_set gem, so it has children of the model itself. I have created CategorySerializer for the model
class CategorySerializer < ActiveModel::Serializer
attributes :id, :parent_id, :lft, :rgt, :text, :permalink, :children
def children
object.children
end
end
But children is not serialized. I have also tried add has_many :children, serializer: self, the result is this
{
"id": 25918,
"parent_id": null,
"lft": 3,
"rgt": 8,
"text": "ARAG",
"permalink": "25918-arag",
"children": [
{
"id": 25919,
"parent_id": 25918,
"lft": 4,
"rgt": 7,
"text": "Coperchi",
"permalink": "25919-coperchi",
"children": [
{
"id": 25920,
"parent_id": 25919,
"lft": 5,
"rgt": 6,
"text": "Ribaltabili",
"description": "",
"page_title": "",
"meta_key": "",
"meta_description": "",
"key_1": null,
"key_2": null,
"key_3": null,
"extra": null,
"created_at": "2019-03-01T21:08:15.000+01:00",
"updated_at": "2019-04-02T12:27:05.000+02:00"
}
]
}
]
}
Second level of children is successfully serialized but it children is not. Is there a way or alternative to serialize all object children?
If you want deep nesting by default, then you can set following config property in the initializer file
# config/initializers/active_model_serializer.rb
ActiveModelSerializers.config.default_includes = '**
For more details, you can check this.
You can also add another serializer for children as follow
class CategorySerializer < ActiveModel::Serializer
attributes :id, :parent_id, :lft, :rgt, :text, :permalink, :children
def children
ActiveModel::SerializableResource.new(object.children, each_serializer: ChildrenSerializer)
end
end
For more information, you can refer to this link
How about this solution with invoking serializer on children? Be careful to not get into infinite loop with deep nesting
def children
object.children.map { |obj| SomeSerializer.new(obj) }
end
I'm using ActiveModelSerializers 0.10.2. - When I Deserializer my params, I'm unable to get my relationships to show up. I have a POST request with the payload:
{
"data": {
"type": "project_toolbox_talks",
"attributes": {
"date": "2017-12-11"
}
},
"relationships": {
"attendees": {
"data": [
{
"type": "atendees",
"id": "559ff2c9-beb6-47cd-9757-66104617403b"
}
]
},
"projects": {
"data": {
"type": "projects",
"id": "d9b28ffd-6f30-4dd0-a227-720caa9b881e"
}
}
}
}
My Serializer for ProjectToolboxTalks is:
module API
module V1
class ProjectToolboxTalkSerializer < ActiveModel::Serializer
attributes :date
has_one :toolbox_talk
has_one :superintendent
has_one :project
has_many :attendees
has_many :project_toolbox_talk_users
end
end
end
In my controller when I call:
ActiveModelSerializers::Deserialization.jsonapi_parse!(
params, only: [:date, :attendees, :projects]
)
the only thing that is returned is {:date=>"2017-12-11"}
Why are my attendees or projects relationships not being returned?
ActiveModelSerializers 0.10.2 + Rails 5.1.2 + Ruby 2.4.2
The key 'relationships' needs to be inside the first 'data'.
I'm seeing a strange behaviour regarding rails 5, active model serializer and the json-api adapter.
Given the following User model with the Rolify gem:
class User < ActiveRecord::Base
#
# Gem Includes
#
rolify
# Include devise modules.
devise :database_authenticatable,
:recoverable, :rememberable, :trackable, :validatable
include DeviseTokenAuth::Concerns::User
#
# Callbacks
#
after_create :assign_default_role
#
# Attributes
#
attr_accessor :remote_image
#
# Validations
#
validates :name, presence: true, length: {in: 1..100}
validates :last_name, presence: true, length: {in: 1..100}
validates :role_ids, presence: true, on: :update
#
# Relations
#
belongs_to :current_scenario, class_name: "Scenario"
#
# Private Instance Methods
#
def assign_default_role
self.add_role(:user) if self.roles.blank?
end
end
and the following controller code:
def show
#user = User.find(params[:id])
authorize #user
render json: #user, include: ['roles'], status: :ok
end
As you can see, I'm including the roles relationship to be rendered as part of the json api response, with json-api adapter format.
FYI, the UserSerializer:
class UserSerializer < ActiveModel::Serializer
#
# Attributes
#
attributes :id, :email, :name, :last_name, :image_url, :image_thumb_url, :created_at, :updated_at, :current_scenario_id, :last_sign_in_at
#
# Relations
#
has_one :current_scenario
has_many :roles
#
# Methods
#
def image_url
object.image_url
end
def image_thumb_url
object.image_url(:thumb)
end
end
When retrieving the json response, I get the following:
{
"data": {
"id":"2",
"type":"users",
"attributes": {
"email":"talvarez#igaltex.com.ar", ...
},
"relationships": {
"current-scenario": {
"data": {
"id":"204",
"type":"scenarios"
}
},
"roles": {
"data": [
{
"id":1,
"name":"user",
"resource-type":null,
"resource-id":null,
"created-at":"2017-01-23T10:27:08.707-03:00",
"updated-at":"2017-01-23T10:27:08.707-03:00"
},
{
"id":2,
"name":"admin",
"resource-type":null,
"resource-id":null,
"created-at":"2017-01-24T09:40:53.020-03:00",
"updated-at":"2017-01-24T09:40:53.020-03:00"
}
]
}
}
}
}
As you can see, the included relationship roles with all its attributes is inside the relationships fragment of the json-api response. Shouldn't the roles data be inside the included fragment, which by the way is missing? Moreover inside the relationship fragment roles should appear only as a reference like: {relationships: {roles: [{id: "1", type: "role"}, {id: "2", type: "role"}]} am I wrong?
To contrast this, look what happens when also including the current_scenario relationship:
{
"data": {
"id":"2",
"type":"users",
"attributes": {
"email":"talvarez#igaltex.com.ar",
"name":"Tomás",
"last-name":"Alvarez",
...
},
"relationships": {
"current-scenario": {
"data": {
"id":"204",
"type":"scenarios"
}
},
"roles": {
"data": [
{
"id":1,
"name":"user",
"resource-type":null,
...
}
]
}
},
"included": [
{
"id":"204",
"type":"scenarios",
"attributes": {
"name":"Scenario reload II",
"description":null,
"created-at":"2017-04-18T11:55:35.242-03:00",
"updated-at":"2017-04-18T11:55:35.242-03:00"
},
"relationships": {
"scenario-stocks": {
"data":[]
}
}
}
]
}
}
See how now the included fragment appears with all the information about current_scenario and only the reference to current_scenario is added to the relationships fragment. Is this because roles is a has_many relationship in the active model serializer while current_scenario is a belongs_to ? Am I understanding wrong the json-api adapter specification?
Many thanks!
Ouch. The inconsistency in the JSON-API response was because i forgot to add a Role model serializer in the backend side (Rails 5). This is the json response now, which is what i was looking for:
{
"data": {
"id": "2",
"type": "users",
"attributes": {
"email": "talvarez#igaltex.com.ar",
"name": "Tomás",
"last-name": "Alvarez",
"image-url": "http://localhost:3001/uploads/user/image/2/05a4dc7.jpg",
"image-thumb-url": "http://localhost:3001/uploads/user/image/2/thumb_05a4dc7.jpg",
"created-at": "2017-01-23T10:39:12.485-03:00",
"updated-at": "2017-04-25T16:32:14.610-03:00",
"current-scenario-id": 204,
"last-sign-in-at": "2017-04-25T16:29:03.559-03:00"
},
"relationships": {
"current-scenario": {
"data": {
"id": "204",
"type": "scenarios"
}
},
"roles": {
"data": [{
"id": "1",
"type": "roles"
}, {
"id": "2",
"type": "roles"
}]
}
}
},
"included": [{
"id": "204",
"type": "scenarios",
"attributes": {
"name": "Scenario reload II",
"description": null,
"created-at": "2017-04-18T11:55:35.242-03:00",
"updated-at": "2017-04-18T11:55:35.242-03:00"
},
"relationships": {
"scenario-stocks": {
"data": []
}
}
}, {
"id": "1",
"type": "roles",
"attributes": {
"name": "user"
}
}, {
"id": "2",
"type": "roles",
"attributes": {
"name": "admin"
}
}]
}
Sorry for this bug. We haven't figured out how to determine the type for a relationship when no serializer is found. Would it have been more helpful if you got an exception?
This is how JSON API works you can not retrieve desired model + relationship in 'one object'. Relationships always are separated. So you need to 'glue' it somehow. You can use gem that can help you with it, or you can do it on front end side (all big frameworks support it).
In general this approach with 'relationships' looks weird, but when you have complex object with tons of dependencies, this is the only one way that works.
I am using Rails 5 and AMS 10. My questions are in bold.
gem 'active_model_serializers', '~> 0.10.0.rc1'
rails -v
Rails 5.0.0.beta3
I am using the JSON API specifications. I have this in an config/initializers/active_model_serializer.rb file:
ActiveModel::Serializer.config.adapter = :json_api
Is the point of this to apply convention to JSON API responses which have total freedom in customization of the nodes? This leads to faster understanding of the data from developers who consume the API for like front-end Ember apps or mobile native apps?
This is my RentalUnitSerializer:
class RentalUnitSerializer < ActiveModel::Serializer
attributes :id, :rooms, :bathrooms, :price, :price_cents
belongs_to :user
end
This is my UserSerializer:
class UserSerializer < ActiveModel::Serializer
attributes :id, :name, :email
def name
names = object.name.split(" ")
"#{names[0].first}. #{names[1]}"
end
end
This is my rental_units_controller:
class RentalUnitsController < ApplicationController
before_action :set_rental_unit, only: [:show, :update, :destroy]
# GET /rental_units
def index
#rental_units = RentalUnit.all
render json: #rental_units
end
This is my JSON response when I hit the /rental_units endpoint.
{
"data": [{
"id": "1",
"type": "rental_units",
"attributes": {
"rooms": 2,
"bathrooms": 2,
"price": null,
"price_cents": 50000
},
"relationships": {
"user": {
"data": {
"id": "1",
"type": "users"
}
}
}
}, {
"id": "2",
"type": "rental_units",
"attributes": {
"rooms": 2,
"bathrooms": 2,
"price": null,
"price_cents": 50000
},
"relationships": {
"user": {
"data": {
"id": "1",
"type": "users"
}
}
}
},
How do I get the user's name in the relationships section of the JSON API response?
You need to add .includes(:user) to your query on the first line of your index method in RentalUnitsController, thusly:
#rental_units = RentalUnit.includes(:user).all
That will add the actual data from the :user relationship's target object to your RentalUnit object and to the serialized JSON.
I've built a small API that, when posted a JSON object, creates the representative model records. The data looks like this:
{
"customer": {
"email": "michael#myemail.com",
"first_name": "Michael T. Smith",
"last_name": "",
"shipping_address_1": "",
"telephone": "5551211212",
"source": "Purchase"
},
"order": {
"system_order_id": "1070",
"shipping_address_1": "",
"billing_address_1": "123 Your Street",
"shipping": "0",
"tax": "0",
"total": "299",
"invoice_date": 1321157461,
"status": "PROCESSING",
"additional_specs": "This is my info!",
"line_items": [
{
"quantity": "1",
"price": "239",
"product": "Thing A",
"comments": "comments"
"specification": {
"width": "12",
"length": "12",
},
},
{
"quantity": "1",
"price": "239",
"product": "Thing A",
"comments": "comments"
"specification": {
"width": "12",
"length": "12",
},
},
]
}
}
The question is how to create the nested objects. My models are setup as such:
class Order < ActiveRecord::Base
has_many :line_items
belongs_to :customer
accepts_nested_attributes_for :line_items
end
class LineItem < ActiveRecord::Base
belongs_to :order
has_many :specifications
end
class Specification < ActiveRecord::Base
belongs_to :LineItem
end
I'm trying to create the records using this code:
#order = #customer.orders.build(#data[:order])
#order.save
Is there a better way to do this? Currently I'm getting this error: ActiveRecord::AssociationTypeMismatch in ApiController#purchase_request LineItem(#70310607516240) expected, got Hash(#70310854628220)
Thanks!
accepts_nested_attributes_for defines a new setter method for the association: the original name with _attributes appended to it.
In your case, there is a line_items_attributes= method on your Order model, which is what you need to use to take advantage of the nested attributes feature. Something as simple as swapping the key before building the model would probably work, e.g.:
#data[:order][:line_items_attributes] = #data[:order].delete(:line_items)
#order = #customer.orders.build(#data[:order])
#order.save
You can use accepts_nested_attributes_for like this:
(in my example, Products has_many ProductionItineraries and ProductionItineraries belongs_to Products)
model/product.rb
has_many :production_itineraries, dependent: :delete_all
accepts_nested_attributes_for :production_itineraries, allow_destroy: true
model/production_itinerary.rb
belongs_to :product
To instantiate:
products = Product.new
products.production_itineraries.build(other_production_itineraries_fields)
Doing this, after save products, the production_itinerary object will be save automatically, with the respective product_id field