Source code for restricted_fields.mixins
import typing
from collections import OrderedDict
from django.utils.functional import cached_property
from rest_framework.fields import SkipField
from rest_framework.relations import PKOnlyObject
from restricted_fields.utils import get_fields
[docs]class RestrictedFieldsSerializerMixin(object):
"""API Serializer mixin
This provides support for restricting serialized data to only a subset of fields using the
``only`` and ``defer`` query parameters.
:param only: Restricted to only a subset of fields (Include these fields).
:type only: list
:param defer: Defer the listed fields (Exclude these fields).
:type defer: list
**Example:**
Serialize only the `id` and `name` fields.
.. code-block:: console
GET https://.../api/users/?only=id&only=name
.. code-block:: console
{
"count":1,
"next": "http://127.0.0.1:8000/api/users/?only=id&only=name&page=1",
"previous":null,
"results":[
{
"id":1,
"name": "Test"
},
...
],
}
"""
RESTRICTED_FIELDS_PARAM = "only"
DEFERRED_FIELDS_PARAM = "defer"
@property
def context(self):
raise NotImplementedError
@cached_property
def _readable_fields(self):
raise NotImplementedError
[docs] def to_representation(self, instance):
"""
Convert Model Object instance -> Dict of primitive datatypes.
:param instance: The django model instance.
:type instance: django.db.models.Model
:return: Dictionary of fields and corresponding value.
"""
request = self.context["request"]
ret = OrderedDict() # type: typing.Dict[str, typing.Any]
restricted_fields = get_fields(
request.query_params, self.RESTRICTED_FIELDS_PARAM
)
deferred_fields = get_fields(request.query_params, self.DEFERRED_FIELDS_PARAM)
fields = self._readable_fields
if restricted_fields:
fields = [f for f in fields if f.field_name in restricted_fields]
if deferred_fields:
fields = [f for f in fields if f.field_name not in deferred_fields]
for field in fields:
try:
attribute = field.get_attribute(instance)
except SkipField:
continue
# We skip `to_representation` for `None` values so that fields do
# not have to explicitly deal with that case.
#
# For related fields with `use_pk_only_optimization` we need to
# resolve the pk value.
check_for_none = (
attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
)
if check_for_none is None:
ret[field.field_name] = None
else:
ret[field.field_name] = field.to_representation(attribute)
return ret