如何强制Django将小数保存到float数据库列

时间:2012-09-25 14:27:23

标签: django python-2.6

我正在尝试使用单一货币进行自己的字段计算,我无法在数据库中使用十进制数据类型,这是我到目前为止所拥有的。我真的只是学习自定义字段类型,我不确定我做错了什么。

class MoneyField(models.DecimalField):
    """
    """

    __metaclass__ = models.SubfieldBase

    def __init__(self, verbose_name=None, name=None, max_digits=10, decimal_places=2, seperator=',', dp='.', pos='', neg='-', trailneg='', **kwargs):
        models.DecimalField.__init__(self, verbose_name, name, decimal_places=2, max_digits=10, **kwargs)

    def get_internal_type(self):
        return models.DecimalField.__name__

    def pre_save(self, model_instance, add):
        value = getattr(model_instance, self.attname)
        if self.decimal_places > 0:
            dp_modifier = Decimal(1 / Decimal(pow(10, self.decimal_places)))
        else:
            dp_modifier = Decimal(1)
        if value != None:
            value = Decimal(str(value))

        print value
        print type(value.quantize(Decimal(dp_modifier)))
        return value.quantize(Decimal(dp_modifier))

    def to_python(self, value):
        try:
            if self.decimal_places > 0:
                dp_modifier = Decimal(1 / Decimal(pow(10, self.decimal_places)))
            else:
                dp_modifier = Decimal(1)
            if value != None:
                value = Decimal(str(value))
            return super(MoneyField, self).to_python(value).quantize(Decimal(dp_modifier))
        except AttributeError:
            return None

    def get_db_prep_save(self, value, connection):
        return float(value)

    def get_prep_value(self, value):
        return float(value)

例外:

Environment:


Request Method: GET
Request URL: http://127.0.0.1:8001/appdata/joinery/costing/cost-boughtin-by-item/glass/1A346B0F-0705-11E2-9E84-7071BCB8D2AB/

Django Version: 1.3.1
Python Version: 2.6.2
Installed Applications:
['django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.sites',
 'django.contrib.admin',
 'appdata.generic',
 'appdata.contacts',
 'appdata.dashboard',
 'appdata.scheduler',
 'appdata.financial',
 'appdata.work',
 'appdata.boughtin',
 'appdata.bespoke',
 'appdata.joinery',
 'appdata.home',
 'appdata.globaltags',
 'appdata.importdata',
 'appdata.database',
 'appdata.ads',
 'appdata.workshop',
 'appdata.transfer',
 'appdata.setup',
 'appdata.languages',
 'appdata.machine_output',
 'appdata.machine_output.modules',
 'appdata.printing',
 'django_cpserver',
 'appdata.djangologdb',
 'raven.contrib.django']
Installed Middleware:
('django.middleware.common.CommonMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.locale.LocaleMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'appdata.middleware.UserAgent',
 'appdata.middleware.SiteLogin',
 'appdata.middleware.RequestIdent',
 'appdata.middleware.ProfilerMiddleware',
 'django.middleware.transaction.TransactionMiddleware',
 'djangologdb.middleware.LoggingMiddleware',
 'raven.contrib.django.middleware.Sentry404CatchMiddleware')


Traceback:
File "C:\JoinerySoft\Development\working folder\lib\site-packages\django\core\handlers\base.py" in get_response
  111.                         response = callback(request, *callback_args, **callback_kwargs)
File "C:\JoinerySoft\Development\working folder\appdata\joinery\costing.py" in cost_boughtin
  202.         net_cost, charge_out, xml = calc_model.objects.cost_by_item(item,get_xml=True)
File "C:\JoinerySoft\Development\working folder\appdata\joinery\models.py" in cost_by_item
  719.         return self._cost_component_list(item_list, costbook, get_xml, item=item)
File "C:\JoinerySoft\Development\working folder\appdata\joinery\models.py" in _cost_component_list
  699.             calc_data = self._cost_components(calc_item_list, costbook)
File "C:\JoinerySoft\Development\working folder\appdata\joinery\models.py" in _cost_components
  688.             calc_item.apply_cost(cost_item, item_cache)
File "C:\JoinerySoft\Development\working folder\appdata\joinery\models.py" in apply_cost
  1024.         calc_component.save()
File "C:\JoinerySoft\Development\working folder\lib\site-packages\django\db\models\base.py" in save
  460.         self.save_base(using=using, force_insert=force_insert, force_update=force_update)
File "C:\JoinerySoft\Development\working folder\lib\site-packages\django\db\models\base.py" in save_base
  526.                         rows = manager.using(using).filter(pk=pk_val)._update(values)
File "C:\JoinerySoft\Development\working folder\lib\site-packages\django\db\models\query.py" in _update
  491.         return query.get_compiler(self.db).execute_sql(None)
File "C:\JoinerySoft\Development\working folder\lib\site-packages\django\db\models\sql\compiler.py" in execute_sql
  869.         cursor = super(SQLUpdateCompiler, self).execute_sql(result_type)
File "C:\JoinerySoft\Development\working folder\lib\site-packages\django\db\models\sql\compiler.py" in execute_sql
  725.             sql, params = self.as_sql()
File "C:\JoinerySoft\Development\working folder\lib\site-packages\django\db\models\sql\compiler.py" in as_sql
  834.                 val = field.get_db_prep_save(val, connection=self.connection)
File "C:\JoinerySoft\Development\working folder\lib\site-packages\django\db\models\fields\subclassing.py" in inner
  28.             return func(*args, **kwargs)
File "C:\JoinerySoft\Development\working folder\lib\site-packages\django\db\models\fields\__init__.py" in get_db_prep_save
  786.         return connection.ops.value_to_db_decimal(self.to_python(value),
File "C:\JoinerySoft\Development\working folder\lib\site-packages\django\db\models\fields\__init__.py" in to_python
  761.             return decimal.Decimal(value)
File "C:\JoinerySoft\Development\working folder\lib\decimal.py" in __new__
  653.                             "First convert the float to a string")

Exception Type: TypeError at /appdata/joinery/costing/cost-boughtin-by-item/glass/1A346B0F-0705-11E2-9E84-7071BCB8D2AB/
Exception Value: Cannot convert float to Decimal.  First convert the float to a string

1 个答案:

答案 0 :(得分:1)

我认为你可能会从错误的方向接近问题。您应该尝试从float字段中获取Decimal值,而不是尝试将db中的Decimal字段保存为float。

尝试这种性质的东西:

class MoneyField(models.FloatField):
    __metaclass__ = models.SubfieldBase

    def to_python(self, value):
        t_value = type(value)

        if t_value is Decimal:
            return value

        elif t_value is float:
            return Decimal.from_float(value)

        elif t_value in (str, unicode,):
            return Decimal(value)

        else:
            raise TypeError('Unsupported value type: %s' % str(t_value))

    def get_prep_value(self, value):
        return float(value)

所以它的作用是它与float字段相同,因此与db中的float字段兼容。但是,每当检索到值时,它都会确保返回浮点数的十进制版本。在尝试保存字段时,它会将其转换为浮动状态。

没有测试过,所以不确定100%是否正确,但我认为这应该让你朝着正确的方向前进。除此之外,你可以强制执行所有的金钱......