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__()))