rails fast_jsonapi fast custom active ruby-on-rails json active-model-serializers

ruby on rails - fast_jsonapi - Serialización de asociaciones profundamente anidadas con active_model_serializers



rails serializer (6)

Así que esta no es la mejor o incluso una buena respuesta, pero está funcionando como lo necesito.

Mientras que la inclusión de atributos anidados y cargados lateralmente parece ser compatible cuando se usa el adaptador json_api con AMS, necesitaba soporte para json plano. Además, este método funcionó bien porque cada serializador genera específicamente exactamente lo que necesito para ser independiente de cualquier otro serializador y sin tener que hacer nada en el controlador.

Comentarios / métodos alternativos son siempre bienvenidos.

Modelo de proyecto

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

ProyectosController

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

Resultado

[ { "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" } ] } ] } ]

Básicamente, ignoré el intento de implementar cualquier has_many o belongs_to en los serializadores y solo personalicé el comportamiento. Utilicé slice para seleccionar atributos específicos. Esperemos que venga una solución más elegante.

Estoy usando Rails 4.2.1 y active_model_serializers 0.10.0.rc2

Soy nuevo en las API y elegí active_model_serializers porque parece que se está convirtiendo en el estándar para los rieles (aunque no estoy en contra de usar RABL u otro serializador)

El problema que tengo es que parece que no puedo incluir varios atributos en relaciones de varios niveles. Por ejemplo tengo:

Proyectos

class ProjectSerializer < ActiveModel::Serializer attributes :id, :name, :updated_at has_many :estimates, include_nested_associations: true end

y estimaciones

class EstimateSerializer < ActiveModel::Serializer attributes :id, :name, :release_version, :exchange_rate, :updated_at, :project_id, :project_code_id, :tax_type_id belongs_to :project belongs_to :project_code belongs_to :tax_type has_many :proposals end

Propuestas

class ProposalSerializer < ActiveModel::Serializer attributes :id, :name, :updated_at, :estimate_id belongs_to :estimate end

Cuando llego a los /projects/1 lo anterior produce:

{ "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", "updated_at": "2015-08-12T04:23:38.183Z", "project_id": 1, "project_code_id": 8, "tax_type_id": 1 } ] }

Sin embargo, lo que me gustaría producir es:

{ "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", "updated_at": "2015-08-12T04:23:38.183Z", "project": { "id": 1, "name": "123 Park Ave." }, "project_code": { "id": 8, "valuation": 30 }, "tax_type": { "id": 1, "name": "no-tax" }, "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" } ] } ] }

Idealmente, también me gustaría poder especificar qué atributos, asociaciones y atributos de esas asociaciones se incluyen en cada serializador.

He estado revisando los problemas de AMS, y parece que hay algo de ida y vuelta sobre cómo se debe manejar esto (o si este tipo de funcionalidad es realmente compatible) pero estoy teniendo dificultades para averiguar exactamente qué es lo actual. estado es.

Una de las soluciones propuestas fue anular el atributo con un método para llamar a los atributos anidados, pero eso parece ser considerado como un hack, por lo que quería evitarlo si es posible.

De todos modos, se agradecería mucho un ejemplo de qué hacer acerca de esto o el consejo general de la API.


En mi caso, creé un archivo llamado ''active_model_serializer.rb'' ubicado en ''MyApp / config / initializers'' con el siguiente contenido:

ActiveModelSerializers.config.default_includes = ''**''

No te olvides de reiniciar el servidor:

$ rails s


Esto debería hacer lo que estás buscando.

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

El anidamiento de nivel superior se incluirá automáticamente, pero cualquier cosa más profunda que eso deberá incluirse en la acción de su programa o donde sea que llame.


Puede cambiar las default_includes para ActiveModel::Serializer :

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

Además, para evitar la recursión infinita, puede controlar la siguiente serialización anidada:

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

Resultado:

{ "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" } ] } ] } ] } }


Según el compromiso 1426: https://github.com/rails-api/active_model_serializers/pull/1426 - y discusión relacionada, puede ver que el anidamiento predeterminado para la serialización json y los attributes es un nivel.

Si desea un anidamiento profundo de forma predeterminada, puede establecer una propiedad de configuración en un inicializador active_model_serializer:

ActiveModelSerializers.config.default_includes = ''**''

Para referencia detallada de v0.10.6 : https://github.com/rails-api/active_model_serializers/blob/v0.10.6/docs/general/adapters.md#include-option


Si está utilizando el adaptador JSONAPI, puede hacer lo siguiente para representar relaciones anidadas:

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

Puede leer más en la documentación de jsonapi: http://jsonapi.org/format/#fetching-includes