Rendering a JSON object with the merging of different models

Asked

Viewed 226 times

0

I am developing an API with Rails 5 and have the following models:

class cotacao < ApplicationRecord
  belongs_to :usuario
  has_many :cotacao_itens, dependent: :destroy

  accepts_nested_attributes_for :cotacao_itens, allow_destroy: true
end

class CotacaoItem < ApplicationRecord
  belongs_to :cotacao
end

class Peca < ApplicationRecord
  belongs_to :fornecedor
end

class Fornecedor < ApplicationRecord
  belongs_to :usuario
  has_many :pecas
end

Now I need to create a method that from a quotation, is presented all the pieces grouped by forcenecedor, and rendered a JSON object with the following structure:

[
    {
        "id": 1,
        "name": "fornecedor 1",
        "pecas": [
            {"numero": "1999"},
            {"numero": "2555"}]
    },
    {
        "id": 2,
        "name": "fornecedor 2",
        "pecas": [
            {"numero": "1999"},
            {"numero": "2555"},
            {"numero": "3666"}]
    }
]

So far this (incomplete) method is as follows:

def quotation_suppliers
    quotation = Quotation.find(params[:id])

    parts = []
    suppliers = []

    quotation.quotation_items.each do |item|
      parts << Part.select { |part| part.part_number == item.part_number }
    end

    render json: parts, status: 200
  end

I am using Activemodel Serializer to format the JSON output, but my difficulty is precisely in creating the "quotation_suppliers" method and the logic needed to create this result.

EDIT

I was able to get very close to the result I’m looking for by creating the following method in models/quotation.Rb

def parts
    part_numbers = self.quotation_items.map { |item| item.part_number }
    parts = part_numbers.map { |item| Part.where(part_number: item) }
    parts = parts.flatten.group_by(&:supplier_id)
end

This method prints a hash grouped by the Supplier ID, but I need to present another attribute, instead of the ID would be the social_name. So I added the following code:

suppliers = parts.keys.map { |k| parts[Supplier.find(k).social_name] = parts.delete(k) }

This way I can see the result I need in the console, but in Postman the hash is without the social_name. I believe this is related to quotation_serializer configuration:

class Api::V2::QuotationSerializer < ActiveModel::Serializer
  attributes :parts
end

But I still don’t know what I need to do to also present the attributo social_name in the hash.

2 answers

0

Activemodel::Serializer allows you to have multiple serializers for the same model.

class SimplePartSerializer < ActiveModel::Serializer
  attributes :id, :name
end

class ComplexPartSerializer < ActiveModel::Serializer
  has_many :suppliers
  attributes :id, :name, :model_id, :created_at, :updated_at
end

To use, you only need to specify which serializer you want to use, so:

# para múltiplas instâncias de part (no caso de um array)
render json: parts, status: 200, each_serializer: ComplexPartSerializer

# para uma só instância de part (no caso um objeto único)
render json: @people, serializer: ComplexPartSerializer
  • Interesting your vnbrs tip, but my question is on how to create the logic of the method "quotation_suppliers" so that it brings the information I need.

0


I managed to solve it! I arrived at the result I needed with the following method:

def parts
    part_numbers = self.quotation_items.map { |item| item.part_number }
    parts = part_numbers.map { |item| Part.where(part_number: item) }
    parts = parts.flatten.group_by(&:supplier_id)
    parts.keys.map { |k| parts[Supplier.find(k).social_name] = parts.delete(k) }
    parts
  end

Browser other questions tagged

You are not signed in. Login or sign up in order to post.