Changeset 5926
- Timestamp:
- 08/18/07 00:42:56 (1 year ago)
- Files:
-
- django/branches/newforms-admin/django/contrib/admin/options.py (modified) (10 diffs)
- django/branches/newforms-admin/django/contrib/admin/templates/admin/auth/user/change_password.html (modified) (1 diff)
- django/branches/newforms-admin/django/contrib/admin/templates/admin/change_form.html (modified) (1 diff)
- django/branches/newforms-admin/django/contrib/admin/widgets.py (modified) (3 diffs)
- django/branches/newforms-admin/django/newforms/formsets.py (modified) (2 diffs)
- django/branches/newforms-admin/django/newforms/forms.py (modified) (4 diffs)
- django/branches/newforms-admin/django/newforms/widgets.py (modified) (5 diffs)
- django/branches/newforms-admin/docs/newforms.txt (modified) (2 diffs)
- django/branches/newforms-admin/tests/regressiontests/forms/media.py (added)
- django/branches/newforms-admin/tests/regressiontests/forms/tests.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/branches/newforms-admin/django/contrib/admin/options.py
r5833 r5926 3 3 from django.newforms.formsets import all_valid 4 4 from django.newforms.models import inline_formset 5 from django.newforms.widgets import Media, MediaDefiningClass 5 6 from django.contrib.admin import widgets 6 7 from django.core.exceptions import ImproperlyConfigured, PermissionDenied … … 49 50 for bf in self.form: 50 51 return bf 52 53 def _media(self): 54 media = self.form.media 55 for fs in self.fieldsets: 56 media = media + fs.media 57 return media 58 media = property(_media) 51 59 52 60 class Fieldset(object): … … 56 64 self.description = description 57 65 66 def _media(self): 67 from django.conf import settings 68 if 'collapse' in self.classes: 69 return Media(js=['%sjs/admin/CollapsedFieldsets.js' % settings.ADMIN_MEDIA_PREFIX]) 70 return Media() 71 media = property(_media) 72 58 73 class BoundFieldset(object): 59 74 def __init__(self, form, fieldset): … … 124 139 # For DateFields, add a custom CSS class. 125 140 if isinstance(db_field, models.DateField): 126 kwargs['widget'] = forms.TextInput(attrs={'class': 'vDateField', 'size': '10'})141 kwargs['widget'] = widgets.AdminDateWidget 127 142 return db_field.formfield(**kwargs) 128 143 129 144 # For TimeFields, add a custom CSS class. 130 145 if isinstance(db_field, models.TimeField): 131 kwargs['widget'] = forms.TextInput(attrs={'class': 'vTimeField', 'size': '8'})146 kwargs['widget'] = widgets.AdminTimeWidget 132 147 return db_field.formfield(**kwargs) 133 148 … … 149 164 class ModelAdmin(BaseModelAdmin): 150 165 "Encapsulates all admin options and functionality for a given model." 151 166 __metaclass__ = MediaDefiningClass 167 152 168 list_display = ('__str__',) 153 169 list_display_links = () … … 160 176 save_on_top = False 161 177 ordering = None 162 js = None163 178 prepopulated_fields = {} 164 179 filter_vertical = () … … 195 210 return self.change_view(request, unquote(url)) 196 211 197 def javascript(self, request, fieldsets): 198 """ 199 Returns a list of URLs to include via <script> statements. 200 201 The URLs can be absolute ('/js/admin/') or explicit 202 ('http://example.com/foo.js'). 203 """ 212 def _media(self): 204 213 from django.conf import settings 214 205 215 js = ['js/core.js', 'js/admin/RelatedObjectLookups.js'] 206 216 if self.prepopulated_fields: 207 217 js.append('js/urlify.js') 208 if self.opts.has_field_type(models.DateTimeField) or self.opts.has_field_type(models.TimeField) or self.opts.has_field_type(models.DateField):209 js.extend(['js/calendar.js', 'js/admin/DateTimeShortcuts.js'])210 218 if self.opts.get_ordered_objects(): 211 219 js.extend(['js/getElementsBySelector.js', 'js/dom-drag.js' , 'js/admin/ordering.js']) 212 if self.js:213 js.extend(self.js)214 220 if self.filter_vertical or self.filter_horizontal: 215 221 js.extend(['js/SelectBox.js' , 'js/SelectFilter2.js']) 216 for fs in fieldsets: 217 if 'collapse' in fs.classes: 218 js.append('js/admin/CollapsedFieldsets.js') 219 break 220 prefix = settings.ADMIN_MEDIA_PREFIX 221 return ['%s%s' % (prefix, url) for url in js] 222 223 def javascript_add(self, request): 224 return self.javascript(request, self.fieldsets_add(request)) 225 226 def javascript_change(self, request, obj): 227 return self.javascript(request, self.fieldsets_change(request, obj)) 228 222 223 return Media(js=['%s%s' % (settings.ADMIN_MEDIA_PREFIX, url) for url in js]) 224 media = property(_media) 225 229 226 def fieldsets(self, request): 230 227 """ … … 245 242 def fieldsets_add(self, request): 246 243 "Hook for specifying Fieldsets for the add form." 247 for fs in self.fieldsets(request): 248 yield fs 244 return list(self.fieldsets(request)) 249 245 250 246 def fieldsets_change(self, request, obj): 251 247 "Hook for specifying Fieldsets for the change form." 252 for fs in self.fieldsets(request): 253 yield fs 248 return list(self.fieldsets(request)) 254 249 255 250 def has_add_permission(self, request): … … 431 426 inline_formsets.append(inline_formset) 432 427 428 adminForm = AdminForm(form, list(self.fieldsets_add(request)), self.prepopulated_fields) 429 media = self.media + adminForm.media 430 for fs in inline_formsets: 431 media = media + fs.media 432 433 433 c = template.RequestContext(request, { 434 434 'title': _('Add %s') % opts.verbose_name, 435 'adminform': AdminForm(form, self.fieldsets_add(request), self.prepopulated_fields),435 'adminform': adminForm, 436 436 'is_popup': request.REQUEST.has_key('_popup'), 437 437 'show_delete': False, 438 ' javascript_imports': self.javascript_add(request),438 'media': media, 439 439 'bound_inlines': [BoundInline(i, fs) for i, fs in zip(self.inlines, inline_formsets)], 440 440 }) … … 495 495 #orig_list = func() 496 496 #oldform.order_objects.extend(orig_list) 497 498 adminForm = AdminForm(form, self.fieldsets_change(request, obj), self.prepopulated_fields) 499 media = self.media + adminForm.media 500 for fs in inline_formsets: 501 media = media + fs.media 502 497 503 c = template.RequestContext(request, { 498 504 'title': _('Change %s') % opts.verbose_name, 499 'adminform': AdminForm(form, self.fieldsets_change(request, obj), self.prepopulated_fields),505 'adminform': adminForm, 500 506 'object_id': object_id, 501 507 'original': obj, 502 508 'is_popup': request.REQUEST.has_key('_popup'), 503 ' javascript_imports': self.javascript_change(request, obj),509 'media': media, 504 510 'bound_inlines': [BoundInline(i, fs) for i, fs in zip(self.inlines, inline_formsets)], 505 511 }) django/branches/newforms-admin/django/contrib/admin/templates/admin/auth/user/change_password.html
r5918 r5926 3 3 {% block extrahead %}{{ block.super }} 4 4 <script type="text/javascript" src="../../../../jsi18n/"></script> 5 {% for js in javascript_imports %}{% include_admin_script js %}{% endfor %}6 5 {% endblock %} 7 6 {% block stylesheet %}{% admin_media_prefix %}css/forms.css{% endblock %} django/branches/newforms-admin/django/contrib/admin/templates/admin/change_form.html
r5473 r5926 4 4 {% block extrahead %}{{ block.super }} 5 5 <script type="text/javascript" src="../../../jsi18n/"></script> 6 {% for js in javascript_imports %}<script type="text/javascript" src="{{ js }}"></script> 7 {% endfor %} 6 {{ media }} 8 7 {% endblock %} 9 8 django/branches/newforms-admin/django/contrib/admin/widgets.py
r5627 r5926 6 6 from django.utils.text import capfirst 7 7 from django.utils.translation import ugettext as _ 8 from django.conf import settings 8 9 9 10 class FilteredSelectMultiple(forms.SelectMultiple): … … 29 30 return u''.join(output) 30 31 32 class AdminDateWidget(forms.TextInput): 33 class Media: 34 js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js", 35 settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js") 36 37 def __init__(self, attrs={}): 38 super(AdminDateWidget, self).__init__(attrs={'class': 'vDateField', 'size': '10'}) 39 40 class AdminTimeWidget(forms.TextInput): 41 class Media: 42 js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js", 43 settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js") 44 45 def __init__(self, attrs={}): 46 super(AdminTimeWidget, self).__init__(attrs={'class': 'vTimeField', 'size': '8'}) 47 31 48 class AdminSplitDateTime(forms.SplitDateTimeWidget): 32 49 """ … … 34 51 """ 35 52 def __init__(self, attrs=None): 36 widgets = [forms.TextInput(attrs={'class': 'vDateField', 'size': '10'}), 37 forms.TextInput(attrs={'class': 'vTimeField', 'size': '8'})] 53 widgets = [AdminDateWidget, AdminTimeWidget] 38 54 # Note that we're calling MultiWidget, not SplitDateTimeWidget, because 39 55 # we want to define widgets. django/branches/newforms-admin/django/newforms/formsets.py
r5833 r5926 1 1 from forms import Form, ValidationError 2 2 from fields import IntegerField, BooleanField 3 from widgets import HiddenInput 3 from widgets import HiddenInput, Media 4 4 5 5 # special field names … … 155 155 return self._is_valid 156 156 157 def _get_media(self): 158 # All the forms on a FormSet are the same, so you only need to 159 # interrogate the first form for media. 160 if self.forms: 161 return self.forms[0].media 162 else: 163 return Media() 164 media = property(_get_media) 165 157 166 def formset_for_form(form, formset=BaseFormSet, num_extra=1, orderable=False, deletable=False): 158 167 """Return a FormSet for the given form class.""" django/branches/newforms-admin/django/newforms/forms.py
r5828 r5926 10 10 11 11 from fields import Field 12 from widgets import TextInput, Textarea12 from widgets import Media, media_property, TextInput, Textarea 13 13 from util import flatatt, ErrorDict, ErrorList, ValidationError 14 14 … … 38 38 Metaclass that converts Field attributes to a dictionary called 39 39 'base_fields', taking into account parent class 'base_fields' as well. 40 Also integrates any additional media definitions 40 41 """ 41 42 def __new__(cls, name, bases, attrs): … … 51 52 52 53 attrs['base_fields'] = SortedDictFromList(fields) 53 return type.__new__(cls, name, bases, attrs) 54 55 new_class = type.__new__(cls, name, bases, attrs) 56 if 'media' not in attrs: 57 new_class.media = media_property(new_class) 58 return new_class 54 59 55 60 class BaseForm(StrAndUnicode): … … 236 241 self.__errors = None 237 242 243 def _get_media(self): 244 """ 245 Provide a description of all media required to render the widgets on this form 246 """ 247 media = Media() 248 for field in self.fields.values(): 249 media = media + field.widget.media 250 return media 251 media = property(_get_media) 252 238 253 class Form(BaseForm): 239 254 "A collection of Fields, plus their associated data." django/branches/newforms-admin/django/newforms/widgets.py
r5828 r5926 9 9 10 10 from itertools import chain 11 from django.conf import settings 11 12 from django.utils.datastructures import MultiValueDict 12 13 from django.utils.html import escape … … 16 17 17 18 __all__ = ( 18 ' Widget', 'TextInput', 'PasswordInput',19 'Media', 'Widget', 'TextInput', 'PasswordInput', 19 20 'HiddenInput', 'MultipleHiddenInput', 20 21 'FileInput', 'Textarea', 'CheckboxInput', … … 23 24 ) 24 25 26 MEDIA_TYPES = ('css','js') 27 28 class Media(StrAndUnicode): 29 def __init__(self, media=None, **kwargs): 30 if media: 31 media_attrs = media.__dict__ 32 else: 33 media_attrs = kwargs 34 35 self._css = {} 36 self._js = [] 37 38 for name in MEDIA_TYPES: 39 getattr(self, 'add_' + name)(media_attrs.get(name, None)) 40 41 # Any leftover attributes must be invalid. 42 # if media_attrs != {}: 43 # raise TypeError, "'class Media' has invalid attribute(s): %s" % ','.join(media_attrs.keys()) 44 45 def __unicode__(self): 46 return self.render() 47 48 def render(self): 49 return u'\n'.join(chain(*[getattr(self, 'render_' + name)() for name in MEDIA_TYPES])) 50 51 def render_js(self): 52 return [u'<script type="text/javascript" src="%s"></script>' % self.absolute_path(path) for path in self._js] 53 54 def render_css(self): 55 # To keep rendering order consistent, we can't just iterate over items(). 56 # We need to sort the keys, and iterate over the sorted list. 57 media = self._css.keys() 58 media.sort() 59 return chain(*[ 60 [u'<link href="%s" type="text/css" media="%s" rel="stylesheet" />' % (self.absolute_path(path), medium) 61 for path in self._css[medium]] 62 for medium in media]) 63 64 def absolute_path(self, path): 65 return (path.startswith(u'http://') or path.startswith(u'https://')) and path or u''.join([settings.MEDIA_URL,path]) 66 67 def __getitem__(self, name): 68 "Returns a Media object that only contains media of the given type" 69 if name in MEDIA_TYPES: 70 return Media(**{name: getattr(self, '_' + name)}) 71 raise KeyError('Unknown media type "%s"' % name) 72 73 def add_js(self, data): 74 if data: 75 self._js.extend([path for path in data if path not in self._js]) 76 77 def add_css(self, data): 78 if data: 79 for medium, paths in data.items(): 80 self._css.setdefault(medium, []).extend([path for path in paths if path not in self._css[medium]]) 81 82 def __add__(self, other): 83 combined = Media() 84 for name in MEDIA_TYPES: 85 getattr(combined, 'add_' + name)(getattr(self, '_' + name, None)) 86 getattr(combined, 'add_' + name)(getattr(other, '_' + name, None)) 87 return combined 88 89 def media_property(cls): 90 def _media(self): 91 # Get the media property of the superclass, if it exists 92 if hasattr(super(cls, self), 'media'): 93 base = super(cls, self).media 94 else: 95 base = Media() 96 97 # Get the media definition for this class 98 definition = getattr(cls, 'Media', None) 99 if definition: 100 extend = getattr(definition, 'extend', True) 101 if extend: 102 if extend == True: 103 m = base 104 else: 105 m = Media() 106 for medium in extend: 107 m = m + base[medium] 108 m = m + Media(definition) 109 return m + Media(definition) 110 else: 111 return Media(definition) 112 else: 113 return base 114 return property(_media) 115 116 class MediaDefiningClass(type): 117 "Metaclass for classes that can have media definitions" 118 def __new__(cls, name, bases, attrs): 119 new_class = type.__new__(cls, name, bases, attrs) 120 if 'media' not in attrs: 121 new_class.media = media_property(new_class) 122 return new_class 123 25 124 class Widget(object): 125 __metaclass__ = MediaDefiningClass 26 126 is_hidden = False # Determines whether this corresponds to an <input type="hidden">. 27 127 … … 406 506 raise NotImplementedError('Subclasses must implement this method.') 407 507 508 def _get_media(self): 509 "Media for a multiwidget is the combination of all media of the subwidgets" 510 media = Media() 511 for w in self.widgets: 512 media = media + w.media 513 return media 514 media = property(_get_media) 515 408 516 class SplitDateTimeWidget(MultiWidget): 409 517 """ … … 418 526 return [value.date(), value.time()] 419 527 return [None, None] 528 django/branches/newforms-admin/docs/newforms.txt
r5918 r5926 77 77 display itself as HTML. 78 78 79 * **Media** -- A definition of the CSS and JavaScript resources that are 80 required to render a form. 81 79 82 The library is decoupled from the other Django components, such as the database 80 83 layer, views and templates. It relies only on Django settings, a couple of … … 1941 1944 isn't that difficult, after all. 1942 1945 1946 Media 1947 ===== 1948 1949 Rendering an attractive and easy-to-use web form requires more than just 1950 HTML - it also requires CSS stylesheets, and if you want to use fancy 1951 "Web2.0" widgets, you may also need to include some JavaScript on each 1952 page. The exact combination of CSS and JavaScript that is required for 1953 any given page will depend upon the widgets that are in use on that page. 1954 1955 This is where Django media definitions come in. Django allows you to 1956 associate different media files with the forms and widgets that require 1957 that media. For example, if you want to use a calendar to render DateFields, 1958 you can define a custom Calendar widget. This widget can then be associated 1959 with the CSS and Javascript that is required to render the calendar. When 1960 the Calendar widget is used on a form, Django is able to identify the CSS and 1961 JavaScript files that are required, and provide the list of file names 1962 in a form suitable for easy inclusion on your web page. 1963 1964 .. admonition:: Media and Django Admin 1965 1966 The Django Admin application defines a number of customized widgets 1967 for calendars, filtered selections, and so on. These widgets define 1968 media requirements, and the Django Admin uses the custom widgets 1969 in place of the Django defaults. The Admin templates will only include 1970 those media files that are required to render the widgets on any 1971 given page. 1972 1973 If you like the widgets that the Django Admin application uses, 1974 feel free to use them in your own application! They're all stored 1975 in ``django.contrib.admin.widgets``. 1976 1977 .. admonition:: Which JavaScript toolkit? 1978 1979 Many JavaScript toolkits exist, and many of them include widgets (such 1980 as calendar widgets) that can be used to enhance your application. 1981 Django has deliberately avoided blessing any one JavaScript toolkit. 1982 Each toolkit has its own relative strengths and weaknesses - use 1983 whichever toolkit suits your requirements. Django is able to integrate 1984 with any JavaScript toolkit. 1985 1986 Media as a static definition 1987 ---------------------------- 1988 1989 The easiest way to define media is as a static definition. Using this method, 1990 the media declaration is an inner class. The properties of the inner class 1991 define the media requirements. 1992 1993 Here's a simple example:: 1994 1995 class CalendarWidget(forms.TextInput): 1996 class Media: 1997 css = { 1998 'all': ('pretty.css',) 1999 } 2000 js = ('animations.js', 'actions.js') 2001 2002 This code defines a ``CalendarWidget``, which will be based on ``TextInput``. 2003 Every time the CalendarWidget is used on a form, that form will be directed 2004 to include the CSS file ``pretty.css``, and the JavaScript files 2005 ``animations.js`` and ``actions.js``. 2006 2007 This static media definition is converted at runtime into a widget property 2008 named ``media``. The media for a CalendarWidget instance can be retrieved 2009 through this property:: 2010 2011 >>> w = CalendarWidget() 2012 >>> print w.media 2013 <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> 2014 <script type="text/javascript" src="http://media.example.com/animations.js"></script> 2015 <script type="text/javascript" src="http://media.example.com/actions.js"></script> 2016 2017 Here's a list of all possible ``Media`` options. There are no required options. 2018 2019 ``css`` 2020 ~~~~~~~ 2021 2022 A dictionary describing the CSS files required for various forms of output 2023 media. 2024 2025 The values in the dictionary should be a tuple/list of file names. See 2026 `the section on media paths`_ for details of how to specify paths to media 2027 files. 2028 2029 .. _the section on media paths: `Paths in media definitions`_ 2030 2031 The keys in the dictionary are the output media types. These are the same 2032 types accepted by CSS files in media declarations: 'all', 'aural', 'braille', 2033 'embossed', 'handheld', 'print', 'projection', 'screen', 'tty' and 'tv'. If 2034 you need to have different stylesheets for different media types, provide 2035 a list of CSS files for each output medium. The following example would 2036 provide two CSS options -- one for the screen, and one for print:: 2037 2038 class Media: 2039 css = { 2040 'screen': ('pretty.css',), 2041 'print': ('newspaper.css',) 2042 } 2043 2044 If a group of CSS files are appropriate for multiple output media types, 2045 the dictionary key can be a comma separated list of output media types. 2046 In the following example, TV's and projectors will have the same media 2047 requirements:: 2048 2049 class Media: 2050 css = { 2051 'screen': ('pretty.css',), 2052 'tv,projector': ('lo_res.css',), 2053 'print': ('newspaper.css',) 2054 } 2055 2056 If this last CSS definition were to be rendered, it would become the following HTML:: 2057 2058 <link href="http://media.example.com/pretty.css" type="text/css" media="screen" rel="stylesheet" /> 2059 <link href="http://media.example.com/lo_res.css" type="text/css" media="tv,projector" rel="stylesheet" /> 2060 <link href="http://media.example.com/newspaper.css" type="text/css" media="print" rel="stylesheet" /> 2061 2062 ``js`` 2063 ~~~~~~ 2064 2065 A tuple describing the required javascript files. See 2066 `the section on media paths`_ for details of how to specify paths to media 2067 files. 2068 2069 ``extend`` 2070 ~~~~~~~~~~ 2071 2072 A boolean defining inheritance behavior for media declarations. 2073 2074 By default, any object using a static media definition will inherit all the 2075 media associated with the parent widget. This occurs regardless of how the 2076 parent defines its media requirements. For example, if we were to extend our 2077 basic Calendar widget from the example above:: 2078 2079 class FancyCalendarWidget(CalendarWidget): 2080 class Media: 2081 css = { 2082 'all': ('fancy.css',) 2083 } 2084 js = ('whizbang.js',) 2085 2086 >>> w = FancyCalendarWidget() 2087 >>> print w.media 2088 <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> 2089 <link href="http://media.example.com/fancy.css" type="text/css" media="all" rel="stylesheet" /> 2090 <script type="text/javascript" src="http://media.example.com/animations.js"></script> 2091 <script type="text/javascript" src="http://media.example.com/actions.js"></script> 2092 <script type="text/javascript" src="http://media.example.com/whizbang.js"></script> 2093 2094 The FancyCalendar widget inherits all the media from it's parent widget. If 2095 you don't want media to be inherited in this way, add an ``extend=False`` 2096 declaration to the media declaration:: 2097 2098 class FancyCalendar(Calendar): 2099 class Media: 2100 extend = False 2101 css = { 2102 'all': ('fancy.css',) 2103 } 2104 js = ('whizbang.js',) 2105 2106 >>> w = FancyCalendarWidget() 2107 >>> print w.media 2108 <link href="http://media.example.com/fancy.css" type="text/css" media="all" rel="stylesheet" /> 2109 <script type="text/javascript" src="http://media.example.com/whizbang.js"></script> 2110 2111 If you require even more control over media inheritance, define your media 2112 using a `dynamic property`_. Dynamic properties give you complete control over 2113 which media files are inherited, and which are not. 2114 2115 .. _dynamic property: `Media as a dynamic property`_ 2116 2117 Media as a dynamic property 2118 --------------------------- 2119 2120 If you need to perform some more sophisticated manipulation of media 2121 requirements, you can define the media property directly. This is done 2122 by defining a model property that returns an instance of ``forms.Media``. 2123 The constructor for ``forms.Media`` accepts ``css`` and ``js`` keyword 2124 arguments in the same format as that used in a static media definition. 2125 2126 For example, the static media definition for our Calendar Widget could 2127 also be defined in a dynamic fashion:: 2128 2129 class CalendarWidget(forms.TextInput): 2130 def _media(self): 2131 return forms.Media(css={'all': ('pretty.css',)}, 2132 js=('animations.js', 'actions.js')) 2133 media = property(_media) 2134 2135 See the section on `Media objects`_ for more details on how to construct 2136 return values for dynamic media properties. 2137 2138 Paths in media definitions 2139 -------------------------- 2140 2141 Paths used to specify media can be either relative or absolute. If a path 2142 starts with 'http://' or 'https://', it will be interpreted as an absolute 2143 path, and left as-is. All other paths will be prepended with the value of 2144 ``settings.MEDIA_URL``. For example, if the MEDIA_URL for your site was 2145 ``http://media.example.com/``:: 2146 2147 class CalendarWidget(forms.TextInput): 2148 class Media: 2149 js = ('animations.js', 'http://othersite.com/actions.js') 2150 2151 >>> w = CalendarWidget() 2152 >>> print w.media 2153 <script type="text/javascript" src="http://media.example.com/animations.js"></script> 2154 <script type="text/javascript" src="http://othersite.com/actions.js"></script> 2155 2156 Media objects 2157 ------------- 2158 2159 When you interrogate the media attribute of a widget or form, the value that 2160 is returned is a ``forms.Media`` object. As we have already seen, the string 2161 representation of a Media object is the HTML required to include media 2162 in the ``<head>`` block of your HTML page. 2163 2164 However, Media objects have some other interesting properties. 2165 2166 Media subsets 2167 ~~~~~~~~~~~~~ 2168 2169 If you only want media of a particular type, you can use the subscript operator 2170 to filter out a medium of interest. For example:: 2171 2172 >>> w = CalendarWidget() 2173 >>> print w.media 2174 <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> 2175 <script type="text/javascript" src="http://media.example.com/animations.js"></script> 2176 <script type="text/javascript" src="http://media.example.com/actions.js"></script> 2177 2178 >>> print w.media['css'] 2179 <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> 2180 2181 When you use the subscript operator, the value that is returned is a new 2182 Media object -- but one that only contains the media of interest. 2183 2184 Combining media objects 2185 ~~~~~~~~~~~~~~~~~~~~~~~ 2186 2187 Media objects can also be added together. When two media objects are added, 2188 the resulting Media object contains the union of the media from both files:: 2189 2190 class CalendarWidget(forms.TextInput): 2191 class Media: 2192 css = { 2193 'all': ('pretty.css',) 2194 } 2195 js = ('animations.js', 'actions.js') 2196 2197 class OtherWidget(forms.TextInput): 2198 class Media: 2199 js = ('whizbang.js',) 2200 2201 >>> w1 = CalendarWidget() 2202 >>> w2 = OtherWidget() 2203 >>> print w1+w2 2204 <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> 2205 <script type="text/javascript" src="http://media.example.com/animations.js"></script> 2206 <script type="text/javascript" src="http://media.example.com/actions.js"></script> 2207 <script type="text/javascript" src="http://media.example.com/whizbang.js"></script> 2208 2209 Media on Forms 2210 -------------- 2211 2212 Widgets aren't the only objects that can have media definitions -- forms 2213 can also define media. The rules for media definitions on forms are the 2214 same as the rules for widgets: declarations can be static or dynamic; 2215 path and inheritance rules for those declarations are exactly the same. 2216 2217 Regardless of whether you define a media declaration, *all* Form objects 2218 have a media property. The default value for this property is the result 2219 of adding the media definitions for all widgets that are part of the form:: 2220 2221 class ContactForm(forms.Form): 2222 date = DateField(widget=CalendarWidget) 2223 name = CharField(max_length=40, widget=OtherWidget) 2224 2225 >>> f = ContactForm() 2226 >>> f.media 2227 <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> 2228 <script type="text/javascript" src="http://media.example.com/animations.js"></script> 2229 <script type="text/javascript" src="http://media.example.com/actions.js"></script> 2230 <script type="text/javascript" src="http://media.example.com/whizbang.js"></script> 2231 2232 If you want to associate additional media with a form -- for example, CSS for form 2233 layout -- simply add a media declaration to the form:: 2234 2235 class ContactForm(forms.Form): 2236 date = DateField(widget=CalendarWidget) 2237 name = CharField(max_length=40, widget=OtherWidget) 2238 2239 class Media: 2240 css = { 2241 'all': ('layout.css',) 2242 } 2243 2244 >>> f = ContactForm() 2245 >>> f.media 2246 <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> 2247 <link href="http://media.example.com/layout.css" type="text/css" media="all" rel="stylesheet" /> 2248 <script type="text/javascript" src="http://media.example.com/animations.js"></script> 2249 <script type="text/javascript" src="http://media.example.com/actions.js"></script> 2250 <script type="text/javascript" src="http://media.example.com/whizbang.js"></script> 2251 1943 2252 More coming soon 1944 2253 ================ django/branches/newforms-admin/tests/regressiontests/forms/tests.py
r5918 r5926 3 3 from regressions import regression_tests 4 4 from formsets import formset_tests 5 from media import media_tests 5 6 6 7 form_tests = r""" … … 3805 3806 'regressions': regression_tests, 3806 3807 'formset_tests': formset_tests, 3808 'media_tests': media_tests, 3807 3809 } 3808 3810
