Skip to content

Instantly share code, notes, and snippets.

@piterskikhsa
Forked from alexgavrisco/serializers.py
Created August 12, 2024 21:09
Show Gist options
  • Save piterskikhsa/4a3b7513b0c772c46fde62d7e1d42a44 to your computer and use it in GitHub Desktop.
Save piterskikhsa/4a3b7513b0c772c46fde62d7e1d42a44 to your computer and use it in GitHub Desktop.
Mixin for creating serializers with configurable fields (for the django rest framework).
"""
Some helper serializer mixins.
Helps to reuse serializers for different purposes.
"""
import logging
from rest_framework import serializers
LOGGER = logging.getLogger(__name__)
class UpdateFieldsMixin(object):
"""
This mixin allows to update existing fields of a serializer.
Basically it could be used to update type of a field without changing serializer.
`update_fields` argument should be a list/tuple of field name and its definition.
E.g. `update_fields = (('is_active', serializers.BooleanField(default=False)), )
"""
def __init__(self, *args, **kwargs):
fields = kwargs.pop('update_fields', None)
super(UpdateFieldsMixin, self).__init__(*args, **kwargs)
if fields and isinstance(fields, (list, tuple)):
init_fields = {x[0]: x[1] for x in fields if len(x) == 2}
self.fields.update(init_fields)
class SetFieldsMixin(object):
"""
This mixin allows to change serializable fields in run time
(when creating the instance).
Just pass a list/tuple with names of fields that you want to
serialize (as the `field` argument).
It also could add new fields using same idea as in `UpdateFieldsSerialzier`.
TODO: DRY
"""
def __init__(self, *args, **kwargs):
fields = kwargs.pop('fields', None)
super(SetFieldsMixin, self).__init__(*args, **kwargs)
if fields and isinstance(fields, (list, tuple)):
new_fields = []
init_fields = {}
for field in fields:
if not isinstance(field, tuple):
new_fields.append(field)
continue
if len(field) != 2:
continue
init_fields[field[0]] = field[1]
existing_fields = set(self.fields.keys())
for field in existing_fields - set(new_fields):
self.fields.pop(field)
for name, value in init_fields.items():
self.fields[name] = value
class ExcludeFieldsMixin(object):
"""
This mixin allows to specify fields to be excluded from serializers
in runtime (at initialization of serializer instance).
Opposite of `SetFieldsMixin`
"""
def __init__(self, *args, **kwargs):
excluded_fields = kwargs.pop('exclude_fields', None)
super(ExcludeFieldsMixin, self).__init__(*args, **kwargs)
if excluded_fields and isinstance(excluded_fields, (list, tuple)):
excluded_fields = set(excluded_fields) & set(self.fields.keys())
for field in excluded_fields:
self.fields.pop(field)
class ConfigurableSerializer(
SetFieldsMixin,
ExcludeFieldsMixin,
UpdateFieldsMixin,
serializers.Serializer):
def __init__(self, *args, **kwargs):
super(ConfigurableSerializer, self).__init__(*args, **kwargs)
class ConfigurableModelSerializer(
SetFieldsMixin,
ExcludeFieldsMixin,
UpdateFieldsMixin,
serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
super(ConfigurableModelSerializer, self).__init__(*args, **kwargs)
# Here is an example of how to use this mixin.
#
# class DashboardMetaSerializer(ConfigurableSerializer):
# isStarred = rest_serializers.BooleanField(default=False)
# isHome = rest_serializers.BooleanField(default=False)
# isSnapshot = rest_serializers.BooleanField(default=False)
# slug = rest_serializers.SlugField()
#
# Let's test it
#
# In [2]: from django_grafana.grafana import serializers
# In [3]: s = serializers.DashboardMetaSerializer()
# In [4]: s.data
# Out[4]: {'isHome': False, 'isStarred': False, 'slug': u'', 'isSnapshot': False}
# In [5]: s = serializers.DashboardMetaSerializer(fields=('isHome', 'slug'))
# In [6]: s.data
# Out[6]: {'isHome': False, 'slug': u''}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment