2011-11-22 33 views
2

下面是一个简单的模型:序列化的模型与正确的十进制数据类型的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,而不是字符串。我不想使用浮点模型类型,因为float有时不正确地存储小数。

预先感谢您。

回答

4

如果实际值为5050.0? A Decimal对象维持最初输入的内容,例如Decimal('50')收益率50

>>> from decimal import Decimal 
>>> d = Decimal('50') 
>>> print d 
50 

不幸的是提供给DecimalField构造函数的参数是仅适用于限制存储值,而不是显示它们。

现在,因为Django是仅仅使用标准的JSON/simplejson lib中,你可以序列化的时候,比如在this question建议指定自定义编码器:

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) 

但它并没有结束。作为detailed in this blog post,Django是明确地传递cls=DjangoJSONEncodersimplejson.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" 
     } 
    } 
] 

这看起来很多工作。在保存之前,使用Django的model validation来确保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!']} 
+0

这种串行仍呈现在JSON字符串。 是否有理由不将“.2f'%obj”替换为“float('。2f'%obj)”? –

+0

这太复杂了 - 最好不要在第一时间使用Django的DecimalField。 – jononomo

+0

你可能是对的,但请记住这是在Django 1.3的日子里3.5年前发布的。请随时帮我更新它。 :) – jathanism