Django

Code

Changeset 5926

Show
Ignore:
Timestamp:
08/18/07 00:42:56 (1 year ago)
Author:
russellm
Message:

newforms-admin: Fixed #4418 -- Added the ability for widgets, forms, and Admin declarations to have media definitions.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/newforms-admin/django/contrib/admin/options.py

    r5833 r5926  
    33from django.newforms.formsets import all_valid 
    44from django.newforms.models import inline_formset 
     5from django.newforms.widgets import Media, MediaDefiningClass 
    56from django.contrib.admin import widgets 
    67from django.core.exceptions import ImproperlyConfigured, PermissionDenied 
     
    4950        for bf in self.form: 
    5051            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) 
    5159 
    5260class Fieldset(object): 
     
    5664        self.description = description 
    5765 
     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     
    5873class BoundFieldset(object): 
    5974    def __init__(self, form, fieldset): 
     
    124139        # For DateFields, add a custom CSS class. 
    125140        if isinstance(db_field, models.DateField): 
    126             kwargs['widget'] = forms.TextInput(attrs={'class': 'vDateField', 'size': '10'}) 
     141            kwargs['widget'] = widgets.AdminDateWidget 
    127142            return db_field.formfield(**kwargs) 
    128143 
    129144        # For TimeFields, add a custom CSS class. 
    130145        if isinstance(db_field, models.TimeField): 
    131             kwargs['widget'] = forms.TextInput(attrs={'class': 'vTimeField', 'size': '8'}) 
     146            kwargs['widget'] = widgets.AdminTimeWidget 
    132147            return db_field.formfield(**kwargs) 
    133148 
     
    149164class ModelAdmin(BaseModelAdmin): 
    150165    "Encapsulates all admin options and functionality for a given model." 
    151  
     166    __metaclass__ = MediaDefiningClass 
     167     
    152168    list_display = ('__str__',) 
    153169    list_display_links = () 
     
    160176    save_on_top = False 
    161177    ordering = None 
    162     js = None 
    163178    prepopulated_fields = {} 
    164179    filter_vertical = () 
     
    195210            return self.change_view(request, unquote(url)) 
    196211 
    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): 
    204213        from django.conf import settings 
     214 
    205215        js = ['js/core.js', 'js/admin/RelatedObjectLookups.js'] 
    206216        if self.prepopulated_fields: 
    207217            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']) 
    210218        if self.opts.get_ordered_objects(): 
    211219            js.extend(['js/getElementsBySelector.js', 'js/dom-drag.js' , 'js/admin/ordering.js']) 
    212         if self.js: 
    213             js.extend(self.js) 
    214220        if self.filter_vertical or self.filter_horizontal: 
    215221            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     
    229226    def fieldsets(self, request): 
    230227        """ 
     
    245242    def fieldsets_add(self, request): 
    246243        "Hook for specifying Fieldsets for the add form." 
    247         for fs in self.fieldsets(request): 
    248             yield fs 
     244        return list(self.fieldsets(request)) 
    249245 
    250246    def fieldsets_change(self, request, obj): 
    251247        "Hook for specifying Fieldsets for the change form." 
    252         for fs in self.fieldsets(request): 
    253             yield fs 
     248        return list(self.fieldsets(request)) 
    254249 
    255250    def has_add_permission(self, request): 
     
    431426                inline_formsets.append(inline_formset) 
    432427 
     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             
    433433        c = template.RequestContext(request, { 
    434434            'title': _('Add %s') % opts.verbose_name, 
    435             'adminform': AdminForm(form, self.fieldsets_add(request), self.prepopulated_fields)
     435            'adminform': adminForm
    436436            'is_popup': request.REQUEST.has_key('_popup'), 
    437437            'show_delete': False, 
    438             'javascript_imports': self.javascript_add(request)
     438            'media': media
    439439            'bound_inlines': [BoundInline(i, fs) for i, fs in zip(self.inlines, inline_formsets)], 
    440440        }) 
     
    495495                #orig_list = func() 
    496496                #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             
    497503        c = template.RequestContext(request, { 
    498504            'title': _('Change %s') % opts.verbose_name, 
    499             'adminform': AdminForm(form, self.fieldsets_change(request, obj), self.prepopulated_fields)
     505            'adminform': adminForm
    500506            'object_id': object_id, 
    501507            'original': obj, 
    502508            'is_popup': request.REQUEST.has_key('_popup'), 
    503             'javascript_imports': self.javascript_change(request, obj)
     509            'media': media
    504510            'bound_inlines': [BoundInline(i, fs) for i, fs in zip(self.inlines, inline_formsets)], 
    505511        }) 
  • django/branches/newforms-admin/django/contrib/admin/templates/admin/auth/user/change_password.html

    r5918 r5926  
    33{% block extrahead %}{{ block.super }} 
    44<script type="text/javascript" src="../../../../jsi18n/"></script> 
    5 {% for js in javascript_imports %}{% include_admin_script js %}{% endfor %} 
    65{% endblock %} 
    76{% block stylesheet %}{% admin_media_prefix %}css/forms.css{% endblock %} 
  • django/branches/newforms-admin/django/contrib/admin/templates/admin/change_form.html

    r5473 r5926  
    44{% block extrahead %}{{ block.super }} 
    55<script type="text/javascript" src="../../../jsi18n/"></script> 
    6 {% for js in javascript_imports %}<script type="text/javascript" src="{{ js }}"></script> 
    7 {% endfor %} 
     6{{ media }} 
    87{% endblock %} 
    98 
  • django/branches/newforms-admin/django/contrib/admin/widgets.py

    r5627 r5926  
    66from django.utils.text import capfirst 
    77from django.utils.translation import ugettext as _ 
     8from django.conf import settings 
    89 
    910class FilteredSelectMultiple(forms.SelectMultiple): 
     
    2930        return u''.join(output) 
    3031 
     32class 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 
     40class 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     
    3148class AdminSplitDateTime(forms.SplitDateTimeWidget): 
    3249    """ 
     
    3451    """ 
    3552    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] 
    3854        # Note that we're calling MultiWidget, not SplitDateTimeWidget, because 
    3955        # we want to define widgets. 
  • django/branches/newforms-admin/django/newforms/formsets.py

    r5833 r5926  
    11from forms import Form, ValidationError 
    22from fields import IntegerField, BooleanField 
    3 from widgets import HiddenInput 
     3from widgets import HiddenInput, Media 
    44 
    55# special field names 
     
    155155        return self._is_valid 
    156156 
     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     
    157166def formset_for_form(form, formset=BaseFormSet, num_extra=1, orderable=False, deletable=False): 
    158167    """Return a FormSet for the given form class.""" 
  • django/branches/newforms-admin/django/newforms/forms.py

    r5828 r5926  
    1010 
    1111from fields import Field 
    12 from widgets import TextInput, Textarea 
     12from widgets import Media, media_property, TextInput, Textarea 
    1313from util import flatatt, ErrorDict, ErrorList, ValidationError 
    1414 
     
    3838    Metaclass that converts Field attributes to a dictionary called 
    3939    'base_fields', taking into account parent class 'base_fields' as well. 
     40    Also integrates any additional media definitions 
    4041    """ 
    4142    def __new__(cls, name, bases, attrs): 
     
    5152 
    5253        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 
    5459 
    5560class BaseForm(StrAndUnicode): 
     
    236241        self.__errors = None 
    237242 
     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     
    238253class Form(BaseForm): 
    239254    "A collection of Fields, plus their associated data." 
  • django/branches/newforms-admin/django/newforms/widgets.py

    r5828 r5926  
    99 
    1010from itertools import chain 
     11from django.conf import settings 
    1112from django.utils.datastructures import MultiValueDict 
    1213from django.utils.html import escape 
     
    1617 
    1718__all__ = ( 
    18     'Widget', 'TextInput', 'PasswordInput', 
     19    'Media', 'Widget', 'TextInput', 'PasswordInput', 
    1920    'HiddenInput', 'MultipleHiddenInput', 
    2021    'FileInput', 'Textarea', 'CheckboxInput', 
     
    2324) 
    2425 
     26MEDIA_TYPES = ('css','js') 
     27 
     28class 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 
     89def 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     
     116class 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         
    25124class Widget(object): 
     125    __metaclass__ = MediaDefiningClass 
    26126    is_hidden = False          # Determines whether this corresponds to an <input type="hidden">. 
    27127 
     
    406506        raise NotImplementedError('Subclasses must implement this method.') 
    407507 
     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     
    408516class SplitDateTimeWidget(MultiWidget): 
    409517    """ 
     
    418526            return [value.date(), value.time()] 
    419527        return [None, None] 
     528     
  • django/branches/newforms-admin/docs/newforms.txt

    r5918 r5926  
    7777      display itself as HTML. 
    7878 
     79    * **Media** -- A definition of the CSS and JavaScript resources that are 
     80      required to render a form. 
     81       
    7982The library is decoupled from the other Django components, such as the database 
    8083layer, views and templates. It relies only on Django settings, a couple of 
     
    19411944isn't that difficult, after all. 
    19421945 
     1946Media 
     1947===== 
     1948 
     1949Rendering an attractive and easy-to-use web form requires more than just  
     1950HTML - 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  
     1952page. The exact combination of CSS and JavaScript that is required for  
     1953any given page will depend upon the widgets that are in use on that page. 
     1954 
     1955This is where Django media definitions come in. Django allows you to  
     1956associate different media files with the forms and widgets that require 
     1957that media. For example, if you want to use a calendar to render DateFields,  
     1958you can define a custom Calendar widget. This widget can then be associated  
     1959with the CSS and Javascript that is required to render the calendar. When  
     1960the Calendar widget is used on a form, Django is able to identify the CSS and 
     1961JavaScript files that are required, and provide the list of file names 
     1962in 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 
     1986Media as a static definition 
     1987---------------------------- 
     1988 
     1989The easiest way to define media is as a static definition. Using this method, 
     1990the media declaration is an inner class. The properties of the inner class 
     1991define the media requirements.  
     1992 
     1993Here'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 
     2002This code defines a ``CalendarWidget``, which will be based on ``TextInput``. 
     2003Every time the CalendarWidget is used on a form, that form will be directed  
     2004to include the CSS file ``pretty.css``, and the JavaScript files  
     2005``animations.js`` and ``actions.js``.  
     2006 
     2007This static media definition is converted at runtime into a widget property 
     2008named ``media``. The media for a CalendarWidget instance can be retrieved  
     2009through 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 
     2017Here's a list of all possible ``Media`` options. There are no required options. 
     2018 
     2019``css`` 
     2020~~~~~~~ 
     2021 
     2022A dictionary describing the CSS files required for various forms of output  
     2023media.  
     2024 
     2025The 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  
     2027files. 
     2028 
     2029.. _the section on media paths: `Paths in media definitions`_ 
     2030 
     2031The keys in the dictionary are the output media types. These are the same  
     2032types accepted by CSS files in media declarations: 'all', 'aural', 'braille', 
     2033'embossed', 'handheld', 'print', 'projection', 'screen', 'tty' and 'tv'. If 
     2034you need to have different stylesheets for different media types, provide 
     2035a list of CSS files for each output medium. The following example would 
     2036provide 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 
     2044If a group of CSS files are appropriate for multiple output media types,  
     2045the dictionary key can be a comma separated list of output media types.  
     2046In the following example, TV's and projectors will have the same media  
     2047requirements:: 
     2048 
     2049    class Media: 
     2050        css = { 
     2051            'screen': ('pretty.css',), 
     2052            'tv,projector': ('lo_res.css',), 
     2053            'print': ('newspaper.css',) 
     2054        } 
     2055     
     2056If 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 
     2065A tuple describing the required javascript files. See 
     2066`the section on media paths`_ for details of how to specify paths to media  
     2067files. 
     2068 
     2069``extend``    
     2070~~~~~~~~~~ 
     2071 
     2072A boolean defining inheritance behavior for media declarations.  
     2073 
     2074By default, any object using a static media definition will inherit all the 
     2075media associated with the parent widget. This occurs regardless of how the  
     2076parent defines its media requirements. For example, if we were to extend our 
     2077basic 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 
     2094The FancyCalendar widget inherits all the media from it's parent widget. If  
     2095you don't want media to be inherited in this way, add an ``extend=False`` 
     2096declaration 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 
     2111If you require even more control over media inheritance, define your media  
     2112using a `dynamic property`_. Dynamic properties give you complete control over 
     2113which media files are inherited, and which are not. 
     2114 
     2115.. _dynamic property: `Media as a dynamic property`_ 
     2116     
     2117Media as a dynamic property 
     2118--------------------------- 
     2119 
     2120If you need to perform some more sophisticated manipulation of media 
     2121requirements, you can define the media property directly. This is done 
     2122by defining a model property that returns an instance of ``forms.Media``. 
     2123The constructor for ``forms.Media`` accepts ``css`` and ``js`` keyword  
     2124arguments in the same format as that used in a static media definition. 
     2125 
     2126For example, the static media definition for our Calendar Widget could 
     2127also 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 
     2135See the section on `Media objects`_ for more details on how to construct 
     2136return values for dynamic media properties. 
     2137 
     2138Paths in media definitions 
     2139-------------------------- 
     2140 
     2141Paths used to specify media can be either relative or absolute. If a path  
     2142starts with 'http://' or 'https://', it will be interpreted as an absolute 
     2143path, 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 
     2156Media objects 
     2157------------- 
     2158 
     2159When you interrogate the media attribute of a widget or form, the value that 
     2160is returned is a ``forms.Media`` object. As we have already seen, the string 
     2161representation of a Media object is the HTML required to include media  
     2162in the ``<head>`` block of your HTML page.  
     2163 
     2164However, Media objects have some other interesting properties. 
     2165 
     2166Media subsets 
     2167~~~~~~~~~~~~~ 
     2168 
     2169If you only want media of a particular type, you can use the subscript operator  
     2170to 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 
     2181When you use the subscript operator, the value that is returned is a new  
     2182Media object -- but one that only contains the media of interest. 
     2183 
     2184Combining media objects 
     2185~~~~~~~~~~~~~~~~~~~~~~~ 
     2186 
     2187Media objects can also be added together. When two media objects are added, 
     2188the 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     
     2209Media on Forms 
     2210-------------- 
     2211 
     2212Widgets aren't the only objects that can have media definitions -- forms 
     2213can also define media. The rules for media definitions on forms are the 
     2214same as the rules for widgets: declarations can be static or dynamic; 
     2215path and inheritance rules for those declarations are exactly the same. 
     2216 
     2217Regardless of whether you define a media declaration, *all* Form objects  
     2218have a media property. The default value for this property is the result  
     2219of 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 
     2232If you want to associate additional media with a form -- for example, CSS for form  
     2233layout -- 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 
    19432252More coming soon 
    19442253================ 
  • django/branches/newforms-admin/tests/regressiontests/forms/tests.py

    r5918 r5926  
    33from regressions import regression_tests 
    44from formsets import formset_tests 
     5from media import media_tests 
    56 
    67form_tests = r""" 
     
    38053806    'regressions': regression_tests, 
    38063807    'formset_tests': formset_tests, 
     3808    'media_tests': media_tests, 
    38073809} 
    38083810