monim67 / django-bootstrap-datepicker-plus

Bootstrap3/Bootstrap4/Bootstrap5 DatePickerInput, TimePickerInput, DateTimePickerInput, MonthPickerInput, YearPickerInput with date-range-picker functionality for django >= 2.0
https://pypi.python.org/pypi/django-bootstrap-datepicker-plus
MIT License
223 stars 63 forks source link

Possible bug: options={'maxDate': foo} changing the initial/default value for field #33

Open GCru opened 5 years ago

GCru commented 5 years ago

Thank you for the great widget.

I am using the minDate and maxDate options in the widgets. The minDate option works as expected. The maxDate option correctly sets the max date, but then also populates the date field as the initial value or default value every time the form is called, instead of using the value from the initial dictionary passed from Views. When only using the minDate option, the correct initial value from the initial dictionary is set.

I also tried another option by not passing the initial dictionary from Views, but setting the defaultDate under options. As before, this works fine alongside the minDate option but gets overridden by the maxDate option so that the default date shown is maxDate.

See my script below. The initial (default) value of date_y is always set to maxDate. Note that in both cases I am defining the widgets in form __init__ because I am passing the minDate and maxDate from Views as these will change each time the form is called.

Thank you Gerhard


class DatePickerPlusPage(View):
    """ Show a form for entering dates date_x and date_y using 
                django_bootstrap_datepicker_plus.
        date_x range cannot be below date_x_range_min
        date_y range cannot be above date_y_range_max
        As intial values for these fields use today's date, or if available the previously entered 
                values 
    """
    form_class = DatePickerPlusForm
    template_name = 'date_picker_plus_page.html'

    def get(self, request):

        if self.request.GET.get('date_x', default=None) is None:
            # first call 
            initial_form_dict={'date_x':datetime.now().date,'date_y': datetime.now().date}
        else:
            # calls with form filled in
            date_picker_form = self.form_class(data=request.GET)
            date_picker_form.is_valid()
            date_x = date_picker_form.cleaned_data['date_x']
            date_y = date_picker_form.cleaned_data['date_y']

            print('This is what the form contains:', date_x, date_y)
            initial_form_dict = {'date_x': date_x,'date_y': date_y}

        context = {}
        context['date_picker_form'] = self.form_class(initial=initial_form_dict,
                date_x_range_min=datetime(2000, 1, 1).date(), 
                                date_y_range_max=datetime(2010, 12, 31).date())

        template = loader.get_template(self.template_name)
        output = template.render(context, request)
        return HttpResponse(output)

class DatePickerPlusForm(forms.Form):

    date_x = forms.CharField()  # needs to be CharField since we are using MonthPicker
    date_y = forms.CharField()

    def __init__(self, *args, date_x_range_min=None, date_y_range_max=None,**kwargs):       
        super().__init__(*args, **kwargs)

        # convert date_x_range_min to json date
        if date_x_range_min is not None:
            date_x_range_min = date_x_range_min.strftime('%b %Y')   
        # convert date_y_range_max to json date
        if date_y_range_max is not None:
            date_y_range_max = date_y_range_max.strftime('%b %Y')

        # set up DatePicker widgets
        self.fields['date_x'].widget=MonthPickerInput(format='%b %Y',options={'minDate': 
                                                                                  date_x_range_min})
        self.fields['date_y'].widget=MonthPickerInput(format='%b %Y',options={'maxDate': 
                                                                                  date_y_range_max})
        return

    def clean_date_x(self):
        """ Convert str to date type"""     
        updated_date = self.cleaned_data['date_x']
        updated_date =  datetime.strptime('1 ' + updated_date ,'%d %b %Y').date()
        return updated_date

    def clean_date_y(self):
        """ Convert str to date type"""     
        updated_date = self.cleaned_data['date_y']
        updated_date = datetime.strptime('15 ' + updated_date,'%d %b %Y').date()
        return updated_date
adremides commented 3 years ago

I encounter this bug too. It seems not working correctly when maxDate is the same day as the actual day. I get around the problem by add 1 to it:

# forms.py
from django import forms
from django.utils import timezone
from dateutil.relativedelta import relativedelta
from .models import SomeModel
from bootstrap_datepicker_plus import DatePickerInput

class SomeModelForm(forms.ModelForm):
    class Meta:
        model = SomeModel
        fields = [
            'date'
        ]

    def __init__(self, *args, **kwargs):
        super(SomeModelForm, self).__init__(*args, **kwargs)

        self.fields['date'].widget = DatePickerInput(
            options={
                "maxDate": (
                    timezone.localdate() + relativedelta(days=1)
                ).strftime('%Y-%m-%d')
            },
        )
adremides commented 3 years ago

Also I found this today in another issue How to restrict date and time in django bootstrap datetimepicker plus? Using strftime('%Y-%m-%d 23:59:59') instead strftime('%Y-%m-%d') solves the issue too.

rez0n commented 3 years ago

Comment for guys who faced with same issue, but with the minDate. You need to set initial date value for datepicker this way.

self.fields['min_date_field'].widget = DatePickerInput(
                format='%Y-%m-%d', options={'minDate': '2021-01-01', "date": '2021-04-04', })