asaglimbeni / django-datetime-widget

Bootstrap django-datetime-widget is a simple and clean widget for DateField, Timefiled and DateTimeField in Django framework. It is based on Bootstrap datetime picker, supports both Bootstrap 3 and Bootstrap 2
Other
219 stars 125 forks source link

Enter a valid date/time. #77

Open glennpierce opened 9 years ago

glennpierce commented 9 years ago

Hi I have setup a form widget like

dt = forms.DateTimeField("Export start time", widget=DateTimeWidget(usel10n=True, bootstrap_version=3))

The format I chose in formats.py is chosen correctly. I can change the format and the widget changes when choosing a date. On submit though I always get "Enter a valid date/time."

Any suggestions ?

Thanks

johanneshk commented 9 years ago

Same here, maybe this is an issue with different localizations / conversion between localizations?!

EDIT: Solution (?): Specify input format for the DateField:

deadline = forms.DateField(input_formats = ["%d.%m.%Y"], required = True, widget=DateWidget(usel10n=True, bootstrap_version=3,))

Now I specify the localized date format and it works for me. Is it possible to specify this automatically!?

tylerecouture commented 9 years ago

I'm also having this problem. If I leave the widget format as the default, the day and month are swapped when saved, if I try to specify any sort of format in the widget, I get the error "Enter a valid date/time."

johanneshk commented 9 years ago

The datewidget simply passes a string (as given on the UI) to the DateField. The DateField first checks if it got a datetime.date(time), which is not the case. Then it tries to parse the string by testing different format specifiers. In my case the format specifier used by the datewidget was not defined in the DateField, so I defined this by hand (see above). What's not so nice is that you would need to specify a format for the DateField for each possible localization, this should somehow work automatically, e.g. if the datewidget did not pass a string but already a datetime.date object.

I assume that the swapping of day and month is because e.g. datewidget use "%d/%m/%Y" and DateField assumes "%m/%d/%Y".

In my case I only have one localization so it's ok for now.

netinsideout commented 8 years ago

The problem in init method of PickerWidgetMixin when language string converted to js objects. Init in django enviroment with following code

datestart = forms.DateTimeField(label=("Date Start"), widget=DateTimeWidget(usel10n=True, bootstrap_version=3), required=False)

called only once, when application loading, and not per request. Actually now, with example from documentation django-datetime-widget does not support locale changes per requests, you need to manually call widget initialization per request to change locale of widget and have true support of l18n. I think this is main problem of ValidationErrors on l18n-enabled sites.

michaeljones commented 8 years ago

Great catch, I suspect this is my fault from my opinionated refactor a while back. I moved some of the code into the __init__ as I clearly failed to realise some of the details might change from request to request. We should change that back.

netinsideout commented 8 years ago

This class works fine for me on l18n-enabled site.

class PickerWidgetMixin(object):

    format_name = None
    glyphicon = None

    def __init__(self, attrs=None, options=None, usel10n=None, bootstrap_version=None):
        if bootstrap_version in [2,3]:
            self.bootstrap_version = bootstrap_version
        else:
            # default 2 to mantain support to old implemetation of django-datetime-widget
            self.bootstrap_version = 2

        if attrs is None:
            attrs = {'readonly': ''}

        self.options = options

        self.format = None
        self.usel10n = usel10n

        if not usel10n and 'format' in self.options:
            # We want to have a Javascript style date format specifier in the options dictionary and we
            # want a Python style date format specifier as a member variable for parsing the date string
            # from the form data
            self.format = toPython_re.sub(
                lambda x: dateConversiontoPython[x.group()],
                self.options['format']
                )
        else:
            self.format = None

        super(PickerWidgetMixin, self).__init__(attrs, format=self.format)

    def render(self, name, value, attrs=None):
        final_attrs = self.build_attrs(attrs)
        rendered_widget = super(PickerWidgetMixin, self).render(name, value, final_attrs)

        language = get_supported_language(get_language())
        self.options['language'] = language

        if self.usel10n or not self.format:
            self.format = get_format(self.format_name)[0]
            self.options['format'] = toJavascript_re.sub(
                lambda x: dateConversiontoJavascript[x.group()],
                self.format
                )

        #if not set, autoclose have to be true.
        self.options.setdefault('autoclose', True)

        # Build javascript options out of python dictionary
        options_list = []
        for key, value in iter(self.options.items()):
            options_list.append("%s: %s" % (key, quote(key, value)))

        js_options = ",\n".join(options_list)

        # Use provided id or generate hex to avoid collisions in document
        id = final_attrs.get('id', uuid.uuid4().hex)

        clearBtn = quote('clearBtn', self.options.get('clearBtn', 'true')) == 'true'

        return mark_safe(
            BOOTSTRAP_INPUT_TEMPLATE[self.bootstrap_version]
                % dict(
                    id=id,
                    rendered_widget=rendered_widget,
                    clear_button=CLEAR_BTN_TEMPLATE[self.bootstrap_version] if clearBtn else "",
                    glyphicon=self.glyphicon,
                    options=js_options
                    )
        )

    def _media(self):

        js = ["js/bootstrap-datetimepicker.js"]

        language = get_supported_language(get_language())
        if language != 'en':
            js.append("js/locales/bootstrap-datetimepicker.%s.js" % language)

        return widgets.Media(
            css={
                'all': ('css/datetimepicker.css',)
                },
            js=js
            )

    media = property(_media)
javaghost commented 8 years ago

While using DateTimeInput (default for admin) it actually splits the date and time fields as id_fieldName_0 as date field and id_fieldName_1 as time field. When this DateTimeWidget is used, the post data contains only fieldName as single value, when django expects list of values. Hence in django admin, DateTimeWidget will always throw ValidationError to send values as list. (Tested in Django 1.8 with l10n false in settings)

silentjay commented 7 years ago

Is there a fix for this? I'm getting this error with a DateTimeField and looking at this comment thread I'm no closer to finding a fix.

Update: Seems the widget is adding apostrophes around the value e.g. "'2016-03-21 11:00:00'" which is causing the validation failure. Is there anyway to disable this?