Projet

Général

Profil

0001-use-specialized-widgets-for-datetimes-15087.patch

Serghei Mihai (congés, retour 15/05), 23 février 2017 15:39

Télécharger (8,16 ko)

Voir les différences:

Subject: [PATCH] use specialized widgets for datetimes (#15087)

 corbo/forms.py                           |  18 +++-
 corbo/templates/corbo/announce_form.html |   5 -
 corbo/widgets.py                         | 174 +++++++++++++++++++++++++++++++
 tests/test_misc.py                       |   6 ++
 4 files changed, 194 insertions(+), 9 deletions(-)
 create mode 100644 corbo/widgets.py
 create mode 100644 tests/test_misc.py
corbo/forms.py
8 8
from django.core.exceptions import ValidationError
9 9

  
10 10
from .models import Announce, Category, Broadcast, channel_choices
11
from . import widgets
12

  
13

  
14
DATETIME_OPTIONS = {
15
        'weekStart': 1,
16
        'autoclose': True,
17
}
18

  
19

  
20
class DateTimeWidget(widgets.DateTimeWidget):
21
    def __init__(self, *args, **kwargs):
22
        super(DateTimeWidget, self).__init__(*args, options=DATETIME_OPTIONS, **kwargs)
11 23

  
12 24

  
13 25
class AnnounceForm(forms.ModelForm):
......
16 28
        model = Announce
17 29
        exclude = ('identifier',)
18 30
        widgets = {
19
            'publication_time': forms.TextInput(attrs={'class': 'datetimepicker',
20
                                                       'readonly': True}),
21
            'expiration_time': forms.TextInput(attrs={'class': 'datetimepicker',
22
                                                      'readonly': True}),
31
            'publication_time': DateTimeWidget(),
32
            'expiration_time': DateTimeWidget(),
23 33
            'category': forms.HiddenInput()
24 34
        }
25 35

  
corbo/templates/corbo/announce_form.html
28 28
    <button class="submit-button">{% trans "Save" %}</button>
29 29
    <a class="cancel" href="{% url 'view_category' slug=category.slug %}">{% trans 'Cancel' %}</a>
30 30
  </div>
31
  <script type="text/javascript">
32
    $(function() {
33
        $(".datetimepicker" ).datetimepicker();
34
    });
35
</script>
36 31
</form>
37 32
{% endblock %}
corbo/widgets.py
1
# Bootstrap django-datetime-widget is a simple and clean widget for DateField,
2
# Timefiled and DateTimeField in Django framework. It is based on Bootstrap
3
# datetime picker, supports Bootstrap 2
4
#
5
# https://github.com/asaglimbeni/django-datetime-widget
6
#
7
# License: BSD
8
# Initial Author: Alfredo Saglimbeni
9

  
10
import json
11
import re
12
import uuid
13

  
14
from django.forms.widgets import DateTimeInput, DateInput, TimeInput
15
from django.utils.formats import get_language
16
from django.utils.safestring import mark_safe
17

  
18
DATE_FORMAT_JS_PY_MAPPING = {
19
    'P': '%p',
20
    'ss': '%S',
21
    'ii': '%M',
22
    'hh': '%H',
23
    'HH': '%I',
24
    'dd': '%d',
25
    'mm': '%m',
26
    'yy': '%y',
27
    'yyyy': '%Y',
28
}
29

  
30
DATE_FORMAT_TO_PYTHON_REGEX = re.compile(r'\b(' + '|'.join(DATE_FORMAT_JS_PY_MAPPING.keys()) + r')\b')
31

  
32

  
33
DATE_FORMAT_PY_JS_MAPPING = {
34
    '%M': 'ii',
35
    '%m': 'mm',
36
    '%I': 'HH',
37
    '%H': 'hh',
38
    '%d': 'dd',
39
    '%Y': 'yyyy',
40
    '%y': 'yy',
41
    '%p': 'P',
42
    '%S': 'ss'
43
}
44

  
45
DATE_FORMAT_TO_JS_REGEX = re.compile(r'(?<!\w)(' + '|'.join(DATE_FORMAT_PY_JS_MAPPING.keys()) + r')\b')
46

  
47

  
48
BOOTSTRAP_INPUT_TEMPLATE = """
49
       <div id="%(id)s"  class="controls input-append date">
50
           %(rendered_widget)s
51
           %(clear_button)s
52
           <span class="add-on"><i class="icon-th"></i></span>
53
       </div>
54
       <script type="text/javascript">
55
           $("#%(id)s").datetimepicker({%(options)s});
56
       </script>
57
       """
58

  
59
CLEAR_BTN_TEMPLATE = """<span class="add-on"><i class="icon-remove"></i></span>"""
60

  
61

  
62
class PickerWidgetMixin(object):
63

  
64
    format_name = None
65
    glyphicon = None
66

  
67
    def __init__(self, attrs=None, options=None, usel10n=None):
68

  
69
        if attrs is None:
70
            attrs = {'readonly': ''}
71

  
72
        self.options = options
73
        self.options['language'] = get_language().split('-')[0]
74

  
75
        # We're not doing localisation, get the Javascript date format provided by the user,
76
        # with a default, and convert it to a Python data format for later string parsing
77
        date_format = self.options['format']
78
        self.format = DATE_FORMAT_TO_PYTHON_REGEX.sub(
79
            lambda x: DATE_FORMAT_JS_PY_MAPPING[x.group()],
80
            date_format
81
            )
82

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

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

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

  
92
        # Build javascript options out of python dictionary
93
        options_list = []
94
        for key, value in iter(self.options.items()):
95
            options_list.append("%s: %s" % (key, json.dumps(value)))
96

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

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

  
102
        return mark_safe(BOOTSTRAP_INPUT_TEMPLATE % dict(
103
                    id=id,
104
                    rendered_widget=rendered_widget,
105
                    clear_button=CLEAR_BTN_TEMPLATE if self.options.get('clearBtn') else '',
106
                    glyphicon=self.glyphicon,
107
                    options=js_options
108
                    )
109
        )
110

  
111

  
112
class DateTimeWidget(PickerWidgetMixin, DateTimeInput):
113
    """
114
    DateTimeWidget is the corresponding widget for Datetime field, it renders both the date and time
115
    sections of the datetime picker.
116
    """
117

  
118
    format_name = 'DATETIME_INPUT_FORMATS'
119
    glyphicon = 'glyphicon-th'
120

  
121
    def __init__(self, attrs=None, options=None, usel10n=None):
122

  
123
        if options is None:
124
            options = {}
125

  
126
        # Set the default options to show only the datepicker object
127
        options['format'] = options.get('format', 'dd/mm/yyyy hh:ii')
128

  
129
        super(DateTimeWidget, self).__init__(attrs, options, usel10n)
130

  
131

  
132
class DateWidget(PickerWidgetMixin, DateInput):
133
    """
134
    DateWidget is the corresponding widget for Date field, it renders only the date section of
135
    datetime picker.
136
    """
137

  
138
    format_name = 'DATE_INPUT_FORMATS'
139
    glyphicon = 'glyphicon-calendar'
140

  
141
    def __init__(self, attrs=None, options=None, usel10n=None):
142

  
143
        if options is None:
144
            options = {}
145

  
146
        # Set the default options to show only the datepicker object
147
        options['startView'] = options.get('startView', 2)
148
        options['minView'] = options.get('minView', 2)
149
        options['format'] = options.get('format', 'dd/mm/yyyy')
150

  
151
        super(DateWidget, self).__init__(attrs, options, usel10n)
152

  
153

  
154
class TimeWidget(PickerWidgetMixin, TimeInput):
155
    """
156
    TimeWidget is the corresponding widget for Time field, it renders only the time section of
157
    datetime picker.
158
    """
159

  
160
    format_name = 'TIME_INPUT_FORMATS'
161
    glyphicon = 'glyphicon-time'
162

  
163
    def __init__(self, attrs=None, options=None, usel10n=None):
164

  
165
        if options is None:
166
            options = {}
167

  
168
        # Set the default options to show only the timepicker object
169
        options['startView'] = options.get('startView', 1)
170
        options['minView'] = options.get('minView', 0)
171
        options['maxView'] = options.get('maxView', 1)
172
        options['format'] = options.get('format', 'hh:ii')
173

  
174
        super(TimeWidget, self).__init__(attrs, options, usel10n)
tests/test_misc.py
1
from corbo.widgets import DateTimeWidget, DateWidget, TimeWidget
2

  
3
def test_widgets_init():
4
    DateTimeWidget()
5
    DateWidget()
6
    TimeWidget()
0
-