diff options
Diffstat (limited to 'django/forms')
| -rw-r--r-- | django/forms/widgets.py | 77 |
1 files changed, 53 insertions, 24 deletions
diff --git a/django/forms/widgets.py b/django/forms/widgets.py index 01ada423c9..93f831e6cc 100644 --- a/django/forms/widgets.py +++ b/django/forms/widgets.py @@ -5,6 +5,7 @@ HTML Widget classes import copy import datetime import re +import warnings from contextlib import suppress from itertools import chain @@ -33,19 +34,23 @@ __all__ = ( MEDIA_TYPES = ('css', 'js') +class MediaOrderConflictWarning(RuntimeWarning): + pass + + @html_safe class Media: - def __init__(self, media=None, **kwargs): - if media: - media_attrs = media.__dict__ + def __init__(self, media=None, css=None, js=None): + if media is not None: + css = getattr(media, 'css', {}) + js = getattr(media, 'js', []) else: - media_attrs = kwargs - - self._css = {} - self._js = [] - - for name in MEDIA_TYPES: - getattr(self, 'add_' + name)(media_attrs.get(name)) + if css is None: + css = {} + if js is None: + js = [] + self._css = css + self._js = js def __str__(self): return self.render() @@ -88,24 +93,48 @@ class Media: return Media(**{str(name): getattr(self, '_' + name)}) raise KeyError('Unknown media type "%s"' % name) - def add_js(self, data): - if data: - for path in data: - if path not in self._js: - self._js.append(path) + @staticmethod + def merge(list_1, list_2): + """ + Merge two lists while trying to keep the relative order of the elements. + Warn if the lists have the same two elements in a different relative + order. - def add_css(self, data): - if data: - for medium, paths in data.items(): - for path in paths: - if not self._css.get(medium) or path not in self._css[medium]: - self._css.setdefault(medium, []).append(path) + For static assets it can be important to have them included in the DOM + in a certain order. In JavaScript you may not be able to reference a + global or in CSS you might want to override a style. + """ + # Start with a copy of list_1. + combined_list = list(list_1) + last_insert_index = len(list_1) + # Walk list_2 in reverse, inserting each element into combined_list if + # it doesn't already exist. + for path in reversed(list_2): + try: + # Does path already exist in the list? + index = combined_list.index(path) + except ValueError: + # Add path to combined_list since it doesn't exist. + combined_list.insert(last_insert_index, path) + else: + if index > last_insert_index: + warnings.warn( + 'Detected duplicate Media files in an opposite order:\n' + '%s\n%s' % (combined_list[last_insert_index], combined_list[index]), + MediaOrderConflictWarning, + ) + # path already exists in the list. Update last_insert_index so + # that the following elements are inserted in front of this one. + last_insert_index = index + return combined_list def __add__(self, other): combined = Media() - for name in MEDIA_TYPES: - getattr(combined, 'add_' + name)(getattr(self, '_' + name, None)) - getattr(combined, 'add_' + name)(getattr(other, '_' + name, None)) + combined._js = self.merge(self._js, other._js) + combined._css = { + medium: self.merge(self._css.get(medium, []), other._css.get(medium, [])) + for medium in self._css.keys() | other._css.keys() + } return combined |
