Сериализация моделей в django с соответствующими десятичными типами данных в json
Вот пример модели:
class FooModel(models.Model):
foo = models.DecimalField(max_digits=6, decimal_places=3, null=True)
Сериализовать:
from django.core import serializers
obj = get_object_or_404(FooModel, pk=1)
data = serializers.serialize("json", [obj])
Это вернет что-то вроде:
[
{
"pk": 1,
"model": "app.foomodel",
"fields": {
"foo": "50"
}
}
]
Вопрос
Как я могу сделать поле foo сериализованным как float, а не как string. Я не хочу использовать тип модели float, так как float иногда не хранит десятичные знаки правильно.
Заранее благодарю вас.
1 ответ:
Если значение на самом деле
50против50.0? ОбъектDecimalсохраняет все, что было первоначально введено, напримерDecimal('50')дает50.К сожалению, аргументы, предоставляемые конструктору>>> from decimal import Decimal >>> d = Decimal('50') >>> print d 50DecimalField, предназначены только для ограничений на хранение значений и не отображают их.Теперь, поскольку Django просто использует стандартный JSON / simplejson lib, вы можете указать пользовательский кодер при сериализации, например, предложенный в this вопрос :
Но на этом все не заканчивается. Как подробно описано в этом блоге , Django явно передаетimport decimal import json class DecimalEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, decimal.Decimal): return '%.2f' % obj # Display Decimal obj as float return json.JSONEncoder.default(self, obj)cls=DjangoJSONEncoderвsimplejson.dump(...), поэтому мы должны обойти это, также создав пользовательский объект сериализатора, ссылающийся наDecimalEncoder, который мы создали:from django.core.serializers.json import Serializer as JSONSerializer class DecimalSerializer(JSONSerializer): def end_serialization(self): self.options.pop('stream', None) self.options.pop('fields', None) json.dump(self.objects, self.stream, cls=DecimalEncoder, **self.options)Далее вы создаете экземпляр
DecimalSerializerкак свой собственный объект сериализатора, и происходит волшебство:my_serializer = DecimalSerializer() print my_serializer.serialize([obj], indent=4)Который дает:
[ { "pk": 1, "model": "app.foomodel", "fields": { "foo": "50.00" } } ]Это кажется большой работой. Возможно, будет проще использовать модель Джанго проверка , чтобы убедиться, что поле
FooModel.fooвсегда является числом с плавающей запятой, перед сохранением. Вот попытка грубой силы:from django.core.exceptions import ValidationError class FooModel(models.Model): foo = models.DecimalField(max_digits=6, decimal_places=3, null=True) def clean(self): if '.' not in str(self.foo): raise ValidationError('Input must be float!') def save(self, *args, **kwargs): self.full_clean() super(FooModel, self).save(*args, **kwargs)А затем:
>>> f = FooModel(foo='1') >>> f.save() Traceback (most recent call last): File "<console>", line 1, in <module> File "/home/jathan/sandbox/foo/app/models.py", line 15, in save self.full_clean() File "/usr/local/lib/python2.6/dist-packages/django/db/models/base.py", line 828, in full_clean raise ValidationError(errors) ValidationError: {'__all__': [u'Input must be float!']}
Comments