在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. POJ 2376 Cleaning Shifts (贪心,区间覆盖)

    题意:给定1-m的区间,然后给定n个小区间,用最少的小区间去覆盖1-m的区间,覆盖不了,输出-1. 析:一看就知道是贪心算法的区间覆盖,主要贪心策略是把左端点排序,如果左端点大于1无解,然后, 忽略小 ...

  2. hdu 2780 Su-Su-Sudoku(DFS数独)

    题目链接:hdu2780 #include<stdio.h> #include<string.h> #include<queue> #include<math ...

  3. SQL笔记---多表左联

    这是实际场景当中的一个例子,拿出来分析总结思路. -- SQL 查询 --SELECT  orderQuery.Rk_SkuCode ,        orderQuery.SkuName,      ...

  4. Source Multiplayer Networking【转】

    https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking Multiplayer games based on th ...

  5. ADO.NET系列之Connection对象

    ADO.NET系列之Connection对象 ADO.NET系列之Command对象 ADO.NET系列之DataAdapter对象 ADO.NET系列之事务和调用存储过程 ADO.NET概念 ADO ...

  6. C# 32位程序,申请大内存,附dome(wpf),亲测可用

    1.我是vs2017,在选装vs的时候,需要安装c++模块,因为申请大内存的必要exe存放在vc的某个目录(下面会给出详细的地址)下的 2.安装完成在vs的安装目录可找到这个文件,我是社区版本的,如果 ...

  7. WPF制作表示透明区域的马赛克画刷

    最近在用WPF制作一款软件,需要像ps一样表示透明区域,于是制作了一个马赛克背景的style.实现比较简单,那么过程和思路就不表了,直接上代码 <DrawingBrush TileMode=&q ...

  8. jsp(Java的服务网页)$javabean

    JSP:Java Server Page(Java的服务网页),也是Java的动态网页.  JSP的本质:其实就是一个Servlet.  JSP---->翻译成Servlet类---->编 ...

  9. log4j学习(二)不同类的日志输出到不同的文件

    目的:一个应用中有两个不同作用的后台服务,我们需要把他们的日志分开,存放到2个不同的日志文件中. 办法:需要在log4j.properties文件中配置两个不同的logger和对应的appender ...

  10. 201621123018《Java程序设计》第3周学习报告

    Week03-面向对象入门 1. 本周学习总结 初学面向对象,会学习到很多碎片化的概念与知识.尝试学会使用思维导图将这些碎片化的概念.知识点组织起来.请使用工具画出本周学习到的知识点及知识点之间的联系 ...