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