mirror of
https://github.com/django/django.git
synced 2025-07-04 17:59:13 +00:00
newforms-admin: Fixed #4418 -- Added the ability for widgets, forms, and Admin declarations to have media definitions.
git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@5926 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
28e350301f
commit
549198b714
@ -2,6 +2,7 @@ from django import oldforms, template
|
||||
from django import newforms as forms
|
||||
from django.newforms.formsets import all_valid
|
||||
from django.newforms.models import inline_formset
|
||||
from django.newforms.widgets import Media, MediaDefiningClass
|
||||
from django.contrib.admin import widgets
|
||||
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
|
||||
from django.db import models
|
||||
@ -49,12 +50,26 @@ class AdminForm(object):
|
||||
for bf in self.form:
|
||||
return bf
|
||||
|
||||
def _media(self):
|
||||
media = self.form.media
|
||||
for fs in self.fieldsets:
|
||||
media = media + fs.media
|
||||
return media
|
||||
media = property(_media)
|
||||
|
||||
class Fieldset(object):
|
||||
def __init__(self, name=None, fields=(), classes=(), description=None):
|
||||
self.name, self.fields = name, fields
|
||||
self.classes = u' '.join(classes)
|
||||
self.description = description
|
||||
|
||||
def _media(self):
|
||||
from django.conf import settings
|
||||
if 'collapse' in self.classes:
|
||||
return Media(js=['%sjs/admin/CollapsedFieldsets.js' % settings.ADMIN_MEDIA_PREFIX])
|
||||
return Media()
|
||||
media = property(_media)
|
||||
|
||||
class BoundFieldset(object):
|
||||
def __init__(self, form, fieldset):
|
||||
self.form, self.fieldset = form, fieldset
|
||||
@ -123,12 +138,12 @@ class BaseModelAdmin(object):
|
||||
|
||||
# For DateFields, add a custom CSS class.
|
||||
if isinstance(db_field, models.DateField):
|
||||
kwargs['widget'] = forms.TextInput(attrs={'class': 'vDateField', 'size': '10'})
|
||||
kwargs['widget'] = widgets.AdminDateWidget
|
||||
return db_field.formfield(**kwargs)
|
||||
|
||||
# For TimeFields, add a custom CSS class.
|
||||
if isinstance(db_field, models.TimeField):
|
||||
kwargs['widget'] = forms.TextInput(attrs={'class': 'vTimeField', 'size': '8'})
|
||||
kwargs['widget'] = widgets.AdminTimeWidget
|
||||
return db_field.formfield(**kwargs)
|
||||
|
||||
# For ForeignKey or ManyToManyFields, use a special widget.
|
||||
@ -148,6 +163,7 @@ class BaseModelAdmin(object):
|
||||
|
||||
class ModelAdmin(BaseModelAdmin):
|
||||
"Encapsulates all admin options and functionality for a given model."
|
||||
__metaclass__ = MediaDefiningClass
|
||||
|
||||
list_display = ('__str__',)
|
||||
list_display_links = ()
|
||||
@ -159,7 +175,6 @@ class ModelAdmin(BaseModelAdmin):
|
||||
save_as = False
|
||||
save_on_top = False
|
||||
ordering = None
|
||||
js = None
|
||||
prepopulated_fields = {}
|
||||
filter_vertical = ()
|
||||
filter_horizontal = ()
|
||||
@ -194,37 +209,19 @@ class ModelAdmin(BaseModelAdmin):
|
||||
else:
|
||||
return self.change_view(request, unquote(url))
|
||||
|
||||
def javascript(self, request, fieldsets):
|
||||
"""
|
||||
Returns a list of URLs to include via <script> statements.
|
||||
|
||||
The URLs can be absolute ('/js/admin/') or explicit
|
||||
('http://example.com/foo.js').
|
||||
"""
|
||||
def _media(self):
|
||||
from django.conf import settings
|
||||
|
||||
js = ['js/core.js', 'js/admin/RelatedObjectLookups.js']
|
||||
if self.prepopulated_fields:
|
||||
js.append('js/urlify.js')
|
||||
if self.opts.has_field_type(models.DateTimeField) or self.opts.has_field_type(models.TimeField) or self.opts.has_field_type(models.DateField):
|
||||
js.extend(['js/calendar.js', 'js/admin/DateTimeShortcuts.js'])
|
||||
if self.opts.get_ordered_objects():
|
||||
js.extend(['js/getElementsBySelector.js', 'js/dom-drag.js' , 'js/admin/ordering.js'])
|
||||
if self.js:
|
||||
js.extend(self.js)
|
||||
if self.filter_vertical or self.filter_horizontal:
|
||||
js.extend(['js/SelectBox.js' , 'js/SelectFilter2.js'])
|
||||
for fs in fieldsets:
|
||||
if 'collapse' in fs.classes:
|
||||
js.append('js/admin/CollapsedFieldsets.js')
|
||||
break
|
||||
prefix = settings.ADMIN_MEDIA_PREFIX
|
||||
return ['%s%s' % (prefix, url) for url in js]
|
||||
|
||||
def javascript_add(self, request):
|
||||
return self.javascript(request, self.fieldsets_add(request))
|
||||
|
||||
def javascript_change(self, request, obj):
|
||||
return self.javascript(request, self.fieldsets_change(request, obj))
|
||||
return Media(js=['%s%s' % (settings.ADMIN_MEDIA_PREFIX, url) for url in js])
|
||||
media = property(_media)
|
||||
|
||||
def fieldsets(self, request):
|
||||
"""
|
||||
@ -244,13 +241,11 @@ class ModelAdmin(BaseModelAdmin):
|
||||
|
||||
def fieldsets_add(self, request):
|
||||
"Hook for specifying Fieldsets for the add form."
|
||||
for fs in self.fieldsets(request):
|
||||
yield fs
|
||||
return list(self.fieldsets(request))
|
||||
|
||||
def fieldsets_change(self, request, obj):
|
||||
"Hook for specifying Fieldsets for the change form."
|
||||
for fs in self.fieldsets(request):
|
||||
yield fs
|
||||
return list(self.fieldsets(request))
|
||||
|
||||
def has_add_permission(self, request):
|
||||
"Returns True if the given request has permission to add an object."
|
||||
@ -430,12 +425,17 @@ class ModelAdmin(BaseModelAdmin):
|
||||
inline_formset = FormSet()
|
||||
inline_formsets.append(inline_formset)
|
||||
|
||||
adminForm = AdminForm(form, list(self.fieldsets_add(request)), self.prepopulated_fields)
|
||||
media = self.media + adminForm.media
|
||||
for fs in inline_formsets:
|
||||
media = media + fs.media
|
||||
|
||||
c = template.RequestContext(request, {
|
||||
'title': _('Add %s') % opts.verbose_name,
|
||||
'adminform': AdminForm(form, self.fieldsets_add(request), self.prepopulated_fields),
|
||||
'adminform': adminForm,
|
||||
'is_popup': request.REQUEST.has_key('_popup'),
|
||||
'show_delete': False,
|
||||
'javascript_imports': self.javascript_add(request),
|
||||
'media': media,
|
||||
'bound_inlines': [BoundInline(i, fs) for i, fs in zip(self.inlines, inline_formsets)],
|
||||
})
|
||||
return render_change_form(self, model, model.AddManipulator(), c, add=True)
|
||||
@ -494,13 +494,19 @@ class ModelAdmin(BaseModelAdmin):
|
||||
#related.get_accessor_name())
|
||||
#orig_list = func()
|
||||
#oldform.order_objects.extend(orig_list)
|
||||
|
||||
adminForm = AdminForm(form, self.fieldsets_change(request, obj), self.prepopulated_fields)
|
||||
media = self.media + adminForm.media
|
||||
for fs in inline_formsets:
|
||||
media = media + fs.media
|
||||
|
||||
c = template.RequestContext(request, {
|
||||
'title': _('Change %s') % opts.verbose_name,
|
||||
'adminform': AdminForm(form, self.fieldsets_change(request, obj), self.prepopulated_fields),
|
||||
'adminform': adminForm,
|
||||
'object_id': object_id,
|
||||
'original': obj,
|
||||
'is_popup': request.REQUEST.has_key('_popup'),
|
||||
'javascript_imports': self.javascript_change(request, obj),
|
||||
'media': media,
|
||||
'bound_inlines': [BoundInline(i, fs) for i, fs in zip(self.inlines, inline_formsets)],
|
||||
})
|
||||
return render_change_form(self, model, model.ChangeManipulator(object_id), c, change=True)
|
||||
|
@ -2,7 +2,6 @@
|
||||
{% load i18n admin_modify adminmedia %}
|
||||
{% block extrahead %}{{ block.super }}
|
||||
<script type="text/javascript" src="../../../../jsi18n/"></script>
|
||||
{% for js in javascript_imports %}{% include_admin_script js %}{% endfor %}
|
||||
{% endblock %}
|
||||
{% block stylesheet %}{% admin_media_prefix %}css/forms.css{% endblock %}
|
||||
{% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %}
|
||||
|
@ -3,8 +3,7 @@
|
||||
|
||||
{% block extrahead %}{{ block.super }}
|
||||
<script type="text/javascript" src="../../../jsi18n/"></script>
|
||||
{% for js in javascript_imports %}<script type="text/javascript" src="{{ js }}"></script>
|
||||
{% endfor %}
|
||||
{{ media }}
|
||||
{% endblock %}
|
||||
|
||||
{% block stylesheet %}{% admin_media_prefix %}css/forms.css{% endblock %}
|
||||
|
@ -5,6 +5,7 @@ Form Widget classes specific to the Django admin site.
|
||||
from django import newforms as forms
|
||||
from django.utils.text import capfirst
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.conf import settings
|
||||
|
||||
class FilteredSelectMultiple(forms.SelectMultiple):
|
||||
"""
|
||||
@ -28,13 +29,28 @@ class FilteredSelectMultiple(forms.SelectMultiple):
|
||||
(name, self.verbose_name.replace('"', '\\"'), int(self.is_stacked), settings.ADMIN_MEDIA_PREFIX))
|
||||
return u''.join(output)
|
||||
|
||||
class AdminDateWidget(forms.TextInput):
|
||||
class Media:
|
||||
js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js",
|
||||
settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js")
|
||||
|
||||
def __init__(self, attrs={}):
|
||||
super(AdminDateWidget, self).__init__(attrs={'class': 'vDateField', 'size': '10'})
|
||||
|
||||
class AdminTimeWidget(forms.TextInput):
|
||||
class Media:
|
||||
js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js",
|
||||
settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js")
|
||||
|
||||
def __init__(self, attrs={}):
|
||||
super(AdminTimeWidget, self).__init__(attrs={'class': 'vTimeField', 'size': '8'})
|
||||
|
||||
class AdminSplitDateTime(forms.SplitDateTimeWidget):
|
||||
"""
|
||||
A SplitDateTime Widget that has some admin-specific styling.
|
||||
"""
|
||||
def __init__(self, attrs=None):
|
||||
widgets = [forms.TextInput(attrs={'class': 'vDateField', 'size': '10'}),
|
||||
forms.TextInput(attrs={'class': 'vTimeField', 'size': '8'})]
|
||||
widgets = [AdminDateWidget, AdminTimeWidget]
|
||||
# Note that we're calling MultiWidget, not SplitDateTimeWidget, because
|
||||
# we want to define widgets.
|
||||
forms.MultiWidget.__init__(self, widgets, attrs)
|
||||
|
@ -9,7 +9,7 @@ from django.utils.html import escape
|
||||
from django.utils.encoding import StrAndUnicode, smart_unicode, force_unicode
|
||||
|
||||
from fields import Field
|
||||
from widgets import TextInput, Textarea
|
||||
from widgets import Media, media_property, TextInput, Textarea
|
||||
from util import flatatt, ErrorDict, ErrorList, ValidationError
|
||||
|
||||
__all__ = ('BaseForm', 'Form')
|
||||
@ -37,6 +37,7 @@ class DeclarativeFieldsMetaclass(type):
|
||||
"""
|
||||
Metaclass that converts Field attributes to a dictionary called
|
||||
'base_fields', taking into account parent class 'base_fields' as well.
|
||||
Also integrates any additional media definitions
|
||||
"""
|
||||
def __new__(cls, name, bases, attrs):
|
||||
fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)]
|
||||
@ -50,7 +51,11 @@ class DeclarativeFieldsMetaclass(type):
|
||||
fields = base.base_fields.items() + fields
|
||||
|
||||
attrs['base_fields'] = SortedDictFromList(fields)
|
||||
return type.__new__(cls, name, bases, attrs)
|
||||
|
||||
new_class = type.__new__(cls, name, bases, attrs)
|
||||
if 'media' not in attrs:
|
||||
new_class.media = media_property(new_class)
|
||||
return new_class
|
||||
|
||||
class BaseForm(StrAndUnicode):
|
||||
# This is the main implementation of all the Form logic. Note that this
|
||||
@ -235,6 +240,16 @@ class BaseForm(StrAndUnicode):
|
||||
self.is_bound = False
|
||||
self.__errors = None
|
||||
|
||||
def _get_media(self):
|
||||
"""
|
||||
Provide a description of all media required to render the widgets on this form
|
||||
"""
|
||||
media = Media()
|
||||
for field in self.fields.values():
|
||||
media = media + field.widget.media
|
||||
return media
|
||||
media = property(_get_media)
|
||||
|
||||
class Form(BaseForm):
|
||||
"A collection of Fields, plus their associated data."
|
||||
# This is a separate class from BaseForm in order to abstract the way
|
||||
|
@ -1,6 +1,6 @@
|
||||
from forms import Form, ValidationError
|
||||
from fields import IntegerField, BooleanField
|
||||
from widgets import HiddenInput
|
||||
from widgets import HiddenInput, Media
|
||||
|
||||
# special field names
|
||||
FORM_COUNT_FIELD_NAME = 'COUNT'
|
||||
@ -154,6 +154,15 @@ class BaseFormSet(object):
|
||||
self.full_clean()
|
||||
return self._is_valid
|
||||
|
||||
def _get_media(self):
|
||||
# All the forms on a FormSet are the same, so you only need to
|
||||
# interrogate the first form for media.
|
||||
if self.forms:
|
||||
return self.forms[0].media
|
||||
else:
|
||||
return Media()
|
||||
media = property(_get_media)
|
||||
|
||||
def formset_for_form(form, formset=BaseFormSet, num_extra=1, orderable=False, deletable=False):
|
||||
"""Return a FormSet for the given form class."""
|
||||
attrs = {'form_class': form, 'num_extra': num_extra, 'orderable': orderable, 'deletable': deletable}
|
||||
|
@ -8,6 +8,7 @@ except NameError:
|
||||
from sets import Set as set # Python 2.3 fallback
|
||||
|
||||
from itertools import chain
|
||||
from django.conf import settings
|
||||
from django.utils.datastructures import MultiValueDict
|
||||
from django.utils.html import escape
|
||||
from django.utils.translation import ugettext
|
||||
@ -15,14 +16,113 @@ from django.utils.encoding import StrAndUnicode, force_unicode
|
||||
from util import flatatt
|
||||
|
||||
__all__ = (
|
||||
'Widget', 'TextInput', 'PasswordInput',
|
||||
'Media', 'Widget', 'TextInput', 'PasswordInput',
|
||||
'HiddenInput', 'MultipleHiddenInput',
|
||||
'FileInput', 'Textarea', 'CheckboxInput',
|
||||
'Select', 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect',
|
||||
'CheckboxSelectMultiple', 'MultiWidget', 'SplitDateTimeWidget',
|
||||
)
|
||||
|
||||
MEDIA_TYPES = ('css','js')
|
||||
|
||||
class Media(StrAndUnicode):
|
||||
def __init__(self, media=None, **kwargs):
|
||||
if media:
|
||||
media_attrs = media.__dict__
|
||||
else:
|
||||
media_attrs = kwargs
|
||||
|
||||
self._css = {}
|
||||
self._js = []
|
||||
|
||||
for name in MEDIA_TYPES:
|
||||
getattr(self, 'add_' + name)(media_attrs.get(name, None))
|
||||
|
||||
# Any leftover attributes must be invalid.
|
||||
# if media_attrs != {}:
|
||||
# raise TypeError, "'class Media' has invalid attribute(s): %s" % ','.join(media_attrs.keys())
|
||||
|
||||
def __unicode__(self):
|
||||
return self.render()
|
||||
|
||||
def render(self):
|
||||
return u'\n'.join(chain(*[getattr(self, 'render_' + name)() for name in MEDIA_TYPES]))
|
||||
|
||||
def render_js(self):
|
||||
return [u'<script type="text/javascript" src="%s"></script>' % self.absolute_path(path) for path in self._js]
|
||||
|
||||
def render_css(self):
|
||||
# To keep rendering order consistent, we can't just iterate over items().
|
||||
# We need to sort the keys, and iterate over the sorted list.
|
||||
media = self._css.keys()
|
||||
media.sort()
|
||||
return chain(*[
|
||||
[u'<link href="%s" type="text/css" media="%s" rel="stylesheet" />' % (self.absolute_path(path), medium)
|
||||
for path in self._css[medium]]
|
||||
for medium in media])
|
||||
|
||||
def absolute_path(self, path):
|
||||
return (path.startswith(u'http://') or path.startswith(u'https://')) and path or u''.join([settings.MEDIA_URL,path])
|
||||
|
||||
def __getitem__(self, name):
|
||||
"Returns a Media object that only contains media of the given type"
|
||||
if name in MEDIA_TYPES:
|
||||
return Media(**{name: getattr(self, '_' + name)})
|
||||
raise KeyError('Unknown media type "%s"' % name)
|
||||
|
||||
def add_js(self, data):
|
||||
if data:
|
||||
self._js.extend([path for path in data if path not in self._js])
|
||||
|
||||
def add_css(self, data):
|
||||
if data:
|
||||
for medium, paths in data.items():
|
||||
self._css.setdefault(medium, []).extend([path for path in paths if path not in self._css[medium]])
|
||||
|
||||
def __add__(self, other):
|
||||
combined = Media()
|
||||
for name in MEDIA_TYPES:
|
||||
getattr(combined, 'add_' + name)(getattr(self, '_' + name, None))
|
||||
getattr(combined, 'add_' + name)(getattr(other, '_' + name, None))
|
||||
return combined
|
||||
|
||||
def media_property(cls):
|
||||
def _media(self):
|
||||
# Get the media property of the superclass, if it exists
|
||||
if hasattr(super(cls, self), 'media'):
|
||||
base = super(cls, self).media
|
||||
else:
|
||||
base = Media()
|
||||
|
||||
# Get the media definition for this class
|
||||
definition = getattr(cls, 'Media', None)
|
||||
if definition:
|
||||
extend = getattr(definition, 'extend', True)
|
||||
if extend:
|
||||
if extend == True:
|
||||
m = base
|
||||
else:
|
||||
m = Media()
|
||||
for medium in extend:
|
||||
m = m + base[medium]
|
||||
m = m + Media(definition)
|
||||
return m + Media(definition)
|
||||
else:
|
||||
return Media(definition)
|
||||
else:
|
||||
return base
|
||||
return property(_media)
|
||||
|
||||
class MediaDefiningClass(type):
|
||||
"Metaclass for classes that can have media definitions"
|
||||
def __new__(cls, name, bases, attrs):
|
||||
new_class = type.__new__(cls, name, bases, attrs)
|
||||
if 'media' not in attrs:
|
||||
new_class.media = media_property(new_class)
|
||||
return new_class
|
||||
|
||||
class Widget(object):
|
||||
__metaclass__ = MediaDefiningClass
|
||||
is_hidden = False # Determines whether this corresponds to an <input type="hidden">.
|
||||
|
||||
def __init__(self, attrs=None):
|
||||
@ -405,6 +505,14 @@ class MultiWidget(Widget):
|
||||
"""
|
||||
raise NotImplementedError('Subclasses must implement this method.')
|
||||
|
||||
def _get_media(self):
|
||||
"Media for a multiwidget is the combination of all media of the subwidgets"
|
||||
media = Media()
|
||||
for w in self.widgets:
|
||||
media = media + w.media
|
||||
return media
|
||||
media = property(_get_media)
|
||||
|
||||
class SplitDateTimeWidget(MultiWidget):
|
||||
"""
|
||||
A Widget that splits datetime input into two <input type="text"> boxes.
|
||||
@ -417,3 +525,4 @@ class SplitDateTimeWidget(MultiWidget):
|
||||
if value:
|
||||
return [value.date(), value.time()]
|
||||
return [None, None]
|
||||
|
@ -76,6 +76,9 @@ The library deals with these concepts:
|
||||
* **Form** -- A collection of fields that knows how to validate itself and
|
||||
display itself as HTML.
|
||||
|
||||
* **Media** -- A definition of the CSS and JavaScript resources that are
|
||||
required to render a form.
|
||||
|
||||
The library is decoupled from the other Django components, such as the database
|
||||
layer, views and templates. It relies only on Django settings, a couple of
|
||||
``django.utils`` helper functions and Django's internationalization hooks (but
|
||||
@ -1940,6 +1943,312 @@ more than one model, or a form that contains fields that *aren't* on a model,
|
||||
you shouldn't use these shortcuts. Creating a ``Form`` class the "long" way
|
||||
isn't that difficult, after all.
|
||||
|
||||
Media
|
||||
=====
|
||||
|
||||
Rendering an attractive and easy-to-use web form requires more than just
|
||||
HTML - it also requires CSS stylesheets, and if you want to use fancy
|
||||
"Web2.0" widgets, you may also need to include some JavaScript on each
|
||||
page. The exact combination of CSS and JavaScript that is required for
|
||||
any given page will depend upon the widgets that are in use on that page.
|
||||
|
||||
This is where Django media definitions come in. Django allows you to
|
||||
associate different media files with the forms and widgets that require
|
||||
that media. For example, if you want to use a calendar to render DateFields,
|
||||
you can define a custom Calendar widget. This widget can then be associated
|
||||
with the CSS and Javascript that is required to render the calendar. When
|
||||
the Calendar widget is used on a form, Django is able to identify the CSS and
|
||||
JavaScript files that are required, and provide the list of file names
|
||||
in a form suitable for easy inclusion on your web page.
|
||||
|
||||
.. admonition:: Media and Django Admin
|
||||
|
||||
The Django Admin application defines a number of customized widgets
|
||||
for calendars, filtered selections, and so on. These widgets define
|
||||
media requirements, and the Django Admin uses the custom widgets
|
||||
in place of the Django defaults. The Admin templates will only include
|
||||
those media files that are required to render the widgets on any
|
||||
given page.
|
||||
|
||||
If you like the widgets that the Django Admin application uses,
|
||||
feel free to use them in your own application! They're all stored
|
||||
in ``django.contrib.admin.widgets``.
|
||||
|
||||
.. admonition:: Which JavaScript toolkit?
|
||||
|
||||
Many JavaScript toolkits exist, and many of them include widgets (such
|
||||
as calendar widgets) that can be used to enhance your application.
|
||||
Django has deliberately avoided blessing any one JavaScript toolkit.
|
||||
Each toolkit has its own relative strengths and weaknesses - use
|
||||
whichever toolkit suits your requirements. Django is able to integrate
|
||||
with any JavaScript toolkit.
|
||||
|
||||
Media as a static definition
|
||||
----------------------------
|
||||
|
||||
The easiest way to define media is as a static definition. Using this method,
|
||||
the media declaration is an inner class. The properties of the inner class
|
||||
define the media requirements.
|
||||
|
||||
Here's a simple example::
|
||||
|
||||
class CalendarWidget(forms.TextInput):
|
||||
class Media:
|
||||
css = {
|
||||
'all': ('pretty.css',)
|
||||
}
|
||||
js = ('animations.js', 'actions.js')
|
||||
|
||||
This code defines a ``CalendarWidget``, which will be based on ``TextInput``.
|
||||
Every time the CalendarWidget is used on a form, that form will be directed
|
||||
to include the CSS file ``pretty.css``, and the JavaScript files
|
||||
``animations.js`` and ``actions.js``.
|
||||
|
||||
This static media definition is converted at runtime into a widget property
|
||||
named ``media``. The media for a CalendarWidget instance can be retrieved
|
||||
through this property::
|
||||
|
||||
>>> w = CalendarWidget()
|
||||
>>> print w.media
|
||||
<link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/animations.js"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/actions.js"></script>
|
||||
|
||||
Here's a list of all possible ``Media`` options. There are no required options.
|
||||
|
||||
``css``
|
||||
~~~~~~~
|
||||
|
||||
A dictionary describing the CSS files required for various forms of output
|
||||
media.
|
||||
|
||||
The values in the dictionary should be a tuple/list of file names. See
|
||||
`the section on media paths`_ for details of how to specify paths to media
|
||||
files.
|
||||
|
||||
.. _the section on media paths: `Paths in media definitions`_
|
||||
|
||||
The keys in the dictionary are the output media types. These are the same
|
||||
types accepted by CSS files in media declarations: 'all', 'aural', 'braille',
|
||||
'embossed', 'handheld', 'print', 'projection', 'screen', 'tty' and 'tv'. If
|
||||
you need to have different stylesheets for different media types, provide
|
||||
a list of CSS files for each output medium. The following example would
|
||||
provide two CSS options -- one for the screen, and one for print::
|
||||
|
||||
class Media:
|
||||
css = {
|
||||
'screen': ('pretty.css',),
|
||||
'print': ('newspaper.css',)
|
||||
}
|
||||
|
||||
If a group of CSS files are appropriate for multiple output media types,
|
||||
the dictionary key can be a comma separated list of output media types.
|
||||
In the following example, TV's and projectors will have the same media
|
||||
requirements::
|
||||
|
||||
class Media:
|
||||
css = {
|
||||
'screen': ('pretty.css',),
|
||||
'tv,projector': ('lo_res.css',),
|
||||
'print': ('newspaper.css',)
|
||||
}
|
||||
|
||||
If this last CSS definition were to be rendered, it would become the following HTML::
|
||||
|
||||
<link href="http://media.example.com/pretty.css" type="text/css" media="screen" rel="stylesheet" />
|
||||
<link href="http://media.example.com/lo_res.css" type="text/css" media="tv,projector" rel="stylesheet" />
|
||||
<link href="http://media.example.com/newspaper.css" type="text/css" media="print" rel="stylesheet" />
|
||||
|
||||
``js``
|
||||
~~~~~~
|
||||
|
||||
A tuple describing the required javascript files. See
|
||||
`the section on media paths`_ for details of how to specify paths to media
|
||||
files.
|
||||
|
||||
``extend``
|
||||
~~~~~~~~~~
|
||||
|
||||
A boolean defining inheritance behavior for media declarations.
|
||||
|
||||
By default, any object using a static media definition will inherit all the
|
||||
media associated with the parent widget. This occurs regardless of how the
|
||||
parent defines its media requirements. For example, if we were to extend our
|
||||
basic Calendar widget from the example above::
|
||||
|
||||
class FancyCalendarWidget(CalendarWidget):
|
||||
class Media:
|
||||
css = {
|
||||
'all': ('fancy.css',)
|
||||
}
|
||||
js = ('whizbang.js',)
|
||||
|
||||
>>> w = FancyCalendarWidget()
|
||||
>>> print w.media
|
||||
<link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/fancy.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/animations.js"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/actions.js"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/whizbang.js"></script>
|
||||
|
||||
The FancyCalendar widget inherits all the media from it's parent widget. If
|
||||
you don't want media to be inherited in this way, add an ``extend=False``
|
||||
declaration to the media declaration::
|
||||
|
||||
class FancyCalendar(Calendar):
|
||||
class Media:
|
||||
extend = False
|
||||
css = {
|
||||
'all': ('fancy.css',)
|
||||
}
|
||||
js = ('whizbang.js',)
|
||||
|
||||
>>> w = FancyCalendarWidget()
|
||||
>>> print w.media
|
||||
<link href="http://media.example.com/fancy.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/whizbang.js"></script>
|
||||
|
||||
If you require even more control over media inheritance, define your media
|
||||
using a `dynamic property`_. Dynamic properties give you complete control over
|
||||
which media files are inherited, and which are not.
|
||||
|
||||
.. _dynamic property: `Media as a dynamic property`_
|
||||
|
||||
Media as a dynamic property
|
||||
---------------------------
|
||||
|
||||
If you need to perform some more sophisticated manipulation of media
|
||||
requirements, you can define the media property directly. This is done
|
||||
by defining a model property that returns an instance of ``forms.Media``.
|
||||
The constructor for ``forms.Media`` accepts ``css`` and ``js`` keyword
|
||||
arguments in the same format as that used in a static media definition.
|
||||
|
||||
For example, the static media definition for our Calendar Widget could
|
||||
also be defined in a dynamic fashion::
|
||||
|
||||
class CalendarWidget(forms.TextInput):
|
||||
def _media(self):
|
||||
return forms.Media(css={'all': ('pretty.css',)},
|
||||
js=('animations.js', 'actions.js'))
|
||||
media = property(_media)
|
||||
|
||||
See the section on `Media objects`_ for more details on how to construct
|
||||
return values for dynamic media properties.
|
||||
|
||||
Paths in media definitions
|
||||
--------------------------
|
||||
|
||||
Paths used to specify media can be either relative or absolute. If a path
|
||||
starts with 'http://' or 'https://', it will be interpreted as an absolute
|
||||
path, and left as-is. All other paths will be prepended with the value of
|
||||
``settings.MEDIA_URL``. For example, if the MEDIA_URL for your site was
|
||||
``http://media.example.com/``::
|
||||
|
||||
class CalendarWidget(forms.TextInput):
|
||||
class Media:
|
||||
js = ('animations.js', 'http://othersite.com/actions.js')
|
||||
|
||||
>>> w = CalendarWidget()
|
||||
>>> print w.media
|
||||
<script type="text/javascript" src="http://media.example.com/animations.js"></script>
|
||||
<script type="text/javascript" src="http://othersite.com/actions.js"></script>
|
||||
|
||||
Media objects
|
||||
-------------
|
||||
|
||||
When you interrogate the media attribute of a widget or form, the value that
|
||||
is returned is a ``forms.Media`` object. As we have already seen, the string
|
||||
representation of a Media object is the HTML required to include media
|
||||
in the ``<head>`` block of your HTML page.
|
||||
|
||||
However, Media objects have some other interesting properties.
|
||||
|
||||
Media subsets
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
If you only want media of a particular type, you can use the subscript operator
|
||||
to filter out a medium of interest. For example::
|
||||
|
||||
>>> w = CalendarWidget()
|
||||
>>> print w.media
|
||||
<link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/animations.js"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/actions.js"></script>
|
||||
|
||||
>>> print w.media['css']
|
||||
<link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" />
|
||||
|
||||
When you use the subscript operator, the value that is returned is a new
|
||||
Media object -- but one that only contains the media of interest.
|
||||
|
||||
Combining media objects
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Media objects can also be added together. When two media objects are added,
|
||||
the resulting Media object contains the union of the media from both files::
|
||||
|
||||
class CalendarWidget(forms.TextInput):
|
||||
class Media:
|
||||
css = {
|
||||
'all': ('pretty.css',)
|
||||
}
|
||||
js = ('animations.js', 'actions.js')
|
||||
|
||||
class OtherWidget(forms.TextInput):
|
||||
class Media:
|
||||
js = ('whizbang.js',)
|
||||
|
||||
>>> w1 = CalendarWidget()
|
||||
>>> w2 = OtherWidget()
|
||||
>>> print w1+w2
|
||||
<link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/animations.js"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/actions.js"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/whizbang.js"></script>
|
||||
|
||||
Media on Forms
|
||||
--------------
|
||||
|
||||
Widgets aren't the only objects that can have media definitions -- forms
|
||||
can also define media. The rules for media definitions on forms are the
|
||||
same as the rules for widgets: declarations can be static or dynamic;
|
||||
path and inheritance rules for those declarations are exactly the same.
|
||||
|
||||
Regardless of whether you define a media declaration, *all* Form objects
|
||||
have a media property. The default value for this property is the result
|
||||
of adding the media definitions for all widgets that are part of the form::
|
||||
|
||||
class ContactForm(forms.Form):
|
||||
date = DateField(widget=CalendarWidget)
|
||||
name = CharField(max_length=40, widget=OtherWidget)
|
||||
|
||||
>>> f = ContactForm()
|
||||
>>> f.media
|
||||
<link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/animations.js"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/actions.js"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/whizbang.js"></script>
|
||||
|
||||
If you want to associate additional media with a form -- for example, CSS for form
|
||||
layout -- simply add a media declaration to the form::
|
||||
|
||||
class ContactForm(forms.Form):
|
||||
date = DateField(widget=CalendarWidget)
|
||||
name = CharField(max_length=40, widget=OtherWidget)
|
||||
|
||||
class Media:
|
||||
css = {
|
||||
'all': ('layout.css',)
|
||||
}
|
||||
|
||||
>>> f = ContactForm()
|
||||
>>> f.media
|
||||
<link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/layout.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/animations.js"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/actions.js"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/whizbang.js"></script>
|
||||
|
||||
More coming soon
|
||||
================
|
||||
|
||||
|
357
tests/regressiontests/forms/media.py
Normal file
357
tests/regressiontests/forms/media.py
Normal file
@ -0,0 +1,357 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Tests for the media handling on widgets and forms
|
||||
|
||||
media_tests = r"""
|
||||
>>> from django.newforms import TextInput, Media, TextInput, CharField, Form, MultiWidget
|
||||
>>> from django.conf import settings
|
||||
>>> settings.MEDIA_URL = 'http://media.example.com'
|
||||
|
||||
# Check construction of media objects
|
||||
>>> 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'))
|
||||
>>> print m
|
||||
<link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js1"></script>
|
||||
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
|
||||
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
|
||||
|
||||
>>> class Foo:
|
||||
... 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')
|
||||
>>> m3 = Media(Foo)
|
||||
>>> print m3
|
||||
<link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js1"></script>
|
||||
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
|
||||
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
|
||||
|
||||
>>> m3 = Media(Foo)
|
||||
>>> print m3
|
||||
<link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js1"></script>
|
||||
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
|
||||
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
|
||||
|
||||
# A widget can exist without a media definition
|
||||
>>> class MyWidget(TextInput):
|
||||
... pass
|
||||
|
||||
>>> w = MyWidget()
|
||||
>>> print w.media
|
||||
<BLANKLINE>
|
||||
|
||||
###############################################################
|
||||
# DSL Class-based media definitions
|
||||
###############################################################
|
||||
|
||||
# A widget can define media if it needs to.
|
||||
# Any absolute path will be preserved; relative paths are combined
|
||||
# with the value of settings.MEDIA_URL
|
||||
>>> class MyWidget1(TextInput):
|
||||
... class 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')
|
||||
|
||||
>>> w1 = MyWidget1()
|
||||
>>> print w1.media
|
||||
<link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js1"></script>
|
||||
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
|
||||
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
|
||||
|
||||
# Media objects can be interrogated by media type
|
||||
>>> print w1.media['css']
|
||||
<link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" />
|
||||
|
||||
>>> print w1.media['js']
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js1"></script>
|
||||
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
|
||||
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
|
||||
|
||||
# Media objects can be combined. Any given media resource will appear only
|
||||
# once. Duplicated media definitions are ignored.
|
||||
>>> class MyWidget2(TextInput):
|
||||
... class Media:
|
||||
... css = {
|
||||
... 'all': ('/path/to/css2','/path/to/css3')
|
||||
... }
|
||||
... js = ('/path/to/js1','/path/to/js4')
|
||||
|
||||
>>> class MyWidget3(TextInput):
|
||||
... class Media:
|
||||
... css = {
|
||||
... 'all': ('/path/to/css3','/path/to/css1')
|
||||
... }
|
||||
... js = ('/path/to/js1','/path/to/js4')
|
||||
|
||||
>>> w2 = MyWidget2()
|
||||
>>> w3 = MyWidget3()
|
||||
>>> print w1.media + w2.media + w3.media
|
||||
<link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/path/to/css3" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js1"></script>
|
||||
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
|
||||
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js4"></script>
|
||||
|
||||
# Check that media addition hasn't affected the original objects
|
||||
>>> print w1.media
|
||||
<link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js1"></script>
|
||||
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
|
||||
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
|
||||
|
||||
###############################################################
|
||||
# Property-based media definitions
|
||||
###############################################################
|
||||
|
||||
# Widget media can be defined as a property
|
||||
>>> class MyWidget4(TextInput):
|
||||
... def _media(self):
|
||||
... return Media(css={'all': ('/some/path',)}, js = ('/some/js',))
|
||||
... media = property(_media)
|
||||
|
||||
>>> w4 = MyWidget4()
|
||||
>>> print w4.media
|
||||
<link href="http://media.example.com/some/path" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/some/js"></script>
|
||||
|
||||
# Media properties can reference the media of their parents
|
||||
>>> class MyWidget5(MyWidget4):
|
||||
... def _media(self):
|
||||
... return super(MyWidget5, self).media + Media(css={'all': ('/other/path',)}, js = ('/other/js',))
|
||||
... media = property(_media)
|
||||
|
||||
>>> w5 = MyWidget5()
|
||||
>>> print w5.media
|
||||
<link href="http://media.example.com/some/path" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/other/path" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/some/js"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/other/js"></script>
|
||||
|
||||
# Media properties can reference the media of their parents,
|
||||
# even if the parent media was defined using a class
|
||||
>>> class MyWidget6(MyWidget1):
|
||||
... def _media(self):
|
||||
... return super(MyWidget6, self).media + Media(css={'all': ('/other/path',)}, js = ('/other/js',))
|
||||
... media = property(_media)
|
||||
|
||||
>>> w6 = MyWidget6()
|
||||
>>> print w6.media
|
||||
<link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/other/path" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js1"></script>
|
||||
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
|
||||
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/other/js"></script>
|
||||
|
||||
###############################################################
|
||||
# Inheritance of media
|
||||
###############################################################
|
||||
|
||||
# If a widget extends another but provides no media definition, it inherits the parent widget's media
|
||||
>>> class MyWidget7(MyWidget1):
|
||||
... pass
|
||||
|
||||
>>> w7 = MyWidget7()
|
||||
>>> print w7.media
|
||||
<link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js1"></script>
|
||||
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
|
||||
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
|
||||
|
||||
# If a widget extends another but defines media, it extends the parent widget's media by default
|
||||
>>> class MyWidget8(MyWidget1):
|
||||
... class Media:
|
||||
... css = {
|
||||
... 'all': ('/path/to/css3','/path/to/css1')
|
||||
... }
|
||||
... js = ('/path/to/js1','/path/to/js4')
|
||||
|
||||
>>> w8 = MyWidget8()
|
||||
>>> print w8.media
|
||||
<link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/path/to/css3" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js1"></script>
|
||||
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
|
||||
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js4"></script>
|
||||
|
||||
# If a widget extends another but defines media, it extends the parents widget's media,
|
||||
# even if the parent defined media using a property.
|
||||
>>> class MyWidget9(MyWidget4):
|
||||
... class Media:
|
||||
... css = {
|
||||
... 'all': ('/other/path',)
|
||||
... }
|
||||
... js = ('/other/js',)
|
||||
|
||||
>>> w9 = MyWidget9()
|
||||
>>> print w9.media
|
||||
<link href="http://media.example.com/some/path" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/other/path" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/some/js"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/other/js"></script>
|
||||
|
||||
# A widget can disable media inheritance by specifying 'extend=False'
|
||||
>>> class MyWidget10(MyWidget1):
|
||||
... class Media:
|
||||
... extend = False
|
||||
... css = {
|
||||
... 'all': ('/path/to/css3','/path/to/css1')
|
||||
... }
|
||||
... js = ('/path/to/js1','/path/to/js4')
|
||||
|
||||
>>> w10 = MyWidget10()
|
||||
>>> print w10.media
|
||||
<link href="http://media.example.com/path/to/css3" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js1"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js4"></script>
|
||||
|
||||
# A widget can explicitly enable full media inheritance by specifying 'extend=True'
|
||||
>>> class MyWidget11(MyWidget1):
|
||||
... class Media:
|
||||
... extend = True
|
||||
... css = {
|
||||
... 'all': ('/path/to/css3','/path/to/css1')
|
||||
... }
|
||||
... js = ('/path/to/js1','/path/to/js4')
|
||||
|
||||
>>> w11 = MyWidget11()
|
||||
>>> print w11.media
|
||||
<link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/path/to/css3" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js1"></script>
|
||||
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
|
||||
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js4"></script>
|
||||
|
||||
# A widget can enable inheritance of one media type by specifying extend as a tuple
|
||||
>>> class MyWidget12(MyWidget1):
|
||||
... class Media:
|
||||
... extend = ('css',)
|
||||
... css = {
|
||||
... 'all': ('/path/to/css3','/path/to/css1')
|
||||
... }
|
||||
... js = ('/path/to/js1','/path/to/js4')
|
||||
|
||||
>>> w12 = MyWidget12()
|
||||
>>> print w12.media
|
||||
<link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/path/to/css3" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js1"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js4"></script>
|
||||
|
||||
###############################################################
|
||||
# Multi-media handling for CSS
|
||||
###############################################################
|
||||
|
||||
# A widget can define CSS media for multiple output media types
|
||||
>>> class MultimediaWidget(TextInput):
|
||||
... class Media:
|
||||
... css = {
|
||||
... 'screen, print': ('/file1','/file2'),
|
||||
... 'screen': ('/file3',),
|
||||
... 'print': ('/file4',)
|
||||
... }
|
||||
... js = ('/path/to/js1','/path/to/js4')
|
||||
|
||||
>>> multimedia = MultimediaWidget()
|
||||
>>> print multimedia.media
|
||||
<link href="http://media.example.com/file4" type="text/css" media="print" rel="stylesheet" />
|
||||
<link href="http://media.example.com/file3" type="text/css" media="screen" rel="stylesheet" />
|
||||
<link href="http://media.example.com/file1" type="text/css" media="screen, print" rel="stylesheet" />
|
||||
<link href="http://media.example.com/file2" type="text/css" media="screen, print" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js1"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js4"></script>
|
||||
|
||||
###############################################################
|
||||
# Multiwidget media handling
|
||||
###############################################################
|
||||
|
||||
# MultiWidgets have a default media definition that gets all the
|
||||
# media from the component widgets
|
||||
>>> class MyMultiWidget(MultiWidget):
|
||||
... def __init__(self, attrs=None):
|
||||
... widgets = [MyWidget1, MyWidget2, MyWidget3]
|
||||
... super(MyMultiWidget, self).__init__(widgets, attrs)
|
||||
|
||||
>>> mymulti = MyMultiWidget()
|
||||
>>> print mymulti.media
|
||||
<link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/path/to/css3" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js1"></script>
|
||||
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
|
||||
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js4"></script>
|
||||
|
||||
###############################################################
|
||||
# Media processing for forms
|
||||
###############################################################
|
||||
|
||||
# You can ask a form for the media required by its widgets.
|
||||
>>> class MyForm(Form):
|
||||
... field1 = CharField(max_length=20, widget=MyWidget1())
|
||||
... field2 = CharField(max_length=20, widget=MyWidget2())
|
||||
>>> f1 = MyForm()
|
||||
>>> print f1.media
|
||||
<link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/path/to/css3" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js1"></script>
|
||||
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
|
||||
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js4"></script>
|
||||
|
||||
# Form media can be combined to produce a single media definition.
|
||||
>>> class AnotherForm(Form):
|
||||
... field3 = CharField(max_length=20, widget=MyWidget3())
|
||||
>>> f2 = AnotherForm()
|
||||
>>> print f1.media + f2.media
|
||||
<link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/path/to/css3" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js1"></script>
|
||||
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
|
||||
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js4"></script>
|
||||
|
||||
# Forms can also define media, following the same rules as widgets.
|
||||
>>> class FormWithMedia(Form):
|
||||
... field1 = CharField(max_length=20, widget=MyWidget1())
|
||||
... field2 = CharField(max_length=20, widget=MyWidget2())
|
||||
... class Media:
|
||||
... js = ('/some/form/javascript',)
|
||||
... css = {
|
||||
... 'all': ('/some/form/css',)
|
||||
... }
|
||||
>>> f3 = FormWithMedia()
|
||||
>>> print f3.media
|
||||
<link href="http://media.example.com/path/to/css1" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/path/to/css2" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/path/to/css3" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/some/form/css" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js1"></script>
|
||||
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
|
||||
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/path/to/js4"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/some/form/javascript"></script>
|
||||
|
||||
"""
|
@ -2,6 +2,7 @@
|
||||
from localflavor import localflavor_tests
|
||||
from regressions import regression_tests
|
||||
from formsets import formset_tests
|
||||
from media import media_tests
|
||||
|
||||
form_tests = r"""
|
||||
>>> from django.newforms import *
|
||||
@ -3804,6 +3805,7 @@ __test__ = {
|
||||
'localflavor': localflavor_tests,
|
||||
'regressions': regression_tests,
|
||||
'formset_tests': formset_tests,
|
||||
'media_tests': media_tests,
|
||||
}
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
Loading…
x
Reference in New Issue
Block a user