TabularInline и ограничение перекрытия диапазона дат
Я использую следующий набор форм, чтобы избежать перекрытия диапазонов дат:
class OccupancyInlineFormset(BaseInlineFormSet):
def clean(self):
super().clean()
for form in self.forms:
conflicts = Occupancy.objects.filter(unit=form.cleaned_data['unit'], begin__lte=form.cleaned_data['end'], end__gte=form.cleaned_data['begin'])
if any(conflicts):
raise ValidationError(_('Overlapping occupancies!'), code='overlap')
В принципе, это работает хорошо, но мой текущий экземпляр Occupancy
всегда должен иметь дату окончания 9999-12-31. Когда я изменяю эту дату в форме администратора для текущего экземпляра (и добавляю новый экземпляр с датой окончания 9999-12-31), функция clean()
всегда будет вызывать исключение на основе значений, хранящихся в базе данных. Я не очень понимаю, как я мог бы избежать этого, не изменяя нарушающую дату окончания (9999-12-31 → 2023-01-31) сначала в другой (неограниченной) форме, что уничтожает цель формы TabularInline
. Спасибо за любую помощь!
После обдумывания проблемы я пришел к следующему (неуклюжему, но работающему) решению:
def clean(self):
super().clean()
infinity = datetime.date(9999,12,31)
for form in self.forms:
try:
current_id = form.cleaned_data['id'].pk
except AttributeError:
current_id = None
unit = form.cleaned_data['unit']
begin = form.cleaned_data['begin']
end = form.cleaned_data['end']
# Exclude the instance corresponding to the form and the current occupancy (infinity end date) from the set of conflicting instances
conflicts = Occupancy.objects.filter(unit=unit, begin__lte=end, end__gte=begin).exclude(pk=current_id).exclude(end=infinity)
if any(conflicts):
raise forms.ValidationError(_('Overlapping occupancies!'), code='overlap')
# if the new occupancy has an infinity end date ...
elif end == infinity:
try:
current_occupancy = Occupancy.objects.get(unit=unit, end=datetime.date(9999,12,31))
# ... and is not identical with the current occupancy ...
if current_occupancy.pk != current_id:
# ... update the current occupancy to end just before the new (infinite) occupancy
current_occupancy.end = begin - datetime.timedelta(days=1)
current_occupancy.save()
except ObjectDoesNotExist:
pass
При этом учитывается, что экземпляр может перекрывать (по крайней мере, во время очистки входа) сам себя, и что новое занятие с бесконечной датой окончания должно заменить прежнее "текущее" занятие