mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			358 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			358 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| =================================
 | |
| Form Assets (the ``Media`` class)
 | |
| =================================
 | |
| 
 | |
| 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 asset definitions come in. Django allows you to
 | |
| associate different files -- like stylesheets and scripts -- with the
 | |
| forms and widgets that require those assets. 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:: Assets and Django Admin
 | |
| 
 | |
|     The Django Admin application defines a number of customized
 | |
|     widgets for calendars, filtered selections, and so on. These
 | |
|     widgets define asset requirements, and the Django Admin uses the
 | |
|     custom widgets in place of the Django defaults. The Admin
 | |
|     templates will only include those 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.
 | |
| 
 | |
| .. _assets-as-a-static-definition:
 | |
| 
 | |
| Assets as a static definition
 | |
| =============================
 | |
| 
 | |
| The easiest way to define assets is as a static definition. Using this
 | |
| method, the declaration is an inner ``Media`` class. The properties of the
 | |
| inner class define the requirements.
 | |
| 
 | |
| Here's a simple example::
 | |
| 
 | |
|     from django import forms
 | |
| 
 | |
|     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 definition is converted at runtime into a widget property
 | |
| named ``media``. The list of assets for a ``CalendarWidget`` instance
 | |
| can be retrieved through this property::
 | |
| 
 | |
|     >>> w = CalendarWidget()
 | |
|     >>> print(w.media)
 | |
|     <link href="http://static.example.com/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://static.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
 | |
| :ref:`the section on paths <form-asset-paths>` for details of how to
 | |
| specify paths to these files.
 | |
| 
 | |
| 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://static.example.com/pretty.css" type="text/css" media="screen" rel="stylesheet" />
 | |
|     <link href="http://static.example.com/lo_res.css" type="text/css" media="tv,projector" rel="stylesheet" />
 | |
|     <link href="http://static.example.com/newspaper.css" type="text/css" media="print" rel="stylesheet" />
 | |
| 
 | |
| ``js``
 | |
| ------
 | |
| 
 | |
| A tuple describing the required JavaScript files. See :ref:`the
 | |
| section on paths <form-asset-paths>` for details of how to specify
 | |
| paths to these files.
 | |
| 
 | |
| ``extend``
 | |
| ----------
 | |
| 
 | |
| A boolean defining inheritance behavior for ``Media`` declarations.
 | |
| 
 | |
| By default, any object using a static ``Media`` definition will
 | |
| inherit all the assets associated with the parent widget. This occurs
 | |
| regardless of how the parent defines its own 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://static.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" />
 | |
|     <link href="http://static.example.com/fancy.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://static.example.com/actions.js"></script>
 | |
|     <script type="text/javascript" src="http://static.example.com/whizbang.js"></script>
 | |
| 
 | |
| The FancyCalendar widget inherits all the assets from its 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://static.example.com/fancy.css" type="text/css" media="all" rel="stylesheet" />
 | |
|     <script type="text/javascript" src="http://static.example.com/whizbang.js"></script>
 | |
| 
 | |
| If you require even more control over inheritance, define your assets using a
 | |
| :ref:`dynamic property <dynamic-property>`. Dynamic properties give you
 | |
| complete control over which files are inherited, and which are not.
 | |
| 
 | |
| .. _dynamic-property:
 | |
| 
 | |
| ``Media`` as a dynamic property
 | |
| ===============================
 | |
| 
 | |
| If you need to perform some more sophisticated manipulation of asset
 | |
| 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 definition for our Calendar Widget could also
 | |
| be defined in a dynamic fashion::
 | |
| 
 | |
|     class CalendarWidget(forms.TextInput):
 | |
|         @property
 | |
|         def media(self):
 | |
|             return forms.Media(css={'all': ('pretty.css',)},
 | |
|                                js=('animations.js', 'actions.js'))
 | |
| 
 | |
| See the section on `Media objects`_ for more details on how to construct
 | |
| return values for dynamic ``media`` properties.
 | |
| 
 | |
| .. _form-asset-paths:
 | |
| 
 | |
| Paths in asset definitions
 | |
| ==========================
 | |
| 
 | |
| Paths used to specify assets 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. If the
 | |
| :mod:`django.contrib.staticfiles` app is installed, it will be used to serve
 | |
| assets.
 | |
| 
 | |
| Whether or not you use :mod:`django.contrib.staticfiles`,  the
 | |
| :setting:`STATIC_URL` and :setting:`STATIC_ROOT` settings are required to
 | |
| render a complete web page.
 | |
| 
 | |
| 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``::
 | |
| 
 | |
|     >>> from django import forms
 | |
|     >>> 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>
 | |
| 
 | |
| Or if :mod:`~django.contrib.staticfiles` is configured using the
 | |
| `~django.contib.staticfiles.ManifestStaticFilesStorage`::
 | |
| 
 | |
|     >>> w = CalendarWidget()
 | |
|     >>> print(w.media)
 | |
|     <link href="/css/pretty.css" type="text/css" media="all" rel="stylesheet" />
 | |
|     <script type="text/javascript" src="https://static.example.com/animations.27e20196a850.js"></script>
 | |
|     <script type="text/javascript" src="http://othersite.com/actions.js"></script>
 | |
| 
 | |
| .. versionchanged:: 1.10
 | |
| 
 | |
|     Older versions didn't serve assets using :mod:`django.contrib.staticfiles`.
 | |
| 
 | |
| ``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 the relevant files in the ``<head>`` block of your
 | |
| HTML page.
 | |
| 
 | |
| However, ``Media`` objects have some other interesting properties.
 | |
| 
 | |
| Subsets of assets
 | |
| -----------------
 | |
| 
 | |
| If you only want files 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://static.example.com/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://static.example.com/actions.js"></script>
 | |
| 
 | |
|     >>> print(w.media['css'])
 | |
|     <link href="http://static.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 assets
 | |
| specified by both::
 | |
| 
 | |
|     >>> from django import forms
 | |
|     >>> 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://static.example.com/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://static.example.com/actions.js"></script>
 | |
|     <script type="text/javascript" src="http://static.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::
 | |
| 
 | |
|     >>> from django import forms
 | |
|     >>> class ContactForm(forms.Form):
 | |
|     ...     date = DateField(widget=CalendarWidget)
 | |
|     ...     name = CharField(max_length=40, widget=OtherWidget)
 | |
| 
 | |
|     >>> f = ContactForm()
 | |
|     >>> f.media
 | |
|     <link href="http://static.example.com/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://static.example.com/actions.js"></script>
 | |
|     <script type="text/javascript" src="http://static.example.com/whizbang.js"></script>
 | |
| 
 | |
| If you want to associate additional assets 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://static.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" />
 | |
|     <link href="http://static.example.com/layout.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://static.example.com/actions.js"></script>
 | |
|     <script type="text/javascript" src="http://static.example.com/whizbang.js"></script>
 |