在Django中,如果一个项目涉及了金融,他的要求是十分严格的。

所以嘞,这里就有一些坑,很多坑,第一次开发的时候很容易出现一系列的错误

在涉及金融计算的地方,不能使用float类型

什么鬼,但事实就是如此,千万不要用float进行计算.....

所以,Python中为我们提供了一个专门的模块计算,decimal

而同样的,Django中也提供了相应的计算字段,DecimalField

class DecimalField(max_digits=None, decimal_places=None[, **options])
  • max_digits:表示最大位数
  • decimal_places:表示小数点后面的位数
  • 假设你最多存999人民币,小数点要精确到2位,需要的max_digits就为6,decimal_places就为2

下面是一些例子

1.在models.py中定义:

from django.db import models

class UserProfile(models.Model):
price = models.DecimalField(max_digits=16,decimal_places=2)

2.添加一条记录:

from decimal import Decimal

obj = models.UserProfile.objects.create(price = Decimal("123.45"))

3.更新记录:

obj.price -= Decimal("1.00")

obj.save()

4.我们输出查看一下SQL语句:

from django.db import connection

print(connection.queries[-1])

//UPDATE `table` SET `price` = '122.45' WHERE `id` = 1

我们之前使用的update会有问题。在并发很高的时候,会遇到类似多线程的问题,因为加减操作都在客户端,某个线程写入price的时候,可能之前拿到的已经被别人更新过了,所以我们需要原子写入。

UPDATE `table` SET `price` = 'price' - '1.00' WHERE `id` = 1

在ORM及是使用F()

obj.price = F("price") - Decimal("1.00")

obj.save(update_fields = ["price"])

注意:在调用save()方法的时候,我们可以用update_fields传入需要update的字段。否则Django可能会把所有的字段都放在SQL中,影响效率


好吧,上面看似已经是把问题都解决了,但是只是看似。

我们在数据库中,定义的字段的精确到小数点后两位,如果我减去一个3位的小数会怎样呢?

obj.price = F("price") - Decimal("1.001")

obj.save(update_field=["price"])

好吧,结果不出意料的报错了

会抛出一个Traceback的错误

这个错误其实是MySQL抛出的一个异常,所以传递到了Django,使更新操作无法成功

怎么解决呢?

//我们手动写个方法进行一个精度的转换

def to_decimal(s,precision=2):

    r = pow(10,precision+1)
v = s if type(s) is Decimal else Decimal(str(s)) try:
return Decimal(round(int(v * r),-1))/r
except:
return Decimal(s) obj.price = F("price") - to_decimal("1.001",2)
obj.save(update_fields=["price"])

这样貌似有解决了一个问题。。。实际呢?

这只是加或者减,如果是乘除呢?

由于这是MySQL层次上出的错误,也就是说实在最后存储的上面出的错,我们是没有办法在Django的层面上对计算出来的结果在进行一次类型转换的。这时候怎么办,只有上raw sql了。

//完整一点
//这次带上事务 from django.db import transaction,connection try:
with transaction.atomic():
cursor = connection.cursor()
ret = cursor.execute(
"UPDATE 'table' SET 'price'=CAST(('price'*%s) AS DECIMAL(16,2)) WHERE 'id' = %s ",
[Decimal("1.001"),obj.id]
)
except:
print("失败")

注意:在MySQL层面上,我们使用CAST(%s AS DECIMAL(16,2))来把结果转化为price字段同样个是Decimal类型

返回更新数据的行数,如果成功了,ret就是1


你以为结束了?

太天真了

注意:如果是事务操作,一定要考虑到多线程并发的造成的数据冲突的问题

即:假设连个线程获取了同意对象,进行了更改,怎么办?

这里就要用到select_for_update()

注意:这点很重要,金钱的操作,我们最好不要使用自增自减运算,而是使用select_for_update()的行级索来避免冲突。

所以嘞,我们的例子又可以改进了。

try:
with transaction.atomic():
locked_obj =UserProfile.objects.select_for_update().get(pk=obj.id)
locked_obj.price -= to_decimal('11.11111', 2)
assert locked_obj.price >= 0 #断言判断是不是合理
locked_obj.save(update_fields=['price'])
except:
print 'save failed'

到这里,才算是告一段落。

还有,补充一点:

  网上都说使用select_for_update可能会产生死锁,具体可以看我的上一篇文章。

  Django中管理并发操作

Django中涉及金融的项目的更多相关文章

  1. Django中Celery的实现介绍(一)

    Django中Celery的实现 Celery官网http://www.celeryproject.org/ 学习资料:http://docs.jinkan.org/docs/celery/ Cele ...

  2. Django中使用Bootstrap----带view.py视图函数(也就是项目下的脚本文件)

    一.Django中使用Bootstrap 1.首先建立工程,建立工程请参照:https://www.cnblogs.com/effortsing/p/10394511.html 2.在Firstdja ...

  3. Django中-事务操作

    如何在Django中进行事务操作呢? 近期,公司里要使用Django开发一套金融相关的系统. 涉及钱了.....安全安全安全 如果钱转到一半,系统崩了,咋办? 如果钱汇到一半,系统崩了,咋办? 如果东 ...

  4. Django中ORM介绍和字段及其参数

    ORM介绍 ORM概念 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术. 简单的说,ORM是通过使用描述 ...

  5. Django中ORM介绍

    Object Relational Mapping(ORM) ORM介绍 ORM概念 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据 ...

  6. django中的跨表查询梳理

    1.前言 最近在写一个小项目,里面主要涉及的就是表与表之间复杂的关系.当真正开发起来的时候,才发现自己对复杂的表关系间的查询有点混乱,趁着这几天的时间,重新梳理了一下. 2.概念 在开始之前,先明确几 ...

  7. Django中的信号

    信号 Django 提供一个“信号分发器”,允许解耦的应用在框架的其它地方发生操作时会被通知到. 简单来说,信号允许特定的sender通知一组receiver某些操作已经发生. 这在多处代码和同一事件 ...

  8. Django中ORM介绍和字段及字段参数

    Object Relational Mapping(ORM) 1 ORM介绍 1.1 ORM概念 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对 ...

  9. django中的ORM介绍和字段及字段参数

    Object Relational Mapping(ORM) ORM介绍 ORM概念 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据 ...

随机推荐

  1. C语言之预处理命令与用typedef命名已有类型

    预处理命令 主要是改进程序设计环境,以提高编程效率,不属于c语言本身的组成部分,不能直接对它们进行编译,必须在对 程序编译之前,先对程序中的这些特殊命令进行“预处理”.比如头文件. 有以下三类:宏定义 ...

  2. gj5 自定义序列类

    5.1 序列类型的分类 容器序列  list.tuple.deque扁平序列[同一种数据类型]  str.bytes.bytearray.array.array可变序列  list, deque,by ...

  3. org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'testService' is defined

    org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'testService' is defi ...

  4. 全国各地dns服务器列表

    211.103.13.101 江苏省无锡市 移动DNS服务器 211.136.28.231 北京市 移动DNS服务器 211.136.28.234 北京市 移动DNS服务器 211.136.28.23 ...

  5. cxf-rs client 调用

    org.apache.cxf.jaxrs.client.WebClient get调用 @GET @Path("/echo/{input}") @Produces("te ...

  6. 配置 cxf-rs spring bean 文件

    http://cxf.apache.org/schemas/jaxrs.xsd http://cxf.apache.org/docs/restful-services.html 示例: <?xm ...

  7. Node.js是什么[译]

    当我向人们介绍Node.js的时候,一般会有两种反应:多数立刻表示“哦,这样啊”,另外的则会感到困惑. 如果你是第二种的话,我会试着这样解释node: 这是一个命令行工具.你可以下载一个tar包,然后 ...

  8. vc中使用SendMessage正确发送自定义消息的方法

    最近在用VC2008做开发,后来由于要用到消息的发送,而且需要自定义消息,在网上查找了很多例子,根据他们所说的,虽然大致都差不多,但是基本上没有 一个能完全做出来的.要知道VC编程有一个小地方出错,都 ...

  9. HDU1241 Oil Deposits 2016-07-24 13:38 66人阅读 评论(0) 收藏

    Oil Deposits Problem Description The GeoSurvComp geologic survey company is responsible for detectin ...

  10. [翻译]CSS3 Media Queries

    Media Queries Official Manual:http://www.w3.org/TR/css3-mediaqueries/ 原文链接:http://www.smashingmagazi ...