PageRenderTime 20ms CodeModel.GetById 13ms app.highlight 2ms RepoModel.GetById 1ms app.codeStats 0ms

/docs/intro/tutorial02.txt

https://code.google.com/p/mango-py/
Plain Text | 462 lines | 330 code | 132 blank | 0 comment | 0 complexity | f24211b053969a62e3302fa6d3d01fff MD5 | raw file
  1=====================================
  2Writing your first Django app, part 2
  3=====================================
  4
  5This tutorial begins where :doc:`Tutorial 1 </intro/tutorial01>` left off. We're
  6continuing the Web-poll application and will focus on Django's
  7automatically-generated admin site.
  8
  9.. admonition:: Philosophy
 10
 11    Generating admin sites for your staff or clients to add, change and delete
 12    content is tedious work that doesn't require much creativity. For that
 13    reason, Django entirely automates creation of admin interfaces for models.
 14
 15    Django was written in a newsroom environment, with a very clear separation
 16    between "content publishers" and the "public" site. Site managers use the
 17    system to add news stories, events, sports scores, etc., and that content is
 18    displayed on the public site. Django solves the problem of creating a
 19    unified interface for site administrators to edit content.
 20
 21    The admin isn't necessarily intended to be used by site visitors; it's for
 22    site managers.
 23
 24Activate the admin site
 25=======================
 26
 27The Django admin site is not activated by default -- it's an opt-in thing. To
 28activate the admin site for your installation, do these three things:
 29
 30    * Add ``"django.contrib.admin"`` to your :setting:`INSTALLED_APPS` setting.
 31
 32    * Run ``python manage.py syncdb``. Since you have added a new application
 33      to :setting:`INSTALLED_APPS`, the database tables need to be updated.
 34
 35    * Edit your ``mysite/urls.py`` file and uncomment the lines that reference
 36      the admin -- there are three lines in total to uncomment. This file is a
 37      URLconf; we'll dig into URLconfs in the next tutorial. For now, all you
 38      need to know is that it maps URL roots to applications. In the end, you
 39      should have a ``urls.py`` file that looks like this:
 40
 41      .. parsed-literal::
 42
 43          from django.conf.urls.defaults import *
 44
 45          # Uncomment the next two lines to enable the admin:
 46          **from django.contrib import admin**
 47          **admin.autodiscover()**
 48
 49          urlpatterns = patterns('',
 50              # Example:
 51              # (r'^mysite/', include('mysite.foo.urls')),
 52
 53              # Uncomment the admin/doc line below and add 'django.contrib.admindocs'
 54              # to INSTALLED_APPS to enable admin documentation:
 55              # (r'^admin/doc/', include('django.contrib.admindocs.urls')),
 56
 57              # Uncomment the next line to enable the admin:
 58              **(r'^admin/', include(admin.site.urls)),**
 59          )
 60
 61      (The bold lines are the ones that needed to be uncommented.)
 62
 63Start the development server
 64============================
 65
 66Let's start the development server and explore the admin site.
 67
 68Recall from Tutorial 1 that you start the development server like so:
 69
 70.. code-block:: bash
 71
 72    python manage.py runserver
 73
 74Now, open a Web browser and go to "/admin/" on your local domain -- e.g.,
 75http://127.0.0.1:8000/admin/. You should see the admin's login screen:
 76
 77.. image:: _images/admin01.png
 78   :alt: Django admin login screen
 79
 80Enter the admin site
 81====================
 82
 83Now, try logging in. (You created a superuser account in the first part of this
 84tutorial, remember?  If you didn't create one or forgot the password you can
 85:ref:`create another one <topics-auth-creating-superusers>`.) You should see
 86the Django admin index page:
 87
 88.. image:: _images/admin02t.png
 89   :alt: Django admin index page
 90
 91You should see a few other types of editable content, including groups, users
 92and sites. These are core features Django ships with by default.
 93
 94Make the poll app modifiable in the admin
 95=========================================
 96
 97But where's our poll app? It's not displayed on the admin index page.
 98
 99Just one thing to do: We need to tell the admin that ``Poll``
100objects have an admin interface. To do this, create a file called
101``admin.py`` in your ``polls`` directory, and edit it to look like this::
102
103    from polls.models import Poll
104    from django.contrib import admin
105
106    admin.site.register(Poll)
107
108You'll need to restart the development server to see your changes. Normally,
109the server auto-reloads code every time you modify a file, but the action of
110creating a new file doesn't trigger the auto-reloading logic.
111
112Explore the free admin functionality
113====================================
114
115Now that we've registered ``Poll``, Django knows that it should be displayed on
116the admin index page:
117
118.. image:: _images/admin03t.png
119   :alt: Django admin index page, now with polls displayed
120
121Click "Polls." Now you're at the "change list" page for polls. This page
122displays all the polls in the database and lets you choose one to change it.
123There's the "What's up?" poll we created in the first tutorial:
124
125.. image:: _images/admin04t.png
126   :alt: Polls change list page
127
128Click the "What's up?" poll to edit it:
129
130.. image:: _images/admin05t.png
131   :alt: Editing form for poll object
132
133Things to note here:
134
135    * The form is automatically generated from the Poll model.
136
137    * The different model field types (:class:`~django.db.models.DateTimeField`,
138      :class:`~django.db.models.CharField`) correspond to the appropriate HTML
139      input widget. Each type of field knows how to display itself in the Django
140      admin.
141
142    * Each :class:`~django.db.models.DateTimeField` gets free JavaScript
143      shortcuts. Dates get a "Today" shortcut and calendar popup, and times get
144      a "Now" shortcut and a convenient popup that lists commonly entered times.
145
146The bottom part of the page gives you a couple of options:
147
148    * Save -- Saves changes and returns to the change-list page for this type of
149      object.
150
151    * Save and continue editing -- Saves changes and reloads the admin page for
152      this object.
153
154    * Save and add another -- Saves changes and loads a new, blank form for this
155      type of object.
156
157    * Delete -- Displays a delete confirmation page.
158
159Change the "Date published" by clicking the "Today" and "Now" shortcuts. Then
160click "Save and continue editing." Then click "History" in the upper right.
161You'll see a page listing all changes made to this object via the Django admin,
162with the timestamp and username of the person who made the change:
163
164.. image:: _images/admin06t.png
165   :alt: History page for poll object
166
167Customize the admin form
168========================
169
170Take a few minutes to marvel at all the code you didn't have to write. By
171registering the Poll model with ``admin.site.register(Poll)``, Django was able
172to construct a default form representation. Often, you'll want to customize how
173the admin form looks and works. You'll do this by telling Django the options
174you want when you register the object.
175
176Let's see how this works by re-ordering the fields on the edit form. Replace
177the ``admin.site.register(Poll)`` line with::
178
179    class PollAdmin(admin.ModelAdmin):
180        fields = ['pub_date', 'question']
181
182    admin.site.register(Poll, PollAdmin)
183
184You'll follow this pattern -- create a model admin object, then pass it as the
185second argument to ``admin.site.register()`` -- any time you need to change the
186admin options for an object.
187
188This particular change above makes the "Publication date" come before the
189"Question" field:
190
191.. image:: _images/admin07.png
192   :alt: Fields have been reordered
193
194This isn't impressive with only two fields, but for admin forms with dozens
195of fields, choosing an intuitive order is an important usability detail.
196
197And speaking of forms with dozens of fields, you might want to split the form
198up into fieldsets::
199
200    class PollAdmin(admin.ModelAdmin):
201        fieldsets = [
202            (None,               {'fields': ['question']}),
203            ('Date information', {'fields': ['pub_date']}),
204        ]
205
206    admin.site.register(Poll, PollAdmin)
207
208The first element of each tuple in ``fieldsets`` is the title of the fieldset.
209Here's what our form looks like now:
210
211.. image:: _images/admin08t.png
212   :alt: Form has fieldsets now
213
214You can assign arbitrary HTML classes to each fieldset. Django provides a
215``"collapse"`` class that displays a particular fieldset initially collapsed.
216This is useful when you have a long form that contains a number of fields that
217aren't commonly used::
218
219        class PollAdmin(admin.ModelAdmin):
220            fieldsets = [
221                (None,               {'fields': ['question']}),
222                ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
223            ]
224
225.. image:: _images/admin09.png
226   :alt: Fieldset is initially collapsed
227
228Adding related objects
229======================
230
231OK, we have our Poll admin page. But a ``Poll`` has multiple ``Choices``, and
232the admin page doesn't display choices.
233
234Yet.
235
236There are two ways to solve this problem. The first is to register ``Choice``
237with the admin just as we did with ``Poll``. That's easy::
238
239    from polls.models import Choice
240
241    admin.site.register(Choice)
242
243Now "Choices" is an available option in the Django admin. The "Add choice" form
244looks like this:
245
246.. image:: _images/admin10.png
247   :alt: Choice admin page
248
249In that form, the "Poll" field is a select box containing every poll in the
250database. Django knows that a :class:`~django.db.models.ForeignKey` should be
251represented in the admin as a ``<select>`` box. In our case, only one poll
252exists at this point.
253
254Also note the "Add Another" link next to "Poll." Every object with a
255``ForeignKey`` relationship to another gets this for free. When you click "Add
256Another," you'll get a popup window with the "Add poll" form. If you add a poll
257in that window and click "Save," Django will save the poll to the database and
258dynamically add it as the selected choice on the "Add choice" form you're
259looking at.
260
261But, really, this is an inefficient way of adding Choice objects to the system.
262It'd be better if you could add a bunch of Choices directly when you create the
263Poll object. Let's make that happen.
264
265Remove the ``register()`` call for the Choice model. Then, edit the ``Poll``
266registration code to read::
267
268    class ChoiceInline(admin.StackedInline):
269        model = Choice
270        extra = 3
271
272    class PollAdmin(admin.ModelAdmin):
273        fieldsets = [
274            (None,               {'fields': ['question']}),
275            ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
276        ]
277        inlines = [ChoiceInline]
278
279    admin.site.register(Poll, PollAdmin)
280
281This tells Django: "Choice objects are edited on the Poll admin page. By
282default, provide enough fields for 3 choices."
283
284Load the "Add poll" page to see how that looks, you may need to restart your development server:
285
286.. image:: _images/admin11t.png
287   :alt: Add poll page now has choices on it
288
289It works like this: There are three slots for related Choices -- as specified
290by ``extra`` -- and each time you come back to the "Change" page for an
291already-created object, you get another three extra slots.
292
293One small problem, though. It takes a lot of screen space to display all the
294fields for entering related Choice objects. For that reason, Django offers a
295tabular way of displaying inline related objects; you just need to change
296the ``ChoiceInline`` declaration to read::
297
298    class ChoiceInline(admin.TabularInline):
299        #...
300
301With that ``TabularInline`` (instead of ``StackedInline``), the
302related objects are displayed in a more compact, table-based format:
303
304.. image:: _images/admin12.png
305   :alt: Add poll page now has more compact choices
306
307Customize the admin change list
308===============================
309
310Now that the Poll admin page is looking good, let's make some tweaks to the
311"change list" page -- the one that displays all the polls in the system.
312
313Here's what it looks like at this point:
314
315.. image:: _images/admin04t.png
316   :alt: Polls change list page
317
318By default, Django displays the ``str()`` of each object. But sometimes it'd be
319more helpful if we could display individual fields. To do that, use the
320``list_display`` admin option, which is a tuple of field names to display, as
321columns, on the change list page for the object::
322
323    class PollAdmin(admin.ModelAdmin):
324        # ...
325        list_display = ('question', 'pub_date')
326
327Just for good measure, let's also include the ``was_published_today`` custom
328method from Tutorial 1::
329
330    class PollAdmin(admin.ModelAdmin):
331        # ...
332        list_display = ('question', 'pub_date', 'was_published_today')
333
334Now the poll change list page looks like this:
335
336.. image:: _images/admin13t.png
337   :alt: Polls change list page, updated
338
339You can click on the column headers to sort by those values -- except in the
340case of the ``was_published_today`` header, because sorting by the output of
341an arbitrary method is not supported. Also note that the column header for
342``was_published_today`` is, by default, the name of the method (with
343underscores replaced with spaces). But you can change that by giving that
344method (in ``models.py``) a ``short_description`` attribute::
345
346    def was_published_today(self):
347        return self.pub_date.date() == datetime.date.today()
348    was_published_today.short_description = 'Published today?'
349
350Edit your admin.py file again and add an improvement to the Poll change list page: Filters. Add the
351following line to ``PollAdmin``::
352
353    list_filter = ['pub_date']
354
355That adds a "Filter" sidebar that lets people filter the change list by the
356``pub_date`` field:
357
358.. image:: _images/admin14t.png
359   :alt: Polls change list page, updated
360
361The type of filter displayed depends on the type of field you're filtering on.
362Because ``pub_date`` is a DateTimeField, Django knows to give the default
363filter options for DateTimeFields: "Any date," "Today," "Past 7 days,"
364"This month," "This year."
365
366This is shaping up well. Let's add some search capability::
367
368    search_fields = ['question']
369
370That adds a search box at the top of the change list. When somebody enters
371search terms, Django will search the ``question`` field. You can use as many
372fields as you'd like -- although because it uses a ``LIKE`` query behind the
373scenes, keep it reasonable, to keep your database happy.
374
375Finally, because Poll objects have dates, it'd be convenient to be able to
376drill down by date. Add this line::
377
378    date_hierarchy = 'pub_date'
379
380That adds hierarchical navigation, by date, to the top of the change list page.
381At top level, it displays all available years. Then it drills down to months
382and, ultimately, days.
383
384Now's also a good time to note that change lists give you free pagination. The
385default is to display 50 items per page. Change-list pagination, search boxes,
386filters, date-hierarchies and column-header-ordering all work together like you
387think they should.
388
389Customize the admin look and feel
390=================================
391
392Clearly, having "Django administration" at the top of each admin page is
393ridiculous. It's just placeholder text.
394
395That's easy to change, though, using Django's template system. The Django admin
396is powered by Django itself, and its interfaces use Django's own template
397system.
398
399Open your settings file (``mysite/settings.py``, remember) and look at the
400:setting:`TEMPLATE_DIRS` setting. :setting:`TEMPLATE_DIRS` is a tuple of
401filesystem directories to check when loading Django templates. It's a search
402path.
403
404By default, :setting:`TEMPLATE_DIRS` is empty. So, let's add a line to it, to
405tell Django where our templates live::
406
407    TEMPLATE_DIRS = (
408        '/home/my_username/mytemplates', # Change this to your own directory.
409    )
410
411Now copy the template ``admin/base_site.html`` from within the default Django
412admin template directory in the source code of Django itself
413(``django/contrib/admin/templates``) into an ``admin`` subdirectory of
414whichever directory you're using in :setting:`TEMPLATE_DIRS`. For example, if
415your :setting:`TEMPLATE_DIRS` includes ``'/home/my_username/mytemplates'``, as
416above, then copy ``django/contrib/admin/templates/admin/base_site.html`` to
417``/home/my_username/mytemplates/admin/base_site.html``. Don't forget that
418``admin`` subdirectory.
419
420Then, just edit the file and replace the generic Django text with your own
421site's name as you see fit.
422
423This template file contains lots of text like ``{% block branding %}``
424and ``{{ title }}``. The ``{%`` and ``{{`` tags are part of Django's
425template language. When Django renders ``admin/base_site.html``, this
426template language will be evaluated to produce the final HTML page.
427Don't worry if you can't make any sense of the template right now --
428we'll delve into Django's templating language in Tutorial 3.
429
430Note that any of Django's default admin templates can be overridden. To
431override a template, just do the same thing you did with ``base_site.html`` --
432copy it from the default directory into your custom directory, and make
433changes.
434
435Astute readers will ask: But if :setting:`TEMPLATE_DIRS` was empty by default,
436how was Django finding the default admin templates? The answer is that, by
437default, Django automatically looks for a ``templates/`` subdirectory within
438each app package, for use as a fallback. See the :ref:`template loader
439documentation <template-loaders>` for full information.
440
441Customize the admin index page
442==============================
443
444On a similar note, you might want to customize the look and feel of the Django
445admin index page.
446
447By default, it displays all the apps in :setting:`INSTALLED_APPS` that have been
448registered with the admin application, in alphabetical order. You may want to
449make significant changes to the layout. After all, the index is probably the
450most important page of the admin, and it should be easy to use.
451
452The template to customize is ``admin/index.html``. (Do the same as with
453``admin/base_site.html`` in the previous section -- copy it from the default
454directory to your custom template directory.) Edit the file, and you'll see it
455uses a template variable called ``app_list``. That variable contains every
456installed Django app. Instead of using that, you can hard-code links to
457object-specific admin pages in whatever way you think is best. Again,
458don't worry if you can't understand the template language -- we'll cover that
459in more detail in Tutorial 3.
460
461When you're comfortable with the admin site, read :doc:`part 3 of this tutorial
462</intro/tutorial03>` to start working on public poll views.