I need to write an api method in the Django Rest Framework that has the ability to get model fields and use them to get model values
I have written the following code for api method:
class RadioStationDynamicFields(APIView):
def post(self, request):
field_names = request.data.get('fields')
if not field_names:
return Response({"error": "fields are not specified."}, status=status.HTTP_400_BAD_REQUEST)
try:
radiostations = RadioStation.objects.all()
serializer = RadioStationSerializer(radiostations, many=True, fields=field_names)
return Response(serializer.data, status=status.HTTP_200_OK)
except Exception as e:
return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
When transmitting data in the form of {"fields":"some_field"}, I get an error:{ "error": "Field.__init__() got an unexpected keyword argument 'fields'" }\
My serializer:
class AddPointsSerializer(serializers.ModelSerializer):
class Meta:
model = AddPoints
fields = ('locality')
class RadioStationSerializer(serializers.ModelSerializer):
add_points = serializers.PrimaryKeyRelatedField(
queryset=Locality.objects.all(), many=True, required=False
)
class Meta:
model = RadioStation
fields = '__all__'
def to_internal_value(self, data):
if 'locality' in data:
try:
data['locality'] = Locality.objects.get(name=data['locality']).pk
except Locality.DoesNotExist:
raise serializers.ValidationError({'locality': 'Locality not found'})
if 'radio_channel' in data:
try:
data['radio_channel'] = RadioChannel.objects.get(name=data['radio_channel']).pk
except RadioChannel.DoesNotExist:
raise serializers.ValidationError({'radio_channel': 'RadioChannel not found'})
if 'add_points' in data:
add_points = []
for point in data['add_points']:
try:
add_points.append(Locality.objects.get(name=point).pk)
except Locality.DoesNotExist:
raise serializers.ValidationError({'add_points': f'Locality {point} not found'})
data['add_points'] = add_points
return super().to_internal_value(data)
def update(self, instance, validated_data):
if 'add_points' in validated_data:
add_points = validated_data.pop('add_points')
instance.add_points.set(add_points)
return super().update(instance, validated_data)
def __init__(self, *args, **kwargs):
super(RadioStationSerializer, self).__init__(*args, **kwargs)
if self.context.get('request').method in ['GET']:
self.fields['radio_channel'] = serializers.StringRelatedField()
self.fields['locality'] = serializers.StringRelatedField()
self.fields['add_points'] = serializers.StringRelatedField(many=True, read_only=True)
` What should I do to make this method work ?
it's easy to organize. You are asking about a serializer with dynamic fields. In DRF you can find documentation about this here: https://www.django-rest-framework.org/api-guide/serializers/#dynamically-modifying-fields
But this is wrong solution, and I cover it in my talks, e.g. here: https://youtu.be/IycQNgzFKwE?si=VV1Avx2KvsHxELvC&t=163 (about 2:45sec after the start).
For your case:
class RadioStationSerializer(serializers.ModelSerializer):
add_points = serializers.PrimaryKeyRelatedField(queryset=Locality.objects.all(), many=True, required=False)
class Meta:
model = RadioStation
fields = '__all__'
def __init__(self, *args, **kwargs):
fields = kwargs.pop('fields')
if fields:
self.Meta = self.Meta()
self.Meta.fields = fields
super().__init__(*args, **kwargs)
in __init__
you override Meta
for current serializer and change field
attribute of serializer.Meta