Django事务

django在批量操作时会使用for循环创建、更新、删除一系列对象,这些操作往往是有对应的前提条件的,如要求只有发布人才能删除对应的文章,最简单的方法是先for循环一遍queryset,看是否所有的对象都满足条件,若所有的对象都满足条件则再次for循环进行对应的删除操作,这样会使得代码极度不美观,效率往往也不高。

使用事务可以很好的处理这一类问题,在for循环开始前设置保存点,for循环时,每对比成功一个进行一次删除操作,若出现不符合条件的文章,则回滚数据到for循环前,若for循环完成了也未出现错误,一次性提交所有的数据库操作。

from django.db import transaction

@transaction.atomic
def create(self, request, *args, **kwargs):
    self.before_create()
    serializer = self.get_serializer(data=request.data)
    if serializer.is_valid():
        sid = transaction.savepoint() # 设置保存点
        items = dict_to_query_dict(request.data).getlist('items')
        # 依次添加子退课业务
        for item in items:
            try:
                item_dict = ast.literal_eval(item)
            except SyntaxError:
                return error_response(1, '{}包含非法字符'.format(item))
            item_serializer = ReturnClassHourItemModifySerializer(data=item_dict)
            if item_serializer.is_valid():
                item_instance = item_serializer.save()
            else:
                transaction.savepoint_rollback(sid) # 发生错误 回滚之前保存的所有对象
                return error_response(1, convert_serializer_errors(item_serializer.errors))
        transaction.savepoint_commit(sid) # for循环完成 无错误发生 提交保存点到此处的所有数据库操作
        return success_response('')

bulk_create内部有 with transaction.atomic(using=self.db, savepoint=False): ,已经封装好了事务,只会出现两种结果,报错但一个对象都不生成或者全部生成完毕。

# 批量添加选项
@detail_route(methods=['POST'])
def add_choices(self, request, pk):
    try:
        question = self.get_object()
        choices = dict_to_query_dict(request.data).getlist('choices')
        # list中str转换为dict,dict再转换为Choice类实例,所有的类实例合并为list
        # bulk_create入参该list,其内部封装好事务,若其中一个生产错误,则会自动撤销之前操作
        choice_list = Choice.objects.bulk_create([Choice(**ast.literal_eval(choice)) for choice in choices])
        # PS:bulk_create不会调用实例的save方法,不会发出post_save信号,返回实例无ID,需要再次手动调用save()
        for choice in choice_list:
            choice.save()
        question.choices.add(*choice_list)
        return success_response('添加成功')
    except Exception as e:
        return error_response(1, '发生错误:{}'.format(e.__str__()))