<class 'OneTimePasswordBackend'> is not JSON serializable
On Monday 05 June 2017 04:14:01 Alison P wrote:
> If I use the default session serializer, I get the following error:
> TypeError at /login/
>
> <class 'OneTimePasswordBackend'> is not JSON serializable
I'm using this is a general solution. Feel free to strike what you don't need.
The basics is that a JSONEncoder knows primitives and how to recurse containers. The rest is passed to a method "default", which generates the above error message. Subclasses should override it to implement their own knowledge about objects to serialize.
class JSONModelEncoder(DjangoJSONEncoder):
exclude_callback = None
recurse_foreign_keys = True
def get_excluded_fields(self, instance):
if self.exclude_callback is not None:
return self.exclude_callback(instance)
else:
return []
def recurse_foreign(self, instance, field) -> dict:
retval = {}
if isinstance(field, models.OneToOneField):
# OneToOneField is a subclass of ForeignKey, thus must be placed
# before ForeignKey.
# If parent_link is true, then we need to pull in the fields
# as if they were part of the current model.
if not field.parent_link:
parent_obj = getattr(instance, field.name)
if parent_obj is not None:
value = self.model_to_dict(
parent_obj,
exclude=self.get_excluded_fields(parent_obj)
)
retval.update(value)
elif isinstance(field, models.ForeignKey):
# Resolve the model pointed to.
foreign = getattr(instance, field.name)
if foreign is not None:
value = self.model_to_dict(foreign)
retval[field.name] = value
else:
retval[field.name] = None
elif isinstance(field, models.ManyToManyField):
# Create a list of model dicts.
modlist = []
related = getattr(instance, field.name)
for rel in related:
modlist.append(self.model_to_dict(rel))
retval[field.name] = modlist
else:
raise TypeError('recurse_foreign called on {}'.format(type(field)))
return retval
def link_foreign(self, instance, field) -> dict:
if isinstance(field, models.ManyToManyField):
return self.recurse_foreign(instance, field)
elif isinstance(field, models.OneToOneField):
if not field.parent_link:
return self.recurse_foreign(instance, field)
elif isinstance(field, models.ForeignKey):
foreign = getattr(instance, field.name)
# raise ValueError(repr(foreign))
if foreign is not None:
if getattr(foreign, 'absolute_url', False):
return {
field.name: {
'text': force_text(foreign),
'link': foreign.absolute_url,
}
}
else:
return {
field.name: {
'text': force_text(foreign),
'pk': foreign.pk
}
}
return {}
def model_to_dict(self, instance, exclude=None, **kwargs) -> dict:
"""Convert a model instance to a dictionary of field names and values.
If the model has a method of the same name, that method is called for
three reasons:
#. Centralization. This method can be used in other parts of django
or an application to provide a consistent dictionary of the
model.
#. The default implementation only maps fields. If the model has
important attributes that are implemented as properties this
mixin will not find them.
#. Hiding of sensitive fields. The model is better equipped to
evaluate if a field contains sensitive information.
:param instance: the model instance to convert
:type instance: models.Model
:param exclude: list of fields to exclude from being sent
:type exclude: list
"""
exclude = exclude or self.get_excluded_fields(instance)
if hasattr(instance, 'model_to_dict') and \
callable(getattr(instance, 'model_to_dict')):
return instance.model_to_dict(exclude, **kwargs)
retval = {}
for field in instance._meta.fields:
if field.name in exclude:
continue
if isinstance(field, (models.ForeignKey, models.ManyToManyField)):
if self.recurse_foreign_keys:
retval.update(self.recurse_foreign(instance, field))
else:
merge = self.link_foreign(instance, field)
if merge:
retval.update(merge)
else:
retval[field.name] = getattr(instance, field.name)
return retval
def default(self, obj) -> object:
"""Prepares a value for json representation.
Complex types that are not containers should be handled by this method.
:param obj:
:type obj: object
"""
if isinstance(obj, models.query.QuerySet):
return list(obj)
elif hasattr(obj, 'model_to_dict') and \
callable(getattr(obj, 'model_to_dict')):
return obj.model_to_dict(exclude=self.get_excluded_fields(obj))
elif isinstance(obj, models.Model):
return self.model_to_dict(obj)
else:
return super().default(obj)
--
Melvyn Sopacua