""" 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''}