summaryrefslogtreecommitdiff
path: root/django/forms
diff options
context:
space:
mode:
Diffstat (limited to 'django/forms')
-rw-r--r--django/forms/widgets.py77
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