Rendering a JSON object with the join of different models

0

I'm developing an API with rails 5 and I 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, show all the pieces grouped by forcenecedor, and render 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'm using the ActiveModel Serializer to format the output of JSON, 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 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 needs to present another attribute, instead of the ID would be social_name. So I added the following code:

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

That way I can see the result I need in the console, but in the postman the hash is left without social_name. I think this is related to the quotation_serializer configuration:

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

But I still do not know what I need to do to also show the social_name attribute in the hash.

    
asked by anonymous 05.10.2018 / 20:18

2 answers

0

I was able to solve it! I got 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
    
08.10.2018 / 17:55
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 just need to specify which serializer you want to use, like this:

# 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
    
06.10.2018 / 14:35