Django中涉及金融的项目
在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中Celery的实现介绍(一)
Django中Celery的实现 Celery官网http://www.celeryproject.org/ 学习资料:http://docs.jinkan.org/docs/celery/ Cele ...
- Django中使用Bootstrap----带view.py视图函数(也就是项目下的脚本文件)
一.Django中使用Bootstrap 1.首先建立工程,建立工程请参照:https://www.cnblogs.com/effortsing/p/10394511.html 2.在Firstdja ...
- Django中-事务操作
如何在Django中进行事务操作呢? 近期,公司里要使用Django开发一套金融相关的系统. 涉及钱了.....安全安全安全 如果钱转到一半,系统崩了,咋办? 如果钱汇到一半,系统崩了,咋办? 如果东 ...
- Django中ORM介绍和字段及其参数
ORM介绍 ORM概念 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术. 简单的说,ORM是通过使用描述 ...
- Django中ORM介绍
Object Relational Mapping(ORM) ORM介绍 ORM概念 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据 ...
- django中的跨表查询梳理
1.前言 最近在写一个小项目,里面主要涉及的就是表与表之间复杂的关系.当真正开发起来的时候,才发现自己对复杂的表关系间的查询有点混乱,趁着这几天的时间,重新梳理了一下. 2.概念 在开始之前,先明确几 ...
- Django中的信号
信号 Django 提供一个“信号分发器”,允许解耦的应用在框架的其它地方发生操作时会被通知到. 简单来说,信号允许特定的sender通知一组receiver某些操作已经发生. 这在多处代码和同一事件 ...
- Django中ORM介绍和字段及字段参数
Object Relational Mapping(ORM) 1 ORM介绍 1.1 ORM概念 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对 ...
- django中的ORM介绍和字段及字段参数
Object Relational Mapping(ORM) ORM介绍 ORM概念 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据 ...
随机推荐
- UVa 12003 Array Transformer (分块)
题意:给定一个序列,然后有 m 个修改,问你最后的序列是什么,修改是这样的 l r v p 先算出从 l 到 r 这个区间内的 小于 v 的个数k,然后把第 p 个的值改成 k * u / (r - ...
- Linux上查看造成IO高负载的进程
方法1:使用iotop工具这是一个python脚本工具,使用方法如:iotop -o方法2:使用工具dmesg使用dmesg之前,需要先开启内核的IO监控:echo 1 >/proc/sys/v ...
- obj-c的优缺点
优点: 1) Cateogies : 类别 2) Posing : 扮演 3) 动态识别 : 编译时与运行时动态识别类型 4) 指标计算 : 指针计算 指针的 +- * / 5) 弹性信息传递 : 某 ...
- day11(多线程,唤醒机制,生产消费者模式,多线程的生命周期)
A:进程: 进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. B:线程: 线程是进程中的一个执行单元,负责当前进程中程序的执 ...
- Hdu1342 Lotto 2017-01-18 17:12 44人阅读 评论(0) 收藏
Lotto Time Limit : 2000/1000ms (Java/Other) Memory Limit : 65536/32768K (Java/Other) Total Submiss ...
- android public.xml 用法
一.android的pulibc.xml文件 如果你用 apktoool 反编译过 apk 就知道,反编译后res/values 下有一个 public.xml 文件,内容如图 这个东西有什么用呢 ...
- C - 无间道之并查集 HihoCoder - 1066
输入 每个测试点(输入文件)有且仅有一组测试数据. 每组测试数据的第1行为一个整数N,表示黑叔叔总共进行的操作次数. 每组测试数据的第2~N+1行,每行分别描述黑叔叔的一次操作,其中第i+1行为一个整 ...
- maven镜像
使用maven管理项目,下载依赖jar包的时候,经常会下载很慢,但是如果使用镜像的话,速度超级快~~只要在.m2/setting.xml文件中设置镜像就可以啦~ 本文来自https://yq.aliy ...
- Ansible之ansible-playbook roles
刚开始学习运用 playbook 时,可能会把 playbook 写成一个很大的文件,到后来可能你会希望这些文件是可以方便去重用的,所以需要重新去组织这些文件. 基本上,使用 include 语句引用 ...
- (原创)Hibernate persistentSet的remove()方法不起作用
情景再现:hibernate 多对多 User对象(员工).Educate对象(培训课程),我想干的事情是想把第三方表格user_educate中的员工_培训课程中的一行信息删掉(删掉员工所选的一门 ...