media.txt 16 KB

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