Я работаю с Django REST Framework и пытаюсь динамически добавлять поля в сериализатор на основе данных, выбранных из пользовательского интерфейса. В частности, мне нужно добавить поля для каждого товара, связанного с экземпляром KindTournamentSerializer. В этих полях должно быть указано количество каждого товара.
from rest_framework import serializers
class KindTournamentSerializer(CelestialSerializer):
voucher_id = serializers.CharField(label=_("Ration number"))
voucher_size = serializers.CharField(label=_("Ration size"))
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Dynamically determine commodities from the instance
if self.instance:
selected_fields = kwargs.get('selected_fields', [])
if 'voucher_size' in selected_fields:
# Get the first instance to extract commodities
first_instance = self.instance[0] if isinstance(self.instance, TournamentSerializer) else self.instance
commodities = first_instance.rations.all()
# Create fields for each commodity
for ration in commodities:
commodity_name = ration.name
sanitized_name = commodity_name.replace(" ", "_").replace("-", "_")
field_name = f"commodity_{sanitized_name}"
# Add the SerializerMethodField
commodity_field = serializers.SerializerMethodField(label=_(commodity_name))
self.fields[field_name] = commodity_field
# Dynamically bind the method for the field
setattr(self, f'get_{field_name}', lambda obj, name=commodity_name: self.get_commodity_value(obj, name))
def get_commodities_data(self, obj):
"""Aggregate quantities of commodities from the latest voucher."""
latest_voucher = obj.latest_voucher
commodity_data = {}
if latest_voucher:
commodities = latest_voucher.commodities.all()
for commodity in commodities:
name = commodity.name
quantity = commodity.quantity
if name not in commodity_data:
commodity_data[name] = 0
commodity_data[name] += quantity
return commodity_data
def get_commodity_value(self, obj, commodity_name):
"""Retrieve the quantity of a specific commodity."""
commodities_data = self.get_commodities_data(obj)
return commodities_data.get(commodity_name, 0)
def to_representation(self, obj):
"""Customize representation to include commodity quantities."""
data = super().to_representation(obj)
# Add dynamic commodity fields to the representation
commodities_data = self.get_commodities_data(obj)
for field in self.fields:
if field.startswith("commodity_"):
commodity_name = field.replace("commodity_", "")
data[field] = commodities_data.get(commodity_name, 0)
return data
Одна из многочисленных проблем, связанных с приведенным выше определением, заключается в следующем. Когда мы вызываем KindTournamentSerializer.serializer_class._declared_fields из экспортера CSV, который использует этот сериализатор. Метод __init__ не срабатывает, поэтому эти динамические поля не создаются.
Я работаю с Django REST Framework и пытаюсь динамически добавлять поля в сериализатор на основе данных, выбранных из пользовательского интерфейса. В частности, мне нужно добавить поля для каждого товара, связанного с экземпляром KindTournamentSerializer. В этих полях должно быть указано количество каждого товара. [code]from rest_framework import serializers
class KindTournamentSerializer(CelestialSerializer): voucher_id = serializers.CharField(label=_("Ration number")) voucher_size = serializers.CharField(label=_("Ration size"))
# Dynamically determine commodities from the instance if self.instance:
selected_fields = kwargs.get('selected_fields', []) if 'voucher_size' in selected_fields: # Get the first instance to extract commodities first_instance = self.instance[0] if isinstance(self.instance, TournamentSerializer) else self.instance commodities = first_instance.rations.all()
# Create fields for each commodity for ration in commodities: commodity_name = ration.name sanitized_name = commodity_name.replace(" ", "_").replace("-", "_") field_name = f"commodity_{sanitized_name}"
# Add the SerializerMethodField commodity_field = serializers.SerializerMethodField(label=_(commodity_name)) self.fields[field_name] = commodity_field
# Dynamically bind the method for the field setattr(self, f'get_{field_name}', lambda obj, name=commodity_name: self.get_commodity_value(obj, name))
def get_commodities_data(self, obj): """Aggregate quantities of commodities from the latest voucher.""" latest_voucher = obj.latest_voucher commodity_data = {}
if latest_voucher: commodities = latest_voucher.commodities.all() for commodity in commodities: name = commodity.name quantity = commodity.quantity if name not in commodity_data: commodity_data[name] = 0 commodity_data[name] += quantity
return commodity_data
def get_commodity_value(self, obj, commodity_name): """Retrieve the quantity of a specific commodity.""" commodities_data = self.get_commodities_data(obj) return commodities_data.get(commodity_name, 0)
def to_representation(self, obj): """Customize representation to include commodity quantities.""" data = super().to_representation(obj)
# Add dynamic commodity fields to the representation commodities_data = self.get_commodities_data(obj)
for field in self.fields: if field.startswith("commodity_"): commodity_name = field.replace("commodity_", "") data[field] = commodities_data.get(commodity_name, 0)
return data [/code] Одна из многочисленных проблем, связанных с приведенным выше определением, заключается в следующем. Когда мы вызываем KindTournamentSerializer.serializer_class._declared_fields из экспортера CSV, который использует этот сериализатор. Метод __init__ не срабатывает, поэтому эти динамические поля не создаются.