В Сериализаторах активных моделей.. Допустим, у меня есть CompanySerializer
и EmployeeSerializer
. В CompanySerializer
у меня есть поле ceo
, в котором я хочу отобразить связь с EmployeeSerializer
, но показать только подмножество полей из EmployeeSerializer
.
Для целей этого примера предположим, что EmployeeSerializer
имеет атрибуты name
, homepage
и salary
, но на CompanySerializer.ceo
мы хотим показывать только name
и homepage
.
Я хотел бы избежать переопределения нового класса CompanyEmployeeSerializer
, если я могу просто «выбрать» поля, которые мне нужны для ассоциации сотрудников внутри CompanySerializer
.
Я также хочу избежать решения, которое запускает EmployeeSerializer
, а затем выполняет срезы для определенных полей. Причина этого в том, что я хочу избежать запуска кода, связанного с сериализацией нежелательных полей.
Примечание. Это отличается от вопроса о том, как скрыть определенные поля внутри сериализатора на основе условия. Речь идет о том, как можно вызвать сериализатор из другого сериализатора и получить определенные поля.
Если бы что-то подобное существовало, я был бы очень рад:
CompanySerializer < ActiveModel::Serializer
belongs_to :ceo, serializer: EmployeeSerializer, fields: [:name, :homepage]
end
@max Я это видел, но это на уровне контроллера, а не на уровне сериализатора. Я хочу, чтобы ассоциация в сериализаторе выбирала правильные поля для сериализации.
Я добавил примечание @engineersmnky, но хочу отметить, что вам никогда не следовало отмечать это для закрытия, очевидно, это отличается от вопроса об условных полях, и это лишнее.
@BenG, насколько я понимаю, вы хотите условно отобразить salary
для конкретного сотрудника, который находится в поле ceo
. Нет ли в этом объекте ничего, что квалифицировало бы его как ceo
при сериализации?
@engineersmnky Будет две конечные точки: компании/ и сотрудники/. Таким образом, условием является то, запрашиваете ли вы его как связь с компаниями или просто запрашиваете сотрудников отдельно. Во-первых, условие — не лучший способ думать об этом, потому что в каждом случае вы имеете дело с разными сериализаторами. Если я добавлю третью конечную точку, я также смогу указать там конкретные поля, которые мне нужны, из ассоциации сотрудников.
Я привел пример того, что решило бы проблему, если бы она существовала.
Это настолько близко, насколько я мог получить:
# app/serializers/company_serializer.rb
class CompanySerializer < ActiveModel::Serializer
belongs_to :ceo do
EmployeeSerializer.new(object.ceo).attributes([:name, :homepage])
end
end
Не могли бы вы сделать это таким образом?
# app/serializers/company_serializer.rb
class CompanySerializer < ActiveModel::Serializer
belongs_to :ceo, serializer: EmployeeSerializer, fields: [:name]
end
Вам придется копнуть глубже:
$ git diff
diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb
index 5e34779..e50f44f 100644
--- a/lib/active_model/serializer.rb
+++ b/lib/active_model/serializer.rb
@@ -367,7 +367,9 @@ module ActiveModel
adapter_options ||= {}
options[:include_directive] ||= ActiveModel::Serializer.include_directive_from_options(adapter_options)
if (fieldset = adapter_options[:fieldset])
- options[:fields] = fieldset.fields_for(json_key)
+ if fields = fieldset.fields_for(json_key)
+ options[:fields] = fields
+ end
end
resource = attributes_hash(adapter_options, options, adapter_instance)
relationships = associations_hash(adapter_options, options, adapter_instance)
diff --git a/lib/active_model/serializer/association.rb b/lib/active_model/serializer/association.rb
index 7aeee33..ce0cd29 100644
--- a/lib/active_model/serializer/association.rb
+++ b/lib/active_model/serializer/association.rb
@@ -55,7 +55,8 @@ module ActiveModel
association_object = association_serializer && association_serializer.object
return unless association_object
- serialization = association_serializer.serializable_hash(adapter_options, {}, adapter_instance)
+ options = {fields: reflection.options[:fields]}.compact
+ serialization = association_serializer.serializable_hash(adapter_options, options, adapter_instance)
if polymorphic? && serialization
polymorphic_type = association_object.class.name.underscore
Патч Monkey, чтобы попробовать:
# config/initializers/serializer_patch.rb
ActiveModel::Serializer::Association.class_eval do
def serializable_hash(adapter_options, adapter_instance)
association_serializer = lazy_association.serializer
return virtual_value if virtual_value
association_object = association_serializer && association_serializer.object
return unless association_object
# pass `fields:` option from the reflection to serializer
options = {fields: reflection.options[:fields]}.compact
serialization = association_serializer.serializable_hash(adapter_options, options, adapter_instance)
if polymorphic? && serialization
polymorphic_type = association_object.class.name.underscore
serialization = {type: polymorphic_type, polymorphic_type.to_sym => serialization}
end
serialization
end
end
ActiveModel::Serializer.class_eval do
def serializable_hash(adapter_options = nil, options = {}, adapter_instance = self.class.serialization_adapter_instance)
adapter_options ||= {}
options[:include_directive] ||= ActiveModel::Serializer.include_directive_from_options(adapter_options)
if (fieldset = adapter_options[:fieldset])
# fix controller `fields:` always overriding our patched fields option
# even if you don't pass `fields:` to `render`:
if fields = fieldset.fields_for(json_key)
options[:fields] = fields
end
end
resource = attributes_hash(adapter_options, options, adapter_instance)
relationships = associations_hash(adapter_options, options, adapter_instance)
resource.merge(relationships)
end
end
Тест:
>> CompaniesController.renderer.render(json: Company.first)
=> "{\"id\":1,\"name\":null,\"ceo\":{\"name\":null}}"
Мне нравится выбранный вами подход к атрибутам, и я собираюсь его опробовать. Правильно ли сказать, что на нежелательных полях не будет выполняться код сериализации?
@BenG да, похоже, извлекаются только запрошенные атрибуты, это определенно не так slice
: github.com/rails-api/active_model_serializers/blob/v0.10.14/lib/…
Это сработало довольно хорошо. Маркировка как правильная. Однако мне бы хотелось, чтобы у них был просто ключ :attributes вместо требования блока.