Serializing deeply nested associations with active_model_serializers

前端 未结 6 1641
無奈伤痛
無奈伤痛 2020-12-08 06:37

I\'m using Rails 4.2.1 and active_model_serializers 0.10.0.rc2

I\'m new to API\'s and chose active_model_serializers because i

相关标签:
6条回答
  • 2020-12-08 07:09

    This should do what you're looking for.

    @project.to_json( include: { estimates: { include: {:project, :project_code, :tax_type, :proposals } } } )

    The top level nesting will be automatically included, but anything deeper than that will need to be included in your show action or wherever you are calling this.

    0 讨论(0)
  • 2020-12-08 07:15

    Per commit 1426: https://github.com/rails-api/active_model_serializers/pull/1426 - and related discussion, you can see that the default nesting for json and attributes serialization is one level.

    If you want deep nesting by default, you can set a configuration property in an active_model_serializer initializer:

    ActiveModelSerializers.config.default_includes = '**'

    For detailed reference from v0.10.6: https://github.com/rails-api/active_model_serializers/blob/v0.10.6/docs/general/adapters.md#include-option

    0 讨论(0)
  • 2020-12-08 07:17

    In my case I created a file called 'active_model_serializer.rb' placed at 'MyApp/config/initializers' with the following content:

    ActiveModelSerializers.config.default_includes = '**'
    

    Do not forget to restart the server:

    $ rails s
    
    0 讨论(0)
  • 2020-12-08 07:27

    You can change default_includes for the ActiveModel::Serializer:

    # config/initializers/active_model_serializer.rb
    ActiveModel::Serializer.config.default_includes = '**' # (default '*')
    

    In addition, in order to avoid infinite recursion, you can control the nested serialization follows:

    class UserSerializer < ActiveModel::Serializer
      include Rails.application.routes.url_helpers
    
      attributes :id, :phone_number, :links, :current_team_id
    
      # Using serializer from app/serializers/profile_serializer.rb
      has_one :profile
      # Using serializer described below:
      # UserSerializer::TeamSerializer
      has_many :teams
    
      def links
        {
          self: user_path(object.id),
          api: api_v1_user_path(id: object.id, format: :json)
        }
      end
    
      def current_team_id
        object.teams&.first&.id
      end
    
      class TeamSerializer < ActiveModel::Serializer
        attributes :id, :name, :image_url, :user_id
    
        # Using serializer described below:
        # UserSerializer::TeamSerializer::GameSerializer
        has_many :games
    
        class GameSerializer < ActiveModel::Serializer
          attributes :id, :kind, :address, :date_at
    
          # Using serializer from app/serializers/gamers_serializer.rb
          has_many :gamers
        end
      end
    end
    

    Result:

    {
       "user":{
          "id":1,
          "phone_number":"79202700000",
          "links":{
             "self":"/users/1",
             "api":"/api/v1/users/1.json"
          },
          "current_team_id":1,
          "profile":{
             "id":1,
             "name":"Alexander Kalinichev",
             "username":"Blackchestnut",
             "birthday_on":"1982-11-19",
             "avatar_url":null
          },
          "teams":[
             {
                "id":1,
                "name":"Agile Season",
                "image_url":null,
                "user_id":1,
                "games":[
                   {
                      "id":13,
                      "kind":"training",
                      "address":"",
                      "date_at":"2016-12-21T10:05:00.000Z",
                      "gamers":[
                         {
                            "id":17,
                            "user_id":1,
                            "game_id":13,
                            "line":1,
                            "created_at":"2016-11-21T10:05:54.653Z",
                            "updated_at":"2016-11-21T10:05:54.653Z"
                         }
                      ]
                   }
                ]
             }
          ]
       }
    }
    
    0 讨论(0)
  • 2020-12-08 07:32

    If you are using the JSONAPI adapter you can do the following to render nested relationships:

    render json: @project, include: ['estimates', 'estimates.project_code', 'estimates.tax_type', 'estimates.proposals']
    

    You can read more from the jsonapi documentation:http://jsonapi.org/format/#fetching-includes

    0 讨论(0)
  • 2020-12-08 07:33

    So this my not be the best or even a good answer, but this is working how I need it to.

    While including nested and side-loaded attributes appears to be supported when using the json_api adapter with AMS, I needed support for flat json. In addition, this method worked well because each serializer is specifically generating exactly what I need it to independent of any other serializer and without having to do anything in the controller.

    Comments / alternate methods are always welcome.

    Project Model

    class Project < ActiveRecord::Base      
      has_many  :estimates, autosave: true, dependent: :destroy
    end
    

    ProjectsController

    def index
      @projects = Project.all
      render json: @projects
    end
    

    ProjectSerializer

    class ProjectSerializer < ActiveModel::Serializer
      attributes  :id, 
                  :name,
                  :updated_at,
    
                  # has_many
                  :estimates
    
    
    
      def estimates
        customized_estimates = []
    
        object.estimates.each do |estimate|
          # Assign object attributes (returns a hash)
          # ===========================================================
          custom_estimate = estimate.attributes
    
    
          # Custom nested and side-loaded attributes
          # ===========================================================
          # belongs_to
          custom_estimate[:project] = estimate.project.slice(:id, :name) # get only :id and :name for the project
          custom_estimate[:project_code] = estimate.project_code
          custom_estimate[:tax_type] = estimate.tax_type
    
          # has_many w/only specified attributes
          custom_estimate[:proposals] = estimate.proposals.collect{|proposal| proposal.slice(:id, :name, :updated_at)}
    
          # ===========================================================
          customized_estimates.push(custom_estimate)
        end
    
        return customized_estimates
      end
    end
    

    Result

    [
      {
        "id": 1,
        "name": "123 Park Ave.",
        "updated_at": "2015-08-09T02:36:23.950Z",
        "estimates": [
          {
            "id": 1,
            "name": "E1",
            "release_version": "v1.0",
            "exchange_rate": "0.0",
            "created_at": "2015-08-12T04:23:38.183Z",
            "updated_at": "2015-08-12T04:23:38.183Z",
            "project": {
              "id": 1,
              "name": "123 Park Ave."
            },
            "project_code": {
              "id": 8,
              "valuation": 30,
              "created_at": "2015-08-09T18:02:42.079Z",
              "updated_at": "2015-08-09T18:02:42.079Z"
            },
            "tax_type": {
              "id": 1,
              "name": "No Tax",
              "created_at": "2015-08-09T18:02:42.079Z",
              "updated_at": "2015-08-09T18:02:42.079Z"
            },
            "proposals": [
              {
                "id": 1,
                "name": "P1",
                "updated_at": "2015-08-12T04:23:38.183Z"
              },
              {
                "id": 2,
                "name": "P2",
                "updated_at": "2015-10-12T04:23:38.183Z"
              }
            ]
          }
        ]
      }
    ]
    

    I basically disregarded trying to implement any has_many or belongs_to associations in the serializers and just customized the behavior. I used slice to select specific attributes. Hopefully a more elegant solution will be forth coming.

    0 讨论(0)
提交回复
热议问题