Skip to content

Instantly share code, notes, and snippets.

@ncopiy
Created June 27, 2019 10:37
Show Gist options
  • Save ncopiy/454db6e1ddfdee1acf9e859852629574 to your computer and use it in GitHub Desktop.
Save ncopiy/454db6e1ddfdee1acf9e859852629574 to your computer and use it in GitHub Desktop.
serializer without removing fields
import copy
from collections import Mapping
from rest_framework import serializers
from rest_framework.fields import SkipField
from rest_framework.relations import PKOnlyObject
def dict_merge(dct, merge_dct):
""" Recursive dict merge. Inspired by :meth:``dict.update()``, instead of
updating only top-level keys, dict_merge recurses down into dicts nested
to an arbitrary depth, updating keys. The ``merge_dct`` is merged into
``dct``.
This version will return a copy of the dictionary and leave the original
arguments untouched.
Args:
dct (dict) onto which the merge is executed
merge_dct (dict): dct merged into dct
Returns:
dict: updated dict
SOURCE: https://gist.github.com/angstwad/bf22d1822c38a92ec0a9
"""
dct = dct.copy()
for k, v in merge_dct.items():
if isinstance(dct.get(k), dict) and isinstance(v, Mapping):
dct[k] = dict_merge(dct[k], v)
else:
dct[k] = v
return dct
class FieldSaveSerializer(serializers.Serializer):
"""
you can define some fields in your serializer class, but other fields will not dropped
"""
def to_representation(self, instance):
ret = copy.deepcopy(instance)
fields = self._readable_fields
sources = []
for field in fields:
try:
attribute = field.get_attribute(instance)
except SkipField:
continue
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)
if field.source is not None:
sources.append(field.source)
if sources:
ret = self._get_representation_without_sources(dct=ret, sources_tree=self._get_source_tree(sources=sources))
return ret
class FinalValue:
pass
def _make_dict_tree(self, values: list):
res = dict()
for v in values[::-1]:
if not res:
res = {v: {self.FinalValue: self.FinalValue}}
else:
res = {v: res}
return res
def _get_source_tree(self, sources: list):
raw_dicts = [self._make_dict_tree(values) for values in [s.split(".") for s in sources]]
res = dict()
for d in raw_dicts:
res = dict_merge(dct=res, merge_dct=d)
return res
def _get_representation_without_sources(self, dct, sources_tree):
if sources_tree:
if (sources_tree == self.FinalValue) or (self.FinalValue in sources_tree):
raise ValueError
else:
keys_to_check = [key for key in sources_tree.keys() if key in dct and key != self.FinalValue]
if keys_to_check:
tmp_dct = {k: v for k, v in dct.items() if k not in keys_to_check}
for key in keys_to_check:
if isinstance(dct[key], dict):
try:
tmp_dct[key] = self._get_representation_without_sources(dct=dct[key],
sources_tree=sources_tree[key])
except ValueError:
continue
if tmp_dct:
return tmp_dct
else:
raise ValueError
return dct
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment