Django forms DateTimeInput widget- how to specify max date?

6.1k views Asked by At

I have a form displayed in my Django project, and one of the form fields is a DateTimeInput widget, defined with:

    presentation_date = mDateTimeField(required=False, label="Presentation date", widget=forms.DateTimeInput(format='%d/%m/%Y %H:%M')) 

This widget currently displays all dates between the start of last year to the end of this year (01/01/2016- 31/12/2017). However, at the end of last year this caused some issues, as it meant that users were unable to select dates for the beginning of this year. What I want to do is extend the range of the dates available for selection in the widget by one year (i.e the new range would be 01/01/2016- 31/12/2018).

I understand that it is possible to do this using form validation (for example, by writing a view such as clean_presentation_date() and performing the validation inside that view, but this won't solve the issue I'm having- as currently, dates before 01/01/2016 or after 31/12/2017 (displayed in the datetimepicker calendar that's shown when the user selects the field in the form) are 'greyed out' and it is not possible to select them. This is exactly the functionality that I want, but I just want to change the values of the dates on which it is performed, so that at least the whole of the next calendar year is always selectable.

I have searched for maxDate & minDate variables within the forms.py file where the form with this field is defined, but can't find anything that looks like it is what's restricting which dates are available to be selected, and which are not selectable.

The view that's showing the page with this form on it is defined with:

def concept(request, project_id):
    project = Project.objects.prefetch_related('budget_versions').get(id=project_id)
    deposit = Deposit.objects.get_or_create(project=project)[0]
    presentations = project.budget_versions.select_related('meeting').prefetch_related('budget_items', 'cci_items', 'presenters').filter(version_number__isnull=False).annotate(vn=F('version_number') * -1).order_by('presentation_date', 'created', '-vn')
    end_details = EndDetails.objects.get_or_create(project=project)[0]
    presentation_formset = BudgetPresentationFormset(prefix="presentations", instance=project, queryset=presentations)
    drawing_formset = DrawingUploadFormset(prefix="drawings", queryset=Drawing.objects.filter(budget__in=presentations).order_by('budget__presentation_date', 'budget__created'))

    context = {
        'project': project,
        'presentations': presentations,
        'presentation_formset': presentation_formset,
        'drawing_formset': drawing_formset,
        'deposit_form': DepositInfoForm(instance=deposit),
        'ended_form': EndDetailsForm(instance=end_details),
        'budget_notes_form': BudgetNotesForm(instance=project.budget_overview),
    }

The particular form I'm looking at is the presentations one, defined in forms.py:

class FirstPresentationForm(ValidatedForm):
    """ UNUSED """
    who_presenting = forms.CharField()
    details = forms.CharField(required=False, label='Original option costed')
    presentation_date = mDateTimeField(required=False, label="Presentation date", widget=forms.DateTimeInput(format='%d/%m/%Y %H:%M')) #ERF(19/12/2016 @ 1210) Remove 'datetimepicker' from field..., attrs=({'class':'datetimepicker'})))

    class Meta:
        model = Budget
        fields = ('presentation_date','who_presenting','details')

    def __init__(self, *args, **kwargs):
        instance = kwargs.get('instance', {})
        project = instance.project

        who_presenting = [pe.employee.id for pe in project.assigned.select_related('employee').filter(role=Role.P)]
        #Make into an array with two employee IDs, or None. If two results were found, they will already be in the right format
        if len(who_presenting)==1:
            who_presenting.append(None)
        elif not who_presenting:
            who_presenting = None

        if instance.presentation_date:
            pres_meeting, created = Meeting.objects.get_or_create(project=project, purpose='6')
            self.pres_meeting_id = pres_meeting.id
            self.pres_meeting_creator = pres_meeting.event_creator or ''
            if created:
                pres_meeting.date = instance.presentation_date
                pres_meeting.save()

        initial = kwargs.get('initial', {})
        initial={
                    'who_presenting': who_presenting,
                    }
        kwargs['initial'] = initial

        super(FirstPresentationForm, self).__init__(*args, **kwargs)
        self.fields['who_presenting'] = AutoFlexiSelect(model='e', required=False, choices=get_choices('DESIGN_EMPLOYEE_CHOICES'), current_id=who_presenting, label="Who is presenting")
        self.fields['presentation_date'].widget.attrs.update({'data-meeting-id': getattr(self,'pres_meeting_id', ''), 'data-meeting-creator': getattr(self,'pres_meeting_creator', '')})

    def save(self, commit=True):
        project = self.instance.project
        data = self.cleaned_data

        try: ProjectEmployee.objects.filter(project=project, role=Role.P).delete() #Delete current records, if any, to prevent build up on editing this field
        except ObjectDoesNotExist: pass
        if data['who_presenting']:
            designers = data['who_presenting']
            # Get the ids from the form field (received as a string)
            designers = [re.sub('\D+', '', s) for s in designers.split(',')]

            who_presenting_1 = Employee.objects.get(id=designers[0])
            who_presenting_2 = designers[1] #If only one employee in selected choice, this will be None
            if who_presenting_2: who_presenting_2 = Employee.objects.get(id=designers[1]) #If two employees in selected choice, sets the second one

            pe = ProjectEmployee(project=project, employee=who_presenting_1, role=Role.P)
            pe.save()

            if who_presenting_2: #If a second designer, delete as well
                pe = ProjectEmployee(project=project, employee=who_presenting_2, role=Role.P)
                pe.save()

        if 'presentation_date' in self.changed_data:
            from events.models import refresh_group_cache
            print '************'
            pres_meeting = Meeting.objects.get(project=project, purpose='6')
            self.instance.meeting = pres_meeting
            self.instance.save()
            print 'Updating date', data['presentation_date'], pres_meeting.id
            pres_meeting.date = data['presentation_date']
            pres_meeting.save()
            refresh_group_cache(pres_meeting.event_creator.calendar_id, pres_meeting.date.year, pres_meeting.date.month, pres_meeting.event_id, pres_meeting)

        return super(FirstPresentationForm, self).save(commit=commit)

I have tried passing/ setting max_date as an attribute of the presentation_date variable, but it doesn't appear to have an attribute/ property by this name...

How can I specify a particular date as this widget's maxDate, so that the user is able to select dates up until December of next year, rather than just up until December of this year?

Edit

The mDateTimeField that's using the widget is also defined in forms.py, with:

class mDateTimeField(forms.DateTimeField):
    def __init__(self, *args, **kwargs):
        kwargs.setdefault('input_formats', DATE_INPUT_FORMATS)
        super(mDateTimeField, self).__init__(*args, **kwargs)
1

There are 1 answers

5
Ranadip Dutta On

This should help you:

widgets = {
    'start_date': forms.DateInput(attrs={
        'class':'datepicker', 'data-min': YOUR_MIN_DATE, 
        'data-max': YOUR_MAX_DATE}),
}

Usage:

minDate: $(this).data('min'),
maxDate: $(this).data('max'),