Return list of objects as dictionary with keys as the objects id with django rest framerwork
I think you can implement the to_representation
function in your Serializer.
class MySerializer(serializers.Serializer): id = serializers.ReadOnlyField() field1 = serializers.ReadOnlyField() field2 = serializers.ReadOnlyField() def to_representation(self, data): res = super(MySerializer, self).to_representation(data) return {res['id']: res} # or you can fetch the id by data directly # return {str(data.id): res}
You can traverse each item and with a dict comprehension create your desired dictionary. For example:
>>> l = [{ "id": 1, "x": 4}, { "id": 2, "x": 3}]>>> {v["id"]: v for v in l}{1: {'x': 4, 'id': 1}, 2: {'x': 3, 'id': 2}}
EDIT: current version available in a Github project and PYPI (pip install drf-keyed-list
)
Here's a general-purpose class that is bi-directional (vs. the read-only implementation above):
class KeyedListSerializer(ListSerializer): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) meta = getattr(self.child, 'Meta', None) assert hasattr(meta, 'keyed_list_serializer_field'), \ "Must provide a field name at keyed_list_serializer_field when using KeyedListSerializer" self._keyed_field = meta.keyed_list_serializer_field def to_internal_value(self, data): # syntax is py3 only data = [{**v, **{self._keyed_field: k}} for k, v in data.items()] return super().to_internal_value(data) def to_representation(self, data): response = super().to_representation(data) return {v.pop(self._keyed_field): v for v response}
For Py2, you need to make the super
calls explicit and replace the indicated dictionary constructor. You use it by assigning it to your list_serializer_class
and selecting a keyed_list_serializer_field
(i.e. the field used as the dict key):
class MySerializer(ModelSerializer): class Meta: list_serializer_class = KeyedListSerializer keyed_list_serializer_field = 'id'
The keyed_list_serializer_field
should contain unique values; the above implementation doesn't check.