| Form 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 FancyCalendarWidget(CalendarWidget): |
| ... 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 widget 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. |
| |
| .. _form-media-paths: |
| |
| Paths in media definitions |
| -------------------------- |
| |
| .. versionchanged:: 1.3 |
| |
| 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 |
| the appropriate prefix. |
| |
| As part of the introduction of the |
| :doc:`staticfiles app </ref/contrib/staticfiles>` two new settings were added |
| to refer to "static files" (images, CSS, Javascript, etc.) that are needed |
| to render a complete web page: :setting:`STATIC_URL` and :setting:`STATIC_ROOT`. |
| |
| To find the appropriate prefix to use, Django will check if the |
| :setting:`STATIC_URL` setting is not ``None`` and automatically fall back |
| to using :setting:`MEDIA_URL`. For example, if the :setting:`MEDIA_URL` for |
| your site was ``'http://uploads.example.com/'`` and :setting:`STATIC_URL` |
| was ``None``:: |
| |
| >>> class CalendarWidget(forms.TextInput): |
| ... class Media: |
| ... css = { |
| ... 'all': ('/css/pretty.css',), |
| ... } |
| ... js = ('animations.js', 'http://othersite.com/actions.js') |
| |
| >>> w = CalendarWidget() |
| >>> print w.media |
| <link href="/css/pretty.css" type="text/css" media="all" rel="stylesheet" /> |
| <script type="text/javascript" src="http://uploads.example.com/animations.js"></script> |
| <script type="text/javascript" src="http://othersite.com/actions.js"></script> |
| |
| But if :setting:`STATIC_URL` is ``'http://static.example.com/'``:: |
| |
| >>> w = CalendarWidget() |
| >>> print w.media |
| <link href="/css/pretty.css" type="text/css" media="all" rel="stylesheet" /> |
| <script type="text/javascript" src="http://static.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.media + w2.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> |
| |
| 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> |