为了账号安全,请及时绑定邮箱和手机立即绑定

Django 原子事务处理唯一约束

Django 原子事务处理唯一约束

梵蒂冈之花 2023-06-06 15:03:55
我有模型class Order(models.Model):  order_id = models.CharField(max_length=30, default='', unique = True)  amount = models.FloatField()我有这个循环将对象从 json 保存到我的数据库并验证唯一字段order_idfor json_obj in json_data:   order = Order(symbol=json_obj['order_id'], amount= json_obj['amnount'])   try:     order.save()   except IntegrityError as exception:     if 'UNIQUE constraint failed' in exception.args[0]:        print('duplicate order id => skip this')        continue一切正常,但是当我将 @transaction.atomic 装饰器添加到我的函数时,一切都会中断。我得到一个错误: "An error occurred in the current transaction. You can't "    django.db.transaction.TransactionManagementError: An error occurred in the current transaction. You can't execute queries until the end of the 'atomic' block.处理这种情况的推荐方法是什么?UPD:我使用事务原子装饰器来加速我的代码,因为我有大量的订单要保存,而且这是一个在后台运行的相当昂贵的操作。UPD2:我尝试使用get_or_create method 但没有得到任何真正的性能提升,所以不能使用它
查看完整描述

2 回答

?
慕村9548890

TA贡献1884条经验 获得超4个赞

引用Django 文档:

捕获数据库错误的正确方法是围绕一个原子块

这是您的代码的改编:

from django.db import IntegrityError, transaction


for json_obj in json_data:

    order = Order(symbol=json_obj['order_id'], amount=json_obj['amnount'])

    try:

        with transaction.atomic():

            order.save()

    except IntegrityError as exception:

        if 'UNIQUE constraint failed' in exception.args[0]:

            print('duplicate order id => skip this')

            continue

您还可以使用get_or_create来实现您想要的:


for json_obj in json_data:

    obj, created = Order.objects.get_or_create(

        symbol=json_obj['order_id'],

        defaults={'amount': json_obj['amnount'])},

    )

    if not created:

        print('order id already existing => skip this')

如果你想要一个快速的代码(根据你的评论),你可以使用bulk_create。


orders_to_create = []


for json_obj in json_data:

    orders_to_create.append(Order(symbol=json_obj['order_id'], amount=json_obj['amnount']))


Order.objects.bulk_create(orders_to_create, ignore_conflicts=True)

请注意


在支持它的数据库上(除 Oracle 之外的所有数据库),将 ignore_conflicts 参数设置为 True 告诉数据库忽略无法插入任何未通过约束(例如重复唯一值)的行


在不使用ignore_conflicts设置的情况下,您可以执行以下操作:


orders_to_create = []


order_ids = [json_obj['order_id'] for json_obj in json_data]


existing_order_ids = Order.objects.filter(symbol__in=order_ids).values_list('symbol', flat=True)


for json_obj in json_data:

    # this condition prevents any IntegrityError du to an existing order_id

    if json_obj['order_id'] not in existing_order_ids:

        orders_to_create.append(Order(symbol=json_obj['order_id'], amount=json_obj['amnount']))


Order.objects.bulk_create(orders_to_create)


查看完整回答
反对 回复 2023-06-06
?
浮云间

TA贡献1829条经验 获得超4个赞

首先,transaction.atomic装饰器将数据库提交推迟到装饰函数的末尾。因此,您的异常处理现在将不起作用,因为异常会在您的 try-except 块之外抛出。文档还建议您不要在原子块内捕获异常。

或许这也会给你带来一个麻烦TransactionManagementError。如果在原子块内发生异常,则不允许执行任何数据库查询。

您应该将 try-except 移到您的函数之外并检查是否可以解决您的问题。


查看完整回答
反对 回复 2023-06-06
  • 2 回答
  • 0 关注
  • 191 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信