如何在Django模型保存时将美元字段自动转换为美分单位存储?
实现方案
方案1:使用@property装饰器(快速实现,无需自定义字段)
这是最轻量化的实现方式,通过Python原生的属性描述符实现存取自动转换:
首先修改模型字段定义,将原存储字段改为美分存储的底层字段,再通过property暴露美元单位的访问入口:
from django.db import models from decimal import Decimal, ROUND_HALF_UP class Bank(models.Model): backup = False device_type = models.BigIntegerField(verbose_name='device type') # 底层存储字段,存美分单位,不直接对外暴露 _transaction_sum_cents = models.BigIntegerField(verbose_name='sum in cents', db_column='transaction_sum') @property def transaction_sum(self): """getter:从美分转美元""" return Decimal(self._transaction_sum_cents) / Decimal('100') @transaction_sum.setter def transaction_sum(self, value): """setter:从美元转美分,自动处理四舍五入避免精度问题""" if value is None: self._transaction_sum_cents = None return # 统一用Decimal处理金额,避免浮点数精度丢失 dollar_value = Decimal(str(value)) cent_value = int((dollar_value * Decimal('100')).quantize(Decimal('1'), rounding=ROUND_HALF_UP)) self._transaction_sum_cents = cent_value
注意:如果需要基于金额做ORM查询,需要对
_transaction_sum_cents字段做过滤,比如查询金额大于100美元的记录:Bank.objects.filter(_transaction_sum_cents__gt=100*100)
方案2:自定义模型字段(完全透明,推荐生产环境使用)
如果希望逻辑更封装、使用完全透明(和普通字段用法完全一致,包括ORM查询也直接支持美元单位),可以自定义金额转换字段,这是Django模型提供的标准扩展能力:
from django.db import models from decimal import Decimal, ROUND_HALF_UP class DollarToCentsField(models.BigIntegerField): def from_db_value(self, value, expression, connection): """从数据库取数据时自动转美元""" if value is None: return value return Decimal(value) / Decimal('100') def get_prep_value(self, value): """存数据时自动转美分""" if value is None: return value dollar_value = Decimal(str(value)) cent_value = int((dollar_value * Decimal('100')).quantize(Decimal('1'), rounding=ROUND_HALF_UP)) return cent_value
之后直接在模型中使用自定义字段即可,不需要额外修改其他逻辑:
class Bank(models.Model): backup = False device_type = models.BigIntegerField(verbose_name='device type') # 直接使用自定义字段,外部操作全是美元单位,自动完成存储转换 transaction_sum = DollarToCentsField(verbose_name='sum in dollars')
该方案下ORM查询也可以直接用美元单位传参,比如
Bank.objects.filter(transaction_sum__gt=100)会自动转换为美分的条件查询。
注意事项
- 所有金额运算建议统一使用
Decimal类型,避免浮点数精度丢失导致的金额错误 - 如果模型需要支持
update()批量更新,方案1需要手动转美分对_transaction_sum_cents赋值,方案2不受影响 - 两种方案都不需要手动调用转换方法,所有对
transaction_sum的赋值、取值操作都会自动完成单位换算。
内容的提问来源于stack exchange,提问作者Павел Иванов
相关产品推荐
相关产品推荐

