from django import forms from django.utils import timezone from .models import ConsultationRequest, ConsultationSlot class AvailableSlotChoiceField(forms.ModelChoiceField): def label_from_instance(self, slot): local_start = timezone.localtime(slot.start_at) return ( f"{local_start:%d.%m.%Y} в {local_start:%H:%M} " f"· {slot.duration_minutes} минут · {slot.get_format_display()}" ) class ConsultationRequestForm(forms.ModelForm): slot = AvailableSlotChoiceField( queryset=ConsultationSlot.objects.none(), empty_label="Выберите свободное время", label="Удобное время", ) class Meta: model = ConsultationRequest fields = ( "name", "phone", "contact", "student_grade", "subject", "exam", "format", "slot", "comment", "consent_given", ) widgets = { "name": forms.TextInput(attrs={"placeholder": "Как к вам обращаться"}), "phone": forms.TelInput(attrs={"placeholder": "+7 (999) 123-45-67"}), "contact": forms.TextInput(attrs={"placeholder": "@username, WhatsApp и т. п."}), "comment": forms.Textarea( attrs={ "placeholder": "Класс, темы и вопросы, которые хотите разобрать", "rows": 4, } ), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields["slot"].queryset = ConsultationSlot.objects.available() for name, field in self.fields.items(): if name != "consent_given": field.widget.attrs["class"] = "form-control" self.fields["consent_given"].widget.attrs["class"] = "consent-control" self.fields["contact"].required = False self.fields["student_grade"].required = False self.fields["comment"].required = False def clean_slot(self): slot = self.cleaned_data["slot"] if not slot.is_available: raise forms.ValidationError("Это время уже занято. Выберите другой свободный слот.") return slot def clean(self): cleaned_data = super().clean() slot = cleaned_data.get("slot") subject = cleaned_data.get("subject") requested_format = cleaned_data.get("format") if slot and subject and slot.subject not in (ConsultationSlot.Subject.ANY, subject): self.add_error("slot", "Это время доступно только для другого предмета.") if slot and requested_format not in (ConsultationRequest.Format.ANY, slot.format): self.add_error("format", f"Для выбранного времени доступен формат: {slot.get_format_display()}.") return cleaned_data