在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. 怎样使用word2013发布csdn博客

    目前大部分的博客作者在用Word写博客这件事情上都会遇到以下3个痛点: 1.所有博客平台关闭了文档发布接口,用户无法使用Word,Windows Live Writer等工具来发布博客.使用Word写 ...

  2. Java设计模式 -- 简单工厂模式(SimpleFactory)

    一.什么是简单工厂模式 简单工厂模式属于类的创建型模式,又叫做静态工厂方法模式.通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类. 二.模式中包含的角色及其职责 1.工厂(C ...

  3. 为Quartus工程生成rbf文件的方法

    rbf文件是Quartus编译生成的fpga配置文件的二进制数据量格式的文件,主要用于使用外部主机通过PS方式配置FPGA. 在含ARM硬核的SoC FPGA中,可以使用HPS配置FPGA,配置时分为 ...

  4. C++实现wc.exe程序

    github项目地址:https://github.com/insomniali/wc 基本功能 wc.exe -c file     统计文件file的字符数  [实现] wc.exe -w fil ...

  5. mysql的sql性能分析器

    MySQL 的SQL性能分析器主要用途是显示SQL执行的整个过程中各项资源的使用情况.分析器可以更好的展示出不良SQL的性能问题所在. mysql sql profile的使用方法 1.开启mysql ...

  6. ActiveMq 总结(二)

    4.2.6 MessageConsumer MessageConsumer是一个由Session创建的对象,用来从Destination接收消息. 4.2.6.1 创建MessageConsumer ...

  7. AndroidPn源码分析(二)

    接上篇: (一)客户端与服务器建立连接 上一篇写到ClientSession createClientSession这里,创建一个客户端的session.在SessionManager类中创建了ses ...

  8. Email feedback to product team about TFS and SharePoint Integration 2017.2.15

    SharePoint与Team Foundation Server的集成,一直是许多研发团队所关注的问题. 通过这种集成,开发团队可以实现下面的几个功能: 1.  搭建一个与团队项目集成的门户网站,并 ...

  9. LeetCode147:Insertion Sort List

    题目: Sort a linked list using insertion sort. 解题思路: 按题目要求,直接进行插入排序 实现代码: #include <iostream> us ...

  10. [翻译]NUnit---RequiredAddin and RequiresMTA Attributes(十六)

    RequiredAddinAttribute (NUnit 2.5) RequiredAddin特性用于提示一个程序集需要特殊的插件才能保证功能正常.如果没有安装插件,整个程序集会被标记为未运行. N ...