1. =================================
    
  2. Form Assets (the ``Media`` class)
    
  3. =================================
    
  4. 
    
  5. Rendering an attractive and easy-to-use web form requires more than just
    
  6. HTML - it also requires CSS stylesheets, and if you want to use fancy widgets,
    
  7. you may also need to include some JavaScript on each page. The exact
    
  8. combination of CSS and JavaScript that is required for any given page will
    
  9. depend upon the widgets that are in use on that page.
    
  10. 
    
  11. This is where asset definitions come in. Django allows you to
    
  12. associate different files -- like stylesheets and scripts -- with the
    
  13. forms and widgets that require those assets. For example, if you want
    
  14. to use a calendar to render DateFields, you can define a custom
    
  15. Calendar widget. This widget can then be associated with the CSS and
    
  16. JavaScript that is required to render the calendar. When the Calendar
    
  17. widget is used on a form, Django is able to identify the CSS and
    
  18. JavaScript files that are required, and provide the list of file names
    
  19. in a form suitable for inclusion on your web page.
    
  20. 
    
  21. .. admonition:: Assets and Django Admin
    
  22. 
    
  23.     The Django Admin application defines a number of customized
    
  24.     widgets for calendars, filtered selections, and so on. These
    
  25.     widgets define asset requirements, and the Django Admin uses the
    
  26.     custom widgets in place of the Django defaults. The Admin
    
  27.     templates will only include those files that are required to
    
  28.     render the widgets on any given page.
    
  29. 
    
  30.     If you like the widgets that the Django Admin application uses,
    
  31.     feel free to use them in your own application! They're all stored
    
  32.     in ``django.contrib.admin.widgets``.
    
  33. 
    
  34. .. admonition:: Which JavaScript toolkit?
    
  35. 
    
  36.     Many JavaScript toolkits exist, and many of them include widgets (such
    
  37.     as calendar widgets) that can be used to enhance your application.
    
  38.     Django has deliberately avoided blessing any one JavaScript toolkit.
    
  39.     Each toolkit has its own relative strengths and weaknesses - use
    
  40.     whichever toolkit suits your requirements. Django is able to integrate
    
  41.     with any JavaScript toolkit.
    
  42. 
    
  43. .. _assets-as-a-static-definition:
    
  44. 
    
  45. Assets as a static definition
    
  46. =============================
    
  47. 
    
  48. The easiest way to define assets is as a static definition. Using this
    
  49. method, the declaration is an inner ``Media`` class. The properties of the
    
  50. inner class define the requirements.
    
  51. 
    
  52. Here's an example::
    
  53. 
    
  54.     from django import forms
    
  55. 
    
  56.     class CalendarWidget(forms.TextInput):
    
  57.         class Media:
    
  58.             css = {
    
  59.                 'all': ('pretty.css',)
    
  60.             }
    
  61.             js = ('animations.js', 'actions.js')
    
  62. 
    
  63. This code defines a ``CalendarWidget``, which will be based on ``TextInput``.
    
  64. Every time the CalendarWidget is used on a form, that form will be directed
    
  65. to include the CSS file ``pretty.css``, and the JavaScript files
    
  66. ``animations.js`` and ``actions.js``.
    
  67. 
    
  68. This static definition is converted at runtime into a widget property
    
  69. named ``media``. The list of assets for a ``CalendarWidget`` instance
    
  70. can be retrieved through this property::
    
  71. 
    
  72.     >>> w = CalendarWidget()
    
  73.     >>> print(w.media)
    
  74.     <link href="http://static.example.com/pretty.css" media="all" rel="stylesheet">
    
  75.     <script src="http://static.example.com/animations.js"></script>
    
  76.     <script src="http://static.example.com/actions.js"></script>
    
  77. 
    
  78. Here's a list of all possible ``Media`` options. There are no required options.
    
  79. 
    
  80. ``css``
    
  81. -------
    
  82. 
    
  83. A dictionary describing the CSS files required for various forms of output
    
  84. media.
    
  85. 
    
  86. The values in the dictionary should be a tuple/list of file names. See
    
  87. :ref:`the section on paths <form-asset-paths>` for details of how to
    
  88. specify paths to these files.
    
  89. 
    
  90. The keys in the dictionary are the output media types. These are the same
    
  91. types accepted by CSS files in media declarations: 'all', 'aural', 'braille',
    
  92. 'embossed', 'handheld', 'print', 'projection', 'screen', 'tty' and 'tv'. If
    
  93. you need to have different stylesheets for different media types, provide
    
  94. a list of CSS files for each output medium. The following example would
    
  95. provide two CSS options -- one for the screen, and one for print::
    
  96. 
    
  97.     class Media:
    
  98.         css = {
    
  99.             'screen': ('pretty.css',),
    
  100.             'print': ('newspaper.css',)
    
  101.         }
    
  102. 
    
  103. If a group of CSS files are appropriate for multiple output media types,
    
  104. the dictionary key can be a comma separated list of output media types.
    
  105. In the following example, TV's and projectors will have the same media
    
  106. requirements::
    
  107. 
    
  108.     class Media:
    
  109.         css = {
    
  110.             'screen': ('pretty.css',),
    
  111.             'tv,projector': ('lo_res.css',),
    
  112.             'print': ('newspaper.css',)
    
  113.         }
    
  114. 
    
  115. If this last CSS definition were to be rendered, it would become the following HTML::
    
  116. 
    
  117.     <link href="http://static.example.com/pretty.css" media="screen" rel="stylesheet">
    
  118.     <link href="http://static.example.com/lo_res.css" media="tv,projector" rel="stylesheet">
    
  119.     <link href="http://static.example.com/newspaper.css" media="print" rel="stylesheet">
    
  120. 
    
  121. .. versionchanged:: 4.1
    
  122. 
    
  123.     In older versions, the ``type="text/css"`` attribute is included in CSS
    
  124.     links.
    
  125. 
    
  126. ``js``
    
  127. ------
    
  128. 
    
  129. A tuple describing the required JavaScript files. See :ref:`the
    
  130. section on paths <form-asset-paths>` for details of how to specify
    
  131. paths to these files.
    
  132. 
    
  133. ``extend``
    
  134. ----------
    
  135. 
    
  136. A boolean defining inheritance behavior for ``Media`` declarations.
    
  137. 
    
  138. By default, any object using a static ``Media`` definition will
    
  139. inherit all the assets associated with the parent widget. This occurs
    
  140. regardless of how the parent defines its own requirements. For
    
  141. example, if we were to extend our basic Calendar widget from the
    
  142. example above::
    
  143. 
    
  144.     >>> class FancyCalendarWidget(CalendarWidget):
    
  145.     ...     class Media:
    
  146.     ...         css = {
    
  147.     ...             'all': ('fancy.css',)
    
  148.     ...         }
    
  149.     ...         js = ('whizbang.js',)
    
  150. 
    
  151.     >>> w = FancyCalendarWidget()
    
  152.     >>> print(w.media)
    
  153.     <link href="http://static.example.com/pretty.css" media="all" rel="stylesheet">
    
  154.     <link href="http://static.example.com/fancy.css" media="all" rel="stylesheet">
    
  155.     <script src="http://static.example.com/animations.js"></script>
    
  156.     <script src="http://static.example.com/actions.js"></script>
    
  157.     <script src="http://static.example.com/whizbang.js"></script>
    
  158. 
    
  159. The FancyCalendar widget inherits all the assets from its parent
    
  160. widget. If you don't want ``Media`` to be inherited in this way, add
    
  161. an ``extend=False`` declaration to the ``Media`` declaration::
    
  162. 
    
  163.     >>> class FancyCalendarWidget(CalendarWidget):
    
  164.     ...     class Media:
    
  165.     ...         extend = False
    
  166.     ...         css = {
    
  167.     ...             'all': ('fancy.css',)
    
  168.     ...         }
    
  169.     ...         js = ('whizbang.js',)
    
  170. 
    
  171.     >>> w = FancyCalendarWidget()
    
  172.     >>> print(w.media)
    
  173.     <link href="http://static.example.com/fancy.css" media="all" rel="stylesheet">
    
  174.     <script src="http://static.example.com/whizbang.js"></script>
    
  175. 
    
  176. If you require even more control over inheritance, define your assets using a
    
  177. :ref:`dynamic property <dynamic-property>`. Dynamic properties give you
    
  178. complete control over which files are inherited, and which are not.
    
  179. 
    
  180. .. _dynamic-property:
    
  181. 
    
  182. ``Media`` as a dynamic property
    
  183. ===============================
    
  184. 
    
  185. If you need to perform some more sophisticated manipulation of asset
    
  186. requirements, you can define the ``media`` property directly. This is
    
  187. done by defining a widget property that returns an instance of
    
  188. ``forms.Media``.  The constructor for ``forms.Media`` accepts ``css``
    
  189. and ``js`` keyword arguments in the same format as that used in a
    
  190. static media definition.
    
  191. 
    
  192. For example, the static definition for our Calendar Widget could also
    
  193. be defined in a dynamic fashion::
    
  194. 
    
  195.     class CalendarWidget(forms.TextInput):
    
  196.         @property
    
  197.         def media(self):
    
  198.             return forms.Media(css={'all': ('pretty.css',)},
    
  199.                                js=('animations.js', 'actions.js'))
    
  200. 
    
  201. See the section on `Media objects`_ for more details on how to construct
    
  202. return values for dynamic ``media`` properties.
    
  203. 
    
  204. .. _form-asset-paths:
    
  205. 
    
  206. Paths in asset definitions
    
  207. ==========================
    
  208. 
    
  209. Paths as strings
    
  210. ----------------
    
  211. 
    
  212. String paths used to specify assets can be either relative or absolute. If a
    
  213. path starts with ``/``, ``http://`` or ``https://``, it will be
    
  214. interpreted as an absolute path, and left as-is. All other paths will
    
  215. be prepended with the value of the appropriate prefix. If the
    
  216. :mod:`django.contrib.staticfiles` app is installed, it will be used to serve
    
  217. assets.
    
  218. 
    
  219. Whether or not you use :mod:`django.contrib.staticfiles`,  the
    
  220. :setting:`STATIC_URL` and :setting:`STATIC_ROOT` settings are required to
    
  221. render a complete web page.
    
  222. 
    
  223. To find the appropriate prefix to use, Django will check if the
    
  224. :setting:`STATIC_URL` setting is not ``None`` and automatically fall back
    
  225. to using :setting:`MEDIA_URL`. For example, if the :setting:`MEDIA_URL` for
    
  226. your site was ``'http://uploads.example.com/'`` and :setting:`STATIC_URL`
    
  227. was ``None``::
    
  228. 
    
  229.     >>> from django import forms
    
  230.     >>> class CalendarWidget(forms.TextInput):
    
  231.     ...     class Media:
    
  232.     ...         css = {
    
  233.     ...             'all': ('/css/pretty.css',),
    
  234.     ...         }
    
  235.     ...         js = ('animations.js', 'http://othersite.com/actions.js')
    
  236. 
    
  237.     >>> w = CalendarWidget()
    
  238.     >>> print(w.media)
    
  239.     <link href="/css/pretty.css" media="all" rel="stylesheet">
    
  240.     <script src="http://uploads.example.com/animations.js"></script>
    
  241.     <script src="http://othersite.com/actions.js"></script>
    
  242. 
    
  243. But if :setting:`STATIC_URL` is ``'http://static.example.com/'``::
    
  244. 
    
  245.     >>> w = CalendarWidget()
    
  246.     >>> print(w.media)
    
  247.     <link href="/css/pretty.css" media="all" rel="stylesheet">
    
  248.     <script src="http://static.example.com/animations.js"></script>
    
  249.     <script src="http://othersite.com/actions.js"></script>
    
  250. 
    
  251. Or if :mod:`~django.contrib.staticfiles` is configured using the
    
  252. :class:`~django.contrib.staticfiles.storage.ManifestStaticFilesStorage`::
    
  253. 
    
  254.     >>> w = CalendarWidget()
    
  255.     >>> print(w.media)
    
  256.     <link href="/css/pretty.css" media="all" rel="stylesheet">
    
  257.     <script src="https://static.example.com/animations.27e20196a850.js"></script>
    
  258.     <script src="http://othersite.com/actions.js"></script>
    
  259. 
    
  260. Paths as objects
    
  261. ----------------
    
  262. 
    
  263. .. versionadded:: 4.1
    
  264. 
    
  265. Asset paths may also be given as hashable objects implementing an
    
  266. ``__html__()`` method. The ``__html__()`` method is typically added using the
    
  267. :func:`~django.utils.html.html_safe` decorator. The object is responsible for
    
  268. outputting the complete HTML ``<script>`` or ``<link>`` tag content::
    
  269. 
    
  270.     >>> from django import forms
    
  271.     >>> from django.utils.html import html_safe
    
  272.     >>>
    
  273.     >>> @html_safe
    
  274.     >>> class JSPath:
    
  275.     ...     def __str__(self):
    
  276.     ...         return '<script src="https://example.org/asset.js" rel="stylesheet">'
    
  277. 
    
  278.     >>> class SomeWidget(forms.TextInput):
    
  279.     ...     class Media:
    
  280.     ...         js = (JSPath(),)
    
  281. 
    
  282. ``Media`` objects
    
  283. =================
    
  284. 
    
  285. When you interrogate the ``media`` attribute of a widget or form, the
    
  286. value that is returned is a ``forms.Media`` object. As we have already
    
  287. seen, the string representation of a ``Media`` object is the HTML
    
  288. required to include the relevant files in the ``<head>`` block of your
    
  289. HTML page.
    
  290. 
    
  291. However, ``Media`` objects have some other interesting properties.
    
  292. 
    
  293. Subsets of assets
    
  294. -----------------
    
  295. 
    
  296. If you only want files of a particular type, you can use the subscript
    
  297. operator to filter out a medium of interest. For example::
    
  298. 
    
  299.     >>> w = CalendarWidget()
    
  300.     >>> print(w.media)
    
  301.     <link href="http://static.example.com/pretty.css" media="all" rel="stylesheet">
    
  302.     <script src="http://static.example.com/animations.js"></script>
    
  303.     <script src="http://static.example.com/actions.js"></script>
    
  304. 
    
  305.     >>> print(w.media['css'])
    
  306.     <link href="http://static.example.com/pretty.css" media="all" rel="stylesheet">
    
  307. 
    
  308. When you use the subscript operator, the value that is returned is a
    
  309. new ``Media`` object -- but one that only contains the media of interest.
    
  310. 
    
  311. Combining ``Media`` objects
    
  312. ---------------------------
    
  313. 
    
  314. ``Media`` objects can also be added together. When two ``Media`` objects are
    
  315. added, the resulting ``Media`` object contains the union of the assets
    
  316. specified by both::
    
  317. 
    
  318.     >>> from django import forms
    
  319.     >>> class CalendarWidget(forms.TextInput):
    
  320.     ...     class Media:
    
  321.     ...         css = {
    
  322.     ...             'all': ('pretty.css',)
    
  323.     ...         }
    
  324.     ...         js = ('animations.js', 'actions.js')
    
  325. 
    
  326.     >>> class OtherWidget(forms.TextInput):
    
  327.     ...     class Media:
    
  328.     ...         js = ('whizbang.js',)
    
  329. 
    
  330.     >>> w1 = CalendarWidget()
    
  331.     >>> w2 = OtherWidget()
    
  332.     >>> print(w1.media + w2.media)
    
  333.     <link href="http://static.example.com/pretty.css" media="all" rel="stylesheet">
    
  334.     <script src="http://static.example.com/animations.js"></script>
    
  335.     <script src="http://static.example.com/actions.js"></script>
    
  336.     <script src="http://static.example.com/whizbang.js"></script>
    
  337. 
    
  338. .. _form-media-asset-order:
    
  339. 
    
  340. Order of assets
    
  341. ---------------
    
  342. 
    
  343. The order in which assets are inserted into the DOM is often important. For
    
  344. example, you may have a script that depends on jQuery. Therefore, combining
    
  345. ``Media`` objects attempts to preserve the relative order in which assets are
    
  346. defined in each ``Media`` class.
    
  347. 
    
  348. For example::
    
  349. 
    
  350.     >>> from django import forms
    
  351.     >>> class CalendarWidget(forms.TextInput):
    
  352.     ...     class Media:
    
  353.     ...         js = ('jQuery.js', 'calendar.js', 'noConflict.js')
    
  354.     >>> class TimeWidget(forms.TextInput):
    
  355.     ...     class Media:
    
  356.     ...         js = ('jQuery.js', 'time.js', 'noConflict.js')
    
  357.     >>> w1 = CalendarWidget()
    
  358.     >>> w2 = TimeWidget()
    
  359.     >>> print(w1.media + w2.media)
    
  360.     <script src="http://static.example.com/jQuery.js"></script>
    
  361.     <script src="http://static.example.com/calendar.js"></script>
    
  362.     <script src="http://static.example.com/time.js"></script>
    
  363.     <script src="http://static.example.com/noConflict.js"></script>
    
  364. 
    
  365. Combining ``Media`` objects with assets in a conflicting order results in a
    
  366. ``MediaOrderConflictWarning``.
    
  367. 
    
  368. ``Media`` on Forms
    
  369. ==================
    
  370. 
    
  371. Widgets aren't the only objects that can have ``media`` definitions --
    
  372. forms can also define ``media``. The rules for ``media`` definitions
    
  373. on forms are the same as the rules for widgets: declarations can be
    
  374. static or dynamic; path and inheritance rules for those declarations
    
  375. are exactly the same.
    
  376. 
    
  377. Regardless of whether you define a ``media`` declaration, *all* Form
    
  378. objects have a ``media`` property. The default value for this property
    
  379. is the result of adding the ``media`` definitions for all widgets that
    
  380. are part of the form::
    
  381. 
    
  382.     >>> from django import forms
    
  383.     >>> class ContactForm(forms.Form):
    
  384.     ...     date = DateField(widget=CalendarWidget)
    
  385.     ...     name = CharField(max_length=40, widget=OtherWidget)
    
  386. 
    
  387.     >>> f = ContactForm()
    
  388.     >>> f.media
    
  389.     <link href="http://static.example.com/pretty.css" media="all" rel="stylesheet">
    
  390.     <script src="http://static.example.com/animations.js"></script>
    
  391.     <script src="http://static.example.com/actions.js"></script>
    
  392.     <script src="http://static.example.com/whizbang.js"></script>
    
  393. 
    
  394. If you want to associate additional assets with a form -- for example,
    
  395. CSS for form layout -- add a ``Media`` declaration to the form::
    
  396. 
    
  397.     >>> class ContactForm(forms.Form):
    
  398.     ...     date = DateField(widget=CalendarWidget)
    
  399.     ...     name = CharField(max_length=40, widget=OtherWidget)
    
  400.     ...
    
  401.     ...     class Media:
    
  402.     ...         css = {
    
  403.     ...             'all': ('layout.css',)
    
  404.     ...         }
    
  405. 
    
  406.     >>> f = ContactForm()
    
  407.     >>> f.media
    
  408.     <link href="http://static.example.com/pretty.css" media="all" rel="stylesheet">
    
  409.     <link href="http://static.example.com/layout.css" media="all" rel="stylesheet">
    
  410.     <script src="http://static.example.com/animations.js"></script>
    
  411.     <script src="http://static.example.com/actions.js"></script>
    
  412.     <script src="http://static.example.com/whizbang.js"></script>