【问题标题】:Is there a 'SelectDateTime' widget for django forms?django 表单是否有“SelectDateTime”小部件?
【发布时间】:2011-07-08 09:15:33
【问题描述】:

与 SelectDate 小部件基本相同,但也有一个小时组件。官方文档里好像没找到?

【问题讨论】:

    标签: django django-forms django-widget


    【解决方案1】:

    这是我使用的拆分日期/时间小部件:

    首先,时间小部件。有很多代码。我在这里复制/粘贴,所以可能有你不需要的进口。我将它保存在一个名为“custom_form_widgets.py”的文件中。

    在底部,您会看到我的“FriendlySplitDateTimeWidget”类,您可以在表单中使用它。小时可以基于 24 或 12 小时制。日期字段呈现为文本输入,但您可以轻松添加您选择的 JavaScript 弹出日历实现。希望对您有所帮助。

    import re
    from datetime import datetime
    
    from django import forms
    from django.conf import settings
    from django.forms.widgets import Widget, Select, MultiWidget, DateInput, TimeInput
    from django.template.loader import render_to_string
    from django.utils.safestring import mark_safe
    from django.utils.text import truncate_words
    
    # Attempt to match many time formats:
    # Example: "12:34:56 P.M."  matches:
    # ('12', '34', ':56', '56', 'P.M.', 'P', '.', 'M', '.')
    # ('12', '34', ':56', '56', 'P.M.')
    # Note that the colon ":" before seconds is optional, but only if seconds are omitted
    time_pattern = r'(\d\d?):(\d\d)(:(\d\d))? *((a{1}|A{1}|p{1}|P{1})(\.)?(m{1}|M{1})(\.)?)?$'
    
    RE_TIME = re.compile(time_pattern)
    # The following are just more readable ways to access re.matched groups:
    HOURS = 0
    MINUTES = 1
    SECONDS = 3
    MERIDIEM = 4
    
    class SelectTimeWidget(Widget):
        """
        A Widget that splits time input into <select> elements.
        Allows form to show as 24hr: <hour>:<minute>,
        or as 12hr: <hour>:<minute> <am|pm> 
    
        Also allows user-defined increments for minutes/seconds
        """
        hour_field = '%s_hour'
        minute_field = '%s_minute'
        second_field = '%s_second'
        meridiem_field = '%s_meridiem'
        twelve_hr = False # Default to 24hr.
    
        def __init__(self, attrs=None, hour_step=None, minute_step=None, second_step=None, twelve_hr=False):
            '''
            hour_step, minute_step, second_step are optional step values for
            for the range of values for the associated select element
            twelve_hr: If True, forces the output to be in 12-hr format (rather than 24-hr)
            '''
            self.attrs = attrs or {}
    
            if twelve_hr:
                self.twelve_hr = True # Do 12hr (rather than 24hr)
    
            if hour_step and twelve_hr:
                self.hours = range(1, 13, hour_step)
            elif hour_step: # 24hr, with stepping.
                self.hours = range(0, 24, hour_step)
            elif twelve_hr: # 12hr, no stepping
                self.hours = range(1, 13)
            else: # 24hr, no stepping
                self.hours = range(0, 24)
    
            if minute_step:
                self.minutes = range(0, 60, minute_step)
            else:
                self.minutes = range(0, 60)
    
            if second_step:
                self.seconds = range(0, 60, second_step)
            else:
                self.seconds = range(0, 60)
    
        def render(self, name, value, attrs=None):
            try: # try to get time values from a datetime.time object (value)
                hour_val, minute_val, second_val = value.hour, value.minute, value.second
                if self.twelve_hr:
                    if hour_val >= 12:
                        meridiem_val = 'p.m.'
                    else:
                        meridiem_val = 'a.m.'
                else:
                    meridiem_val = None
            except AttributeError:
                hour_val = minute_val = second_val = meridiem_val = ''
                if isinstance(value, basestring):
                    match = RE_TIME.match(value)
                    if match:
                        time_groups = match.groups();
                        hour_val = int(time_groups[HOURS]) % 24 # force to range(0-24)
                        minute_val = int(time_groups[MINUTES])
    
                        if time_groups[SECONDS] is None:
                            second_val = 0
                        else:
                            second_val = int(time_groups[SECONDS])
    
                        # check to see if meridiem was passed in
                        if time_groups[MERIDIEM] is not None:
                            meridiem_val = time_groups[MERIDIEM]
                        else: # otherwise, set the meridiem based on the time
                            if self.twelve_hr:
                                if hour_val >= 12:
                                    meridiem_val = 'p.m.'
                                else:
                                    meridiem_val = 'a.m.'
                            else:
                                meridiem_val = None
    
            if self.twelve_hr:
                # Modify the hour value appopriately for 12-hour clocks.
                if hour_val > 12 and hour_val < 24:
                    hour_val = hour_val % 12
                elif hour_val == 0:
                    hour_val = 12
    
            output = []
            if 'id' in self.attrs:
                id_ = self.attrs['id']
            else:
                id_ = 'id_%s' % name
    
            # NOTE: for times to get displayed correctly, the values MUST be converted to unicode
            # When Select builds a list of options, it checks against Unicode values
            if hour_val != '':
                hour_val = u"%.2d" % hour_val
            if minute_val != '':
                minute_val = u"%.2d" % minute_val
            if second_val != '':
                second_val = u"%.2d" % second_val
    
            hour_choices = [("%.2d" % i, "%.2d" % i) for i in self.hours]
            hour_choices[0:0] = [('', '--')]
            local_attrs = self.build_attrs(id=self.hour_field % id_)
            select_html = Select(choices=hour_choices).render(self.hour_field % name, hour_val, local_attrs)
            output.append(select_html)
    
            minute_choices = [("%.2d" % i, "%.2d" % i) for i in self.minutes]
            minute_choices[0:0] = [('', '--')]
            local_attrs['id'] = self.minute_field % id_
            select_html = Select(choices=minute_choices).render(self.minute_field % name, minute_val, local_attrs)
            output.append(select_html)
    
            second_choices = [("%.2d" % i, "%.2d" % i) for i in self.seconds]
            second_choices[0:0] = [('', '--')]
            local_attrs['id'] = self.second_field % id_
            select_html = Select(choices=second_choices).render(self.second_field % name, second_val, local_attrs)
            output.append(select_html)
    
            if self.twelve_hr:
                #  If we were given an initial value, make sure the correct meridiem get's selected.
                if meridiem_val and meridiem_val.startswith('p'):
                    meridiem_choices = [('p.m.', 'p.m.'), ('a.m.', 'a.m.'), ('', '--')]
                else:
                    meridiem_choices = [('', '--'), ('a.m.', 'a.m.'), ('p.m.', 'p.m.')]
    
                local_attrs['id'] = local_attrs['id'] = self.meridiem_field % id_
                select_html = Select(choices=meridiem_choices).render(self.meridiem_field % name, meridiem_val, local_attrs)
                output.append(select_html)
    
            selects_html = u'\n'.join(output)
    
            return mark_safe('<div class="friendly_time_widget">%s</div>') % (selects_html)
    
        def id_for_label(self, id_):
            return '%s_hour' % id_
        id_for_label = classmethod(id_for_label)
    
        def value_from_datadict(self, data, files, name):
            meridiem = data.get(self.meridiem_field % name)
            h = data.get(self.hour_field % name, 0) # hour
            m = data.get(self.minute_field % name, 0) # minute
            s = data.get(self.second_field % name, 0) # second
    
            if any(falsy in (h, m) for falsy in ('', None)):
                return None
    
            #NOTE: if meridiem IS None, assume 24-hr
            if meridiem is not None:
                if meridiem.lower().startswith('p') and int(h) != 12:
                    h = (int(h) + 12) % 24
                elif meridiem.lower().startswith('a') and int(h) == 12:
                    h = 0
    
            if (int(h) == 0 or h) and m and s:
                return '%s:%s:%s' % (h, m, s)
    
            return data.get(name, None)
    
    #This is the widget that splits the date and time.
    class FriendlySplitDateTimeWidget(MultiWidget):
        """
        A Widget that splits datetime input into two <input type="text"> boxes
        and uses a better time selector
        """
        date_format = DateInput.format
        time_format = TimeInput.format
    
        def __init__(self, attrs=None, date_format=None, time_format=None, twelve_hr=True):
            if date_format:
                self.date_format = date_format
            if time_format:
                self.time_format = time_format
            widgets = (DateInput(attrs=attrs, format=self.date_format),
                       SelectTimeWidget(twelve_hr=twelve_hr))
            super(FriendlySplitDateTimeWidget, self).__init__(widgets, attrs)
    
        def decompress(self, value):
            if value:
                if isinstance(value, basestring):
                    value = datetime.strptime(value, '%Y-%m-%d %H:%M:%S')
                return [value.date(), value.time().replace(microsecond=0)]
            return [None, None]
    

    【讨论】:

      猜你喜欢
      • 2021-06-13
      • 2011-01-19
      • 2019-07-30
      • 2015-10-06
      • 2013-04-03
      • 2017-10-27
      • 2011-10-29
      • 2020-05-22
      相关资源
      最近更新 更多