Created
June 27, 2019 10:37
-
-
Save ncopiy/454db6e1ddfdee1acf9e859852629574 to your computer and use it in GitHub Desktop.
serializer without removing fields
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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