Ticket #4418: newforms-admin-media.5651.diff
File newforms-admin-media.5651.diff, 43.1 KB (added by , 17 years ago) |
---|
-
django/contrib/admin/options.py
2 2 from django import newforms as forms 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 7 8 from django.db import models … … 48 49 def first_field(self): 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): 53 61 def __init__(self, name=None, fields=(), classes=(), description=None): … … 55 63 self.classes = u' '.join(classes) 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): 60 75 self.form, self.fieldset = form, fieldset … … 123 138 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 134 149 # For ForeignKey or ManyToManyFields, use a special widget. … … 148 163 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 = () 154 170 list_filter = () … … 159 175 save_as = False 160 176 save_on_top = False 161 177 ordering = None 162 js = None163 178 prepopulated_fields = {} 164 179 filter_vertical = () 165 180 filter_horizontal = () … … 194 209 else: 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. 212 def _media(self): 213 from django.conf import settings 200 214 201 The URLs can be absolute ('/js/admin/') or explicit202 ('http://example.com/foo.js').203 """204 from django.conf import settings205 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 """ 231 228 Generator that yields Fieldset objects for use on add and change admin … … 244 241 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): 256 251 "Returns True if the given request has permission to add an object." … … 433 428 inline_formset = FormSet() 434 429 inline_formsets.append(inline_formset) 435 430 431 adminForm = AdminForm(form, list(self.fieldsets_add(request)), self.prepopulated_fields) 432 media = self.media + adminForm.media 433 for fs in inline_formsets: 434 media = media + fs.media 435 436 436 c = template.RequestContext(request, { 437 437 'title': _('Add %s') % opts.verbose_name, 438 'adminform': AdminForm(form, self.fieldsets_add(request), self.prepopulated_fields),438 'adminform': adminForm, 439 439 'is_popup': request.REQUEST.has_key('_popup'), 440 440 'show_delete': False, 441 ' javascript_imports': self.javascript_add(request),441 'media': media, 442 442 'bound_inlines': [BoundInline(i, fs) for i, fs in zip(self.inlines, inline_formsets)], 443 443 }) 444 444 return render_change_form(self, model, model.AddManipulator(), c, add=True) … … 500 500 #related.get_accessor_name()) 501 501 #orig_list = func() 502 502 #oldform.order_objects.extend(orig_list) 503 504 adminForm = AdminForm(form, self.fieldsets_change(request, obj), self.prepopulated_fields) 505 media = self.media + adminForm.media 506 for fs in inline_formsets: 507 media = media + fs.media 508 503 509 c = template.RequestContext(request, { 504 510 'title': _('Change %s') % opts.verbose_name, 505 'adminform': AdminForm(form, self.fieldsets_change(request, obj), self.prepopulated_fields),511 'adminform': adminForm, 506 512 'object_id': object_id, 507 513 'original': obj, 508 514 'is_popup': request.REQUEST.has_key('_popup'), 509 ' javascript_imports': self.javascript_change(request, obj),515 'media': media, 510 516 'bound_inlines': [BoundInline(i, fs) for i, fs in zip(self.inlines, inline_formsets)], 511 517 }) 512 518 return render_change_form(self, model, model.ChangeManipulator(object_id), c, change=True) -
django/contrib/admin/widgets.py
5 5 from django import newforms as forms 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): 10 11 """ … … 28 29 (name, self.verbose_name.replace('"', '\\"'), int(self.is_stacked), settings.ADMIN_MEDIA_PREFIX)) 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 """ 33 50 A SplitDateTime Widget that has some admin-specific styling. 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. 40 56 forms.MultiWidget.__init__(self, widgets, attrs) -
django/contrib/admin/templates/admin/auth/user/change_password.html
2 2 {% load i18n admin_modify adminmedia %} 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 %} 8 7 {% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %} -
django/contrib/admin/templates/admin/change_form.html
3 3 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 10 9 {% block stylesheet %}{% admin_media_prefix %}css/forms.css{% endblock %} -
django/newforms/formsets.py
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 6 6 FORM_COUNT_FIELD_NAME = 'COUNT' … … 149 149 self.full_clean() 150 150 return self._is_valid 151 151 152 def _get_media(self): 153 # All the forms on a FormSet are the same, so you only need to 154 # interrogate the first form for media. 155 if self.forms: 156 return self.forms[0].media 157 else: 158 return Media() 159 media = property(_get_media) 160 152 161 def formset_for_form(form, formset=BaseFormSet, num_extra=1, orderable=False, deletable=False): 153 162 """Return a FormSet for the given form class.""" 154 163 attrs = {'form_class': form, 'num_extra': num_extra, 'orderable': orderable, 'deletable': deletable} -
django/newforms/forms.py
9 9 from django.utils.encoding import StrAndUnicode, smart_unicode, force_unicode 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 15 15 __all__ = ('BaseForm', 'Form') … … 37 37 """ 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): 42 43 fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)] … … 50 51 fields = base.base_fields.items() + fields 51 52 52 53 attrs['base_fields'] = SortedDictFromList(fields) 53 return type.__new__(cls, name, bases, attrs)54 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 59 55 60 class BaseForm(StrAndUnicode): 56 61 # This is the main implementation of all the Form logic. Note that this 57 62 # class is different than Form. See the comments by the Form class for more … … 234 239 self.is_bound = False 235 240 self.__errors = None 236 241 242 def _get_media(self): 243 """ 244 Provide a description of all media required to render the widgets on this form 245 """ 246 media = Media() 247 for field in self.fields.values(): 248 media = media + field.widget.media 249 return media 250 media = property(_get_media) 251 237 252 class Form(BaseForm): 238 253 "A collection of Fields, plus their associated data." 239 254 # This is a separate class from BaseForm in order to abstract the way -
django/newforms/widgets.py
8 8 from sets import Set as set # Python 2.3 fallback 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 13 14 from django.utils.translation import ugettext … … 15 16 from util import flatatt 16 17 17 18 __all__ = ( 18 ' Widget', 'TextInput', 'PasswordInput',19 'Media', 'Widget', 'TextInput', 'PasswordInput', 19 20 'HiddenInput', 'MultipleHiddenInput', 20 21 'FileInput', 'Textarea', 'CheckboxInput', 21 22 'Select', 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect', 22 23 'CheckboxSelectMultiple', 'MultiWidget', 'SplitDateTimeWidget', 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 28 128 def __init__(self, attrs=None): … … 377 477 """ 378 478 raise NotImplementedError('Subclasses must implement this method.') 379 479 480 def _get_media(self): 481 "Media for a multiwidget is the combination of all media of the subwidgets" 482 media = Media() 483 for w in self.widgets: 484 media = media + w.media 485 return media 486 media = property(_get_media) 487 380 488 class SplitDateTimeWidget(MultiWidget): 381 489 """ 382 490 A Widget that splits datetime input into two <input type="text"> boxes. … … 389 497 if value: 390 498 return [value.date(), value.time()] 391 499 return [None, None] 500 501 No newline at end of file -
tests/regressiontests/forms/media.py
1 # -*- coding: utf-8 -*- 2 # Tests for the media handling on widgets and forms 3 4 media_tests = r""" 5 >>> from django.newforms import TextInput, Media, TextInput, CharField, Form, MultiWidget 6 >>> from django.conf import settings 7 >>> settings.MEDIA_URL = 'http://media.example.com' 8 9 # Check construction of media objects 10 >>> m = Media(css={'all': ('/path/to/css1','/path/to/css2')}, js=('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3')) 11 >>> print m 12 <link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" /> 13 <link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" /> 14 <script type="text/javascript" src="http://media.example.com/path/to/js1"></script> 15 <script type="text/javascript" src="http://media.other.com/path/to/js2"></script> 16 <script type="text/javascript" src="https://secure.other.com/path/to/js3"></script> 17 18 >>> class Foo: 19 ... css = { 20 ... 'all': ('/path/to/css1','/path/to/css2') 21 ... } 22 ... js = ('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3') 23 >>> m3 = Media(Foo) 24 >>> print m3 25 <link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" /> 26 <link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" /> 27 <script type="text/javascript" src="http://media.example.com/path/to/js1"></script> 28 <script type="text/javascript" src="http://media.other.com/path/to/js2"></script> 29 <script type="text/javascript" src="https://secure.other.com/path/to/js3"></script> 30 31 >>> m3 = Media(Foo) 32 >>> print m3 33 <link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" /> 34 <link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" /> 35 <script type="text/javascript" src="http://media.example.com/path/to/js1"></script> 36 <script type="text/javascript" src="http://media.other.com/path/to/js2"></script> 37 <script type="text/javascript" src="https://secure.other.com/path/to/js3"></script> 38 39 # A widget can exist without a media definition 40 >>> class MyWidget(TextInput): 41 ... pass 42 43 >>> w = MyWidget() 44 >>> print w.media 45 <BLANKLINE> 46 47 ############################################################### 48 # DSL Class-based media definitions 49 ############################################################### 50 51 # A widget can define media if it needs to. 52 # Any absolute path will be preserved; relative paths are combined 53 # with the value of settings.MEDIA_URL 54 >>> class MyWidget1(TextInput): 55 ... class Media: 56 ... css = { 57 ... 'all': ('/path/to/css1','/path/to/css2') 58 ... } 59 ... js = ('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3') 60 61 >>> w1 = MyWidget1() 62 >>> print w1.media 63 <link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" /> 64 <link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" /> 65 <script type="text/javascript" src="http://media.example.com/path/to/js1"></script> 66 <script type="text/javascript" src="http://media.other.com/path/to/js2"></script> 67 <script type="text/javascript" src="https://secure.other.com/path/to/js3"></script> 68 69 # Media objects can be interrogated by media type 70 >>> print w1.media['css'] 71 <link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" /> 72 <link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" /> 73 74 >>> print w1.media['js'] 75 <script type="text/javascript" src="http://media.example.com/path/to/js1"></script> 76 <script type="text/javascript" src="http://media.other.com/path/to/js2"></script> 77 <script type="text/javascript" src="https://secure.other.com/path/to/js3"></script> 78 79 # Media objects can be combined. Any given media resource will appear only 80 # once. Duplicated media definitions are ignored. 81 >>> class MyWidget2(TextInput): 82 ... class Media: 83 ... css = { 84 ... 'all': ('/path/to/css2','/path/to/css3') 85 ... } 86 ... js = ('/path/to/js1','/path/to/js4') 87 88 >>> class MyWidget3(TextInput): 89 ... class Media: 90 ... css = { 91 ... 'all': ('/path/to/css3','/path/to/css1') 92 ... } 93 ... js = ('/path/to/js1','/path/to/js4') 94 95 >>> w2 = MyWidget2() 96 >>> w3 = MyWidget3() 97 >>> print w1.media + w2.media + w3.media 98 <link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" /> 99 <link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" /> 100 <link href="http://media.example.com/path/to/css3" type="text/css" media="all" rel="stylesheet" /> 101 <script type="text/javascript" src="http://media.example.com/path/to/js1"></script> 102 <script type="text/javascript" src="http://media.other.com/path/to/js2"></script> 103 <script type="text/javascript" src="https://secure.other.com/path/to/js3"></script> 104 <script type="text/javascript" src="http://media.example.com/path/to/js4"></script> 105 106 # Check that media addition hasn't affected the original objects 107 >>> print w1.media 108 <link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" /> 109 <link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" /> 110 <script type="text/javascript" src="http://media.example.com/path/to/js1"></script> 111 <script type="text/javascript" src="http://media.other.com/path/to/js2"></script> 112 <script type="text/javascript" src="https://secure.other.com/path/to/js3"></script> 113 114 ############################################################### 115 # Property-based media definitions 116 ############################################################### 117 118 # Widget media can be defined as a property 119 >>> class MyWidget4(TextInput): 120 ... def _media(self): 121 ... return Media(css={'all': ('/some/path',)}, js = ('/some/js',)) 122 ... media = property(_media) 123 124 >>> w4 = MyWidget4() 125 >>> print w4.media 126 <link href="http://media.example.com/some/path" type="text/css" media="all" rel="stylesheet" /> 127 <script type="text/javascript" src="http://media.example.com/some/js"></script> 128 129 # Media properties can reference the media of their parents 130 >>> class MyWidget5(MyWidget4): 131 ... def _media(self): 132 ... return super(MyWidget5, self).media + Media(css={'all': ('/other/path',)}, js = ('/other/js',)) 133 ... media = property(_media) 134 135 >>> w5 = MyWidget5() 136 >>> print w5.media 137 <link href="http://media.example.com/some/path" type="text/css" media="all" rel="stylesheet" /> 138 <link href="http://media.example.com/other/path" type="text/css" media="all" rel="stylesheet" /> 139 <script type="text/javascript" src="http://media.example.com/some/js"></script> 140 <script type="text/javascript" src="http://media.example.com/other/js"></script> 141 142 # Media properties can reference the media of their parents, 143 # even if the parent media was defined using a class 144 >>> class MyWidget6(MyWidget1): 145 ... def _media(self): 146 ... return super(MyWidget6, self).media + Media(css={'all': ('/other/path',)}, js = ('/other/js',)) 147 ... media = property(_media) 148 149 >>> w6 = MyWidget6() 150 >>> print w6.media 151 <link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" /> 152 <link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" /> 153 <link href="http://media.example.com/other/path" type="text/css" media="all" rel="stylesheet" /> 154 <script type="text/javascript" src="http://media.example.com/path/to/js1"></script> 155 <script type="text/javascript" src="http://media.other.com/path/to/js2"></script> 156 <script type="text/javascript" src="https://secure.other.com/path/to/js3"></script> 157 <script type="text/javascript" src="http://media.example.com/other/js"></script> 158 159 ############################################################### 160 # Inheritance of media 161 ############################################################### 162 163 # If a widget extends another but provides no media definition, it inherits the parent widget's media 164 >>> class MyWidget7(MyWidget1): 165 ... pass 166 167 >>> w7 = MyWidget7() 168 >>> print w7.media 169 <link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" /> 170 <link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" /> 171 <script type="text/javascript" src="http://media.example.com/path/to/js1"></script> 172 <script type="text/javascript" src="http://media.other.com/path/to/js2"></script> 173 <script type="text/javascript" src="https://secure.other.com/path/to/js3"></script> 174 175 # If a widget extends another but defines media, it extends the parent widget's media by default 176 >>> class MyWidget8(MyWidget1): 177 ... class Media: 178 ... css = { 179 ... 'all': ('/path/to/css3','/path/to/css1') 180 ... } 181 ... js = ('/path/to/js1','/path/to/js4') 182 183 >>> w8 = MyWidget8() 184 >>> print w8.media 185 <link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" /> 186 <link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" /> 187 <link href="http://media.example.com/path/to/css3" type="text/css" media="all" rel="stylesheet" /> 188 <script type="text/javascript" src="http://media.example.com/path/to/js1"></script> 189 <script type="text/javascript" src="http://media.other.com/path/to/js2"></script> 190 <script type="text/javascript" src="https://secure.other.com/path/to/js3"></script> 191 <script type="text/javascript" src="http://media.example.com/path/to/js4"></script> 192 193 # If a widget extends another but defines media, it extends the parents widget's media, 194 # even if the parent defined media using a property. 195 >>> class MyWidget9(MyWidget4): 196 ... class Media: 197 ... css = { 198 ... 'all': ('/other/path',) 199 ... } 200 ... js = ('/other/js',) 201 202 >>> w9 = MyWidget9() 203 >>> print w9.media 204 <link href="http://media.example.com/some/path" type="text/css" media="all" rel="stylesheet" /> 205 <link href="http://media.example.com/other/path" type="text/css" media="all" rel="stylesheet" /> 206 <script type="text/javascript" src="http://media.example.com/some/js"></script> 207 <script type="text/javascript" src="http://media.example.com/other/js"></script> 208 209 # A widget can disable media inheritance by specifying 'extend=False' 210 >>> class MyWidget10(MyWidget1): 211 ... class Media: 212 ... extend = False 213 ... css = { 214 ... 'all': ('/path/to/css3','/path/to/css1') 215 ... } 216 ... js = ('/path/to/js1','/path/to/js4') 217 218 >>> w10 = MyWidget10() 219 >>> print w10.media 220 <link href="http://media.example.com/path/to/css3" type="text/css" media="all" rel="stylesheet" /> 221 <link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" /> 222 <script type="text/javascript" src="http://media.example.com/path/to/js1"></script> 223 <script type="text/javascript" src="http://media.example.com/path/to/js4"></script> 224 225 # A widget can explicitly enable full media inheritance by specifying 'extend=True' 226 >>> class MyWidget11(MyWidget1): 227 ... class Media: 228 ... extend = True 229 ... css = { 230 ... 'all': ('/path/to/css3','/path/to/css1') 231 ... } 232 ... js = ('/path/to/js1','/path/to/js4') 233 234 >>> w11 = MyWidget11() 235 >>> print w11.media 236 <link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" /> 237 <link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" /> 238 <link href="http://media.example.com/path/to/css3" type="text/css" media="all" rel="stylesheet" /> 239 <script type="text/javascript" src="http://media.example.com/path/to/js1"></script> 240 <script type="text/javascript" src="http://media.other.com/path/to/js2"></script> 241 <script type="text/javascript" src="https://secure.other.com/path/to/js3"></script> 242 <script type="text/javascript" src="http://media.example.com/path/to/js4"></script> 243 244 # A widget can enable inheritance of one media type by specifying extend as a tuple 245 >>> class MyWidget12(MyWidget1): 246 ... class Media: 247 ... extend = ('css',) 248 ... css = { 249 ... 'all': ('/path/to/css3','/path/to/css1') 250 ... } 251 ... js = ('/path/to/js1','/path/to/js4') 252 253 >>> w12 = MyWidget12() 254 >>> print w12.media 255 <link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" /> 256 <link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" /> 257 <link href="http://media.example.com/path/to/css3" type="text/css" media="all" rel="stylesheet" /> 258 <script type="text/javascript" src="http://media.example.com/path/to/js1"></script> 259 <script type="text/javascript" src="http://media.example.com/path/to/js4"></script> 260 261 ############################################################### 262 # Multi-media handling for CSS 263 ############################################################### 264 265 # A widget can define CSS media for multiple output media types 266 >>> class MultimediaWidget(TextInput): 267 ... class Media: 268 ... css = { 269 ... 'screen, print': ('/file1','/file2'), 270 ... 'screen': ('/file3',), 271 ... 'print': ('/file4',) 272 ... } 273 ... js = ('/path/to/js1','/path/to/js4') 274 275 >>> multimedia = MultimediaWidget() 276 >>> print multimedia.media 277 <link href="http://media.example.com/file4" type="text/css" media="print" rel="stylesheet" /> 278 <link href="http://media.example.com/file3" type="text/css" media="screen" rel="stylesheet" /> 279 <link href="http://media.example.com/file1" type="text/css" media="screen, print" rel="stylesheet" /> 280 <link href="http://media.example.com/file2" type="text/css" media="screen, print" rel="stylesheet" /> 281 <script type="text/javascript" src="http://media.example.com/path/to/js1"></script> 282 <script type="text/javascript" src="http://media.example.com/path/to/js4"></script> 283 284 ############################################################### 285 # Multiwidget media handling 286 ############################################################### 287 288 # MultiWidgets have a default media definition that gets all the 289 # media from the component widgets 290 >>> class MyMultiWidget(MultiWidget): 291 ... def __init__(self, attrs=None): 292 ... widgets = [MyWidget1, MyWidget2, MyWidget3] 293 ... super(MyMultiWidget, self).__init__(widgets, attrs) 294 295 >>> mymulti = MyMultiWidget() 296 >>> print mymulti.media 297 <link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" /> 298 <link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" /> 299 <link href="http://media.example.com/path/to/css3" type="text/css" media="all" rel="stylesheet" /> 300 <script type="text/javascript" src="http://media.example.com/path/to/js1"></script> 301 <script type="text/javascript" src="http://media.other.com/path/to/js2"></script> 302 <script type="text/javascript" src="https://secure.other.com/path/to/js3"></script> 303 <script type="text/javascript" src="http://media.example.com/path/to/js4"></script> 304 305 ############################################################### 306 # Media processing for forms 307 ############################################################### 308 309 # You can ask a form for the media required by its widgets. 310 >>> class MyForm(Form): 311 ... field1 = CharField(max_length=20, widget=MyWidget1()) 312 ... field2 = CharField(max_length=20, widget=MyWidget2()) 313 >>> f1 = MyForm() 314 >>> print f1.media 315 <link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" /> 316 <link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" /> 317 <link href="http://media.example.com/path/to/css3" type="text/css" media="all" rel="stylesheet" /> 318 <script type="text/javascript" src="http://media.example.com/path/to/js1"></script> 319 <script type="text/javascript" src="http://media.other.com/path/to/js2"></script> 320 <script type="text/javascript" src="https://secure.other.com/path/to/js3"></script> 321 <script type="text/javascript" src="http://media.example.com/path/to/js4"></script> 322 323 # Form media can be combined to produce a single media definition. 324 >>> class AnotherForm(Form): 325 ... field3 = CharField(max_length=20, widget=MyWidget3()) 326 >>> f2 = AnotherForm() 327 >>> print f1.media + f2.media 328 <link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" /> 329 <link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" /> 330 <link href="http://media.example.com/path/to/css3" type="text/css" media="all" rel="stylesheet" /> 331 <script type="text/javascript" src="http://media.example.com/path/to/js1"></script> 332 <script type="text/javascript" src="http://media.other.com/path/to/js2"></script> 333 <script type="text/javascript" src="https://secure.other.com/path/to/js3"></script> 334 <script type="text/javascript" src="http://media.example.com/path/to/js4"></script> 335 336 # Forms can also define media, following the same rules as widgets. 337 >>> class FormWithMedia(Form): 338 ... field1 = CharField(max_length=20, widget=MyWidget1()) 339 ... field2 = CharField(max_length=20, widget=MyWidget2()) 340 ... class Media: 341 ... js = ('/some/form/javascript',) 342 ... css = { 343 ... 'all': ('/some/form/css',) 344 ... } 345 >>> f3 = FormWithMedia() 346 >>> print f3.media 347 <link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" /> 348 <link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" /> 349 <link href="http://media.example.com/path/to/css3" type="text/css" media="all" rel="stylesheet" /> 350 <link href="http://media.example.com/some/form/css" type="text/css" media="all" rel="stylesheet" /> 351 <script type="text/javascript" src="http://media.example.com/path/to/js1"></script> 352 <script type="text/javascript" src="http://media.other.com/path/to/js2"></script> 353 <script type="text/javascript" src="https://secure.other.com/path/to/js3"></script> 354 <script type="text/javascript" src="http://media.example.com/path/to/js4"></script> 355 <script type="text/javascript" src="http://media.example.com/some/form/javascript"></script> 356 357 """ 358 No newline at end of file -
tests/regressiontests/forms/tests.py
2 2 from localflavor import localflavor_tests 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""" 7 8 >>> from django.newforms import * … … 3698 3699 'localflavor': localflavor_tests, 3699 3700 'regressions': regression_tests, 3700 3701 'formset_tests': formset_tests, 3702 'media_tests': media_tests, 3701 3703 } 3702 3704 3703 3705 if __name__ == "__main__": -
docs/newforms.txt
919 919 ~~~~~~~~~~ 920 920 921 921 The ``widget`` argument lets you specify a ``Widget`` class to use when 922 rendering this ``Field``. See "Widgets" below for more information.922 rendering this ``Field``. See "Widgets"_ below for more information. 923 923 924 924 ``help_text`` 925 925 ~~~~~~~~~~~~~ … … 1325 1325 senders = MultiEmailField() 1326 1326 cc_myself = forms.BooleanField() 1327 1327 1328 Widgets 1329 ======= 1330 1331 A widget is Django's representation of a HTML form widget. The widget 1332 handles the rendering of the HTML, and the extraction of data from a GET/POST 1333 dictionary that corresponds to the widget. 1334 1335 Django provides a representation of all the basic HTML widgets, plus some 1336 commonly used groups of widgets: 1337 1338 ============================ =========================================== 1339 Widget HTML Equivalent 1340 ============================ =========================================== 1341 ``TextInput`` ``<input type='text' ...`` 1342 ``PasswordInput`` ``<input type='password' ...`` 1343 ``HiddenInput`` ``<input type='hidden' ...`` 1344 ``MultipleHiddenInput`` Multiple ``<input type='hidden' ...`` 1345 instances. 1346 ``FileInput`` ``<input type='file' ...`` 1347 ``Textarea`` ``<textarea>...</textarea>`` 1348 ``CheckboxInput`` ``<input type='checkbox' ...`` 1349 1350 ``Select`` ``<select><option ...`` 1351 1352 ``NullBooleanSelect`` Select widget with options 'Unknown', 1353 'Yes' and 'No' 1354 1355 ``SelectMultiple`` ``<select multiple='multiple'><option ...`` 1356 1357 ``RadioSelect`` ``<ul><li><input type='radio' ...`` 1358 1359 ``CheckboxSelectMultiple`` ``<ul><li><input type='checkbox' ...`` 1360 1361 ``MultiWidget`` Wrapper around multiple other widgets 1362 1363 ``SplitDateTimeWidget`` Wrapper around two ``TextInput`` widgets 1364 1365 Specifying widgets 1366 ------------------ 1367 1368 Whenever you specify a field on a form, Django will use a default widget 1369 that is appropriate to the type of data that is to be displayed. 1370 1371 However, if you want to use a different widget, you can - just use the 1372 'widget' argument on the field definition. For example:: 1373 1374 class MyForm(forms.Form): 1375 name = forms.CharField(max_length=20, widget=forms.HiddenInput) 1376 1377 This would specify a form with a CharField that uses a HiddenInput widget, 1378 rather than the default TextInput widget. 1379 1380 Customizing widget instances 1381 ---------------------------- 1382 - sometimes you may want to associate a CSS class with a widget, 1383 or modify the number of rows or columns displayed in a text area. 1384 1385 When you specify a widget, you can provide a list of attributes that 1386 will be used when rendering the widget. The attributes are 1387 Django also provides a mechanism to customize the rendering of 1388 1389 Django will then use these attributes 1390 1391 For example, if you want to associate a specific CSS class with the 1392 HTML generated by the widget, you can provide the CSS class as an 1393 attribute definition:: 1394 1395 class MyForm(forms.Form): 1396 name = forms.CharField( 1397 max_length=20, 1398 widget=forms.TextInput(attrs={'class':'special'}) 1399 1400 These attributes will be 1401 1402 Customized Widgets 1403 ------------------ 1404 1405 If you need to reuse a particular set of customized widget attributes, it 1406 may be worth defining your own widget that encapsulates those customized 1407 1408 Sometimes, a particular set of customized attributes will be s 1409 The widgets implemented in Django cover all the possibilities for HTML form 1410 input. While this set of widgets is entirely functional, it doesn't necessarily 1411 represent the best possible user interface for every application. 1412 1413 1414 1415 For example, consider the case of a DateField in a model. By default, 1416 a DateField on a form will be rendered using a TextInput widget. While this 1417 is entirely functional, a calendar is a much better user interface for 1418 inputting dates than a simple text field. 1419 1420 To get a calendar input on your page, you will need to use JavaScript 1421 1422 ..note: 1423 Django has deliberately not blessed any one JavaScript toolkit. 1424 There are many options out there, and they all have their relative 1425 strengths and weaknesses. 1426 1427 Whenever you specify 1428 1429 However, if you're going to be using 1430 1431 If you want to spice up your application, one approach you can take is 1432 to define some customized 1433 1434 user interface for a 1435 1436 One way to improve the user interface for DateFields would be to define 1437 a customized CalendarInput widget that displays a 1438 1439 is to define and use a customized widget 1440 1441 For the record - this is exactly what the Django Admin application does. 1442 1443 The Django Admin application defines a number of customized widgets, and 1444 uses those widgets in place of the Django defaults. 1445 1328 1446 Generating forms for models 1329 1447 =========================== 1330 1448 … … 1633 1751 you shouldn't use these shortcuts. Creating a ``Form`` class the "long" way 1634 1752 isn't that difficult, after all. 1635 1753 1754 Media 1755 ===== 1756 1757 In a 'previous section'_ 1758 provides a mechanism to simplify the definition and collation of the 1759 media requirements 1760 1761 .. 'previous section' 1762 However, the CalendarInput widget requires more than just HTML - it 1763 also requires CSS and JavaScript files to be fully functional. 1764 1765 1766 . you may wish to display a calendar for the input of 1767 dates, rather than a simple text fie 1768 1636 1769 More coming soon 1637 1770 ================ 1638 1771