Django中如何阻止添加日期重复的公休假日?现有代码失效求解
问题原因
你遇到的问题核心是序列化器验证用的是原始提交日期,但pre_save信号会把日期替换成当前年份的同月同日,导致验证时查询的日期和最终存入数据库的日期不是同一个,自然检测不到重复。
比如你提交2023-10-01,验证时查的是2023-10-01,但信号会改成2024-10-01(假设当前是2024年),如果数据库里已经有2024-10-01,验证时根本查不到,就会跳过验证直接保存,最终导致重复。
解决步骤
1. 统一验证和信号处理的日期逻辑
把信号里的日期转换逻辑抽成工具函数,让序列化器验证时也用转换后的日期查询,确保验证和保存用的是同一个日期:
先添加工具函数:
from django.utils import timezone def get_current_year_date(date): return date.replace(year=timezone.now().year)
修改pre_save信号:
@receiver(pre_save, sender=PublicHoliday) def save_current_year(sender, instance, **kwargs): instance.date_time = get_current_year_date(instance.date_time)
修改序列化器的验证逻辑,用转换后的日期检查:
class PublicHolidayCreateSerializer(serializers.ModelSerializer): class Meta: model = PublicHoliday fields = '__all__' def validate_date_time(self, value): # 先把日期转换成当前年份的日期 current_year_date = get_current_year_date(value) # 创建时检查重复 if self.instance is None: if PublicHoliday.objects.filter(date_time=current_year_date).exists(): raise serializers.ValidationError("This public day already exists") # 更新时排除自身检查重复 else: if PublicHoliday.objects.filter(date_time=current_year_date).exclude(pk=self.instance.pk).exists(): raise serializers.ValidationError("This public day already exists") return value def create(self, validated_data): return super().create(validated_data) def update(self, instance, validated_data): return super().update(instance, validated_data)
2. 模型层添加唯一约束(必做)
只在序列化器做验证有并发风险,比如两个请求同时提交相同日期,序列化器验证都通过,但保存时会出现重复。所以要在模型层给date_time加唯一约束:
class PublicHoliday(models.Model): name = models.CharField(_('Holiday Name'), max_length=100, unique=True) date_time = models.DateField(_('Holiday Date'), unique=True) # 添加unique=True created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __str__(self) -> str: return self.name # 信号部分不变
加完后执行迁移:
python manage.py makemigrations python manage.py migrate
3. 捕获模型层唯一约束异常(可选)
模型层的唯一约束会抛出IntegrityError,可以在序列化器里捕获这个异常,转换成友好的验证错误:
from django.db import IntegrityError class PublicHolidayCreateSerializer(serializers.ModelSerializer): # 其他代码不变 def create(self, validated_data): try: return super().create(validated_data) except IntegrityError: raise serializers.ValidationError("This public day already exists") def update(self, instance, validated_data): try: return super().update(instance, validated_data) except IntegrityError: raise serializers.ValidationError("This public day already exists")
总结
- 用统一的日期转换函数,解决验证逻辑和实际保存日期不一致的问题;
- 模型层加唯一约束,从根本上防止重复数据;
- 捕获模型层异常,返回友好的错误信息。
内容的提问来源于stack exchange,提问作者Mike
相关产品推荐
相关产品推荐

