DRF序列化器validate方法并行查询下失效问题求助
解决DRF并发请求下重复创建影片数据的问题
你的问题是典型的**竞态条件(Race Condition)**导致的:两个并行请求在执行序列化器validate方法时,都查询到数据库中没有对应影片,随后同时执行保存操作,最终导致重复数据入库。序列化器的validate仅能在内存层面做前置检查,无法应对并发场景下的数据库状态变化,必须结合数据库约束或事务锁来解决。
最优方案:数据库层面添加唯一约束
数据库级别的约束是防止重复数据最可靠的手段,无论业务层代码如何操作,数据库都会直接拦截违反约束的写入请求。
根据你的需求(不区分标题、导演的大小写),可以在Film模型中添加基于字段小写转换的组合唯一约束:
from django.db import models from django.db.models.functions import Lower class Film(models.Model): title = models.CharField(max_length=255) release_date = models.DateField() director = models.CharField(max_length=255) class Meta: constraints = [ models.UniqueConstraint( Lower('title'), 'release_date', Lower('director'), name='unique_film_identity' ) ]
- 该约束会将
title和director转换为小写后,与release_date组合作为唯一标识,实现不区分大小写的重复校验。 - 注意:MySQL 8.0+、PostgreSQL等主流数据库均支持函数式唯一约束,若使用旧版数据库,可提前在模型中存储小写后的字段(如
title_lower)并建立普通唯一约束。
业务层适配异常处理
添加数据库约束后,需要在视图层捕获数据库抛出的IntegrityError,转换为DRF友好的验证错误:
from django.db import IntegrityError from rest_framework import generics from rest_framework import serializers class FilmCreateAPIView(generics.CreateAPIView): serializer_class = FilmSerializer def perform_create(self, serializer): try: serializer.save() except IntegrityError: raise serializers.ValidationError('Film with such parameters (title, director, release date) already exists')
补充方案:业务层事务+行锁
如果暂时无法修改数据库约束,可在视图层通过原子事务和行锁来避免竞态:
from django.db import transaction from rest_framework import generics from rest_framework import serializers class FilmCreateAPIView(generics.CreateAPIView): serializer_class = FilmSerializer @transaction.atomic def perform_create(self, serializer): validated_data = serializer.validated_data # 使用select_for_update锁定查询结果,确保并发请求串行处理 existing_film = Film.objects.filter( title__iexact=validated_data['title'], release_date=validated_data['release_date'], director__iexact=validated_data['director'] ).select_for_update().first() if existing_film: raise serializers.ValidationError('Film with such parameters already exists') serializer.save()
@transaction.atomic确保整个查询-保存操作是原子性的;select_for_update会锁定查询到的行(若存在),未查询到则后续的保存操作会在事务内完成,避免两个请求同时创建重复数据。
注意事项
- 优先选择数据库唯一约束:业务层的锁机制仅能覆盖API请求场景,若存在后台管理、脚本等其他数据写入渠道,数据库约束是唯一可靠的拦截手段;
- 避免依赖序列化器的
validate方法做并发校验:该方法无法感知其他请求对数据库的修改,仅适合做业务规则的前置校验。
内容的提问来源于stack exchange,提问作者TeraBasedProgrammer
相关产品推荐
相关产品推荐

