Skip to content

Commit

Permalink
Updated tutorials to use newforms-admin syntax.
Browse files Browse the repository at this point in the history
git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@7959 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
jacobian committed Jul 18, 2008
1 parent 2c48a0b commit 15497ee
Showing 1 changed file with 82 additions and 81 deletions.
163 changes: 82 additions & 81 deletions docs/tutorial02.txt
Expand Up @@ -31,10 +31,10 @@ activate the admin site for your installation, do these three things:
* Add ``"django.contrib.admin"`` to your ``INSTALLED_APPS`` setting. * Add ``"django.contrib.admin"`` to your ``INSTALLED_APPS`` setting.
* Run ``python manage.py syncdb``. Since you have added a new application * Run ``python manage.py syncdb``. Since you have added a new application
to ``INSTALLED_APPS``, the database tables need to be updated. to ``INSTALLED_APPS``, the database tables need to be updated.
* Edit your ``mysite/urls.py`` file and uncomment the line below * Edit your ``mysite/urls.py`` file and uncomment the lines below the
"Uncomment this for admin:". This file is a URLconf; we'll dig into "Uncomment this for admin:" comments. This file is a URLconf; we'll dig
URLconfs in the next tutorial. For now, all you need to know is that it into URLconfs in the next tutorial. For now, all you need to know is that
maps URL roots to applications. it maps URL roots to applications.


Start the development server Start the development server
============================ ============================
Expand Down Expand Up @@ -71,19 +71,13 @@ Make the poll app modifiable in the admin


But where's our poll app? It's not displayed on the admin index page. But where's our poll app? It's not displayed on the admin index page.


Just one thing to do: We need to specify in the ``Poll`` model that ``Poll`` Just one thing to do: We need to tell the admin that ``Poll``
objects have an admin interface. Edit the ``mysite/polls/models.py`` file and objects have an admin interface. Edit the ``mysite/polls/models.py`` file and
make the following change to add an inner ``Admin`` class:: add the following to the bottom of the file::


class Poll(models.Model): from django.contrib import admin
# ...
class Admin: admin.site.register(Poll)
pass

The ``class Admin`` will contain all the settings that control how this model
appears in the Django admin. All the settings are optional, however, so
creating an empty class means "give this object an admin interface using
all the default options."


Now reload the Django admin page to see your changes. Note that you don't have Now reload the Django admin page to see your changes. Note that you don't have
to restart the development server -- the server will auto-reload your project, to restart the development server -- the server will auto-reload your project,
Expand All @@ -92,8 +86,8 @@ so any modifications code will be seen immediately in your browser.
Explore the free admin functionality Explore the free admin functionality
==================================== ====================================


Now that ``Poll`` has the inner ``Admin`` class, Django knows that it should be Now that we've registered ``Poll``, Django knows that it should be displayed on
displayed on the admin index page: the admin index page:


.. image:: http://media.djangoproject.com/img/doc/tutorial-trunk/admin03t.png .. image:: http://media.djangoproject.com/img/doc/tutorial-trunk/admin03t.png
:alt: Django admin index page, now with polls displayed :alt: Django admin index page, now with polls displayed
Expand Down Expand Up @@ -145,17 +139,26 @@ with the timestamp and username of the person who made the change:
Customize the admin form Customize the admin form
======================== ========================


Take a few minutes to marvel at all the code you didn't have to write. Take a few minutes to marvel at all the code you didn't have to write. When you
call ``admin.site.register(Poll)``, Django just lets you edit the object and
"guess" at how to display it within the admin. Often you'll want to control how
the admin looks and works. You'll do this by telling Django about the options
you want when you register the object.


Let's customize this a bit. We can reorder the fields by explicitly adding a Let's see how this works by reordering the fields on the edit form. Replace the
``fields`` parameter to ``Admin``:: ``admin.site.register(Poll)`` line with::


class Admin: class PollAdmin(admin.ModelAdmin):
fields = ( fields = ['pub_date', 'question']
(None, {'fields': ('pub_date', 'question')}),
) admin.site.register(Poll, PollAdmin)


That made the "Publication date" show up first instead of second: You'll follow this pattern -- create a model admin object, then pass it as the
second argument to ``admin.site.register()`` -- any time you need to change the
admin options for an object.

This particular change above makes the "Publication date" come before the
"Question" field:


.. image:: http://media.djangoproject.com/img/doc/tutorial-trunk/admin07.png .. image:: http://media.djangoproject.com/img/doc/tutorial-trunk/admin07.png
:alt: Fields have been reordered :alt: Fields have been reordered
Expand All @@ -166,13 +169,15 @@ of fields, choosing an intuitive order is an important usability detail.
And speaking of forms with dozens of fields, you might want to split the form And speaking of forms with dozens of fields, you might want to split the form
up into fieldsets:: up into fieldsets::


class Admin: class PollAdmin(admin.ModelAdmin):
fields = ( fieldsets = [
(None, {'fields': ('question',)}), (None, {'fields': ['question']}),
('Date information', {'fields': ('pub_date',)}), ('Date information', {'fields': ['pub_date']}),
) ]

admin.site.register(Poll, PollAdmin)


The first element of each tuple in ``fields`` is the title of the fieldset. The first element of each tuple in ``fieldsets`` is the title of the fieldset.
Here's what our form looks like now: Here's what our form looks like now:


.. image:: http://media.djangoproject.com/img/doc/tutorial-trunk/admin08t.png .. image:: http://media.djangoproject.com/img/doc/tutorial-trunk/admin08t.png
Expand All @@ -184,11 +189,11 @@ You can assign arbitrary HTML classes to each fieldset. Django provides a
This is useful when you have a long form that contains a number of fields that This is useful when you have a long form that contains a number of fields that
aren't commonly used:: aren't commonly used::


class Admin: class PollAdmin(admin.ModelAdmin):
fields = ( fieldsets = [
(None, {'fields': ('question',)}), (None, {'fields': ['question']}),
('Date information', {'fields': ('pub_date',), 'classes': 'collapse'}), ('Date information', {'fields': ['pub_date'], 'classes': 'pub_date'}),
) ]


.. image:: http://media.djangoproject.com/img/doc/tutorial-trunk/admin09.png .. image:: http://media.djangoproject.com/img/doc/tutorial-trunk/admin09.png
:alt: Fieldset is initially collapsed :alt: Fieldset is initially collapsed
Expand All @@ -201,14 +206,10 @@ the admin page doesn't display choices.


Yet. Yet.


There are two ways to solve this problem. The first is to give the ``Choice`` There are two ways to solve this problem. The first register ``Choice`` with the
model its own inner ``Admin`` class, just as we did with ``Poll``. Here's what admin just as we did with ``Poll``. That's easy::
that would look like::


class Choice(models.Model): admin.site.register(Choice)
# ...
class Admin:
pass


Now "Choices" is an available option in the Django admin. The "Add choice" form Now "Choices" is an available option in the Django admin. The "Add choice" form
looks like this: looks like this:
Expand All @@ -220,33 +221,35 @@ In that form, the "Poll" field is a select box containing every poll in the
database. Django knows that a ``ForeignKey`` should be represented in the admin database. Django knows that a ``ForeignKey`` should be represented in the admin
as a ``<select>`` box. In our case, only one poll exists at this point. as a ``<select>`` box. In our case, only one poll exists at this point.


Also note the "Add Another" link next to "Poll." Every object with a ForeignKey Also note the "Add Another" link next to "Poll." Every object with a
relationship to another gets this for free. When you click "Add Another," you'll ``ForeignKey`` relationship to another gets this for free. When you click "Add
get a popup window with the "Add poll" form. If you add a poll in that window Another," you'll get a popup window with the "Add poll" form. If you add a poll
and click "Save," Django will save the poll to the database and dynamically add in that window and click "Save," Django will save the poll to the database and
it as the selected choice on the "Add choice" form you're looking at. dynamically add it as the selected choice on the "Add choice" form you're
looking at.


But, really, this is an inefficient way of adding Choice objects to the system. But, really, this is an inefficient way of adding Choice objects to the system.
It'd be better if you could add a bunch of Choices directly when you create the It'd be better if you could add a bunch of Choices directly when you create the
Poll object. Let's make that happen. Poll object. Let's make that happen.


Remove the ``Admin`` for the Choice model. Then, edit the ``ForeignKey(Poll)`` Remove the ``register()`` cal for the Choice model. Then, edit the ``Poll``
field like so:: registration code to read::


poll = models.ForeignKey(Poll, edit_inline=models.STACKED, num_in_admin=3) class ChoiceInline(admin.StackedInline):

model = Choice
This tells Django: "Choice objects are edited on the Poll admin page. By extra = 3
default, provide enough fields for 3 Choices."
class PollAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['question']}),
('Date information', {'fields': ['pub_date'], 'classes': 'pub_date'}),
]
inlines = [ChoiceInline]


Then change the other fields in ``Choice`` to give them ``core=True``:: admin.site.register(Poll, PollAdmin)


choice = models.CharField(max_length=200, core=True) This tells Django: "Choice objects are edited on the Poll admin page. By
votes = models.IntegerField(core=True) default, provide enough fields for 3 choices."

This tells Django: "When you edit a Choice on the Poll admin page, the 'choice'
and 'votes' fields are required. The presence of at least one of them signifies
the addition of a new Choice object, and clearing both of them signifies the
deletion of that existing Choice object."


Load the "Add poll" page to see how that looks: Load the "Add poll" page to see how that looks:


Expand All @@ -255,19 +258,18 @@ Load the "Add poll" page to see how that looks:
:target: http://media.djangoproject.com/img/doc/tutorial-trunk/admin11.png :target: http://media.djangoproject.com/img/doc/tutorial-trunk/admin11.png


It works like this: There are three slots for related Choices -- as specified It works like this: There are three slots for related Choices -- as specified
by ``num_in_admin`` -- but each time you come back to the "Change" page for an by ``extra`` -- and each time you come back to the "Change" page for an
already-created object, you get one extra slot. (This means there's no already-created object, you get another three extra slots.
hard-coded limit on how many related objects can be added.) If you wanted space
for three extra Choices each time you changed the poll, you'd use
``num_extra_on_change=3``.


One small problem, though. It takes a lot of screen space to display all the One small problem, though. It takes a lot of screen space to display all the
fields for entering related Choice objects. For that reason, Django offers an fields for entering related Choice objects. For that reason, Django offers an
alternate way of displaying inline related objects:: tabular way of displaying inline related objects; you just need to change
the ``ChoiceInline`` declaration to read::


poll = models.ForeignKey(Poll, edit_inline=models.TABULAR, num_in_admin=3) class ChoiceInline(admin.TabularInline):
#...


With that ``edit_inline=models.TABULAR`` (instead of ``models.STACKED``), the With that ``TabularInline`` (instead of ``StackedInline``), the
related objects are displayed in a more compact, table-based format: related objects are displayed in a more compact, table-based format:


.. image:: http://media.djangoproject.com/img/doc/tutorial-trunk/admin12.png .. image:: http://media.djangoproject.com/img/doc/tutorial-trunk/admin12.png
Expand All @@ -285,21 +287,21 @@ Here's what it looks like at this point:
:alt: Polls change list page :alt: Polls change list page
:target: http://media.djangoproject.com/img/doc/tutorial-trunk/admin04.png :target: http://media.djangoproject.com/img/doc/tutorial-trunk/admin04.png


By default, Django displays the ``str()`` of each object. But sometimes it'd By default, Django displays the ``str()`` of each object. But sometimes it'd be
be more helpful if we could display individual fields. To do that, use the more helpful if we could display individual fields. To do that, use the
``list_display`` option, which is a tuple of field names to display, as columns, ``list_display`` admin option, which is a tuple of field names to display, as
on the change list page for the object:: columns, on the change list page for the object::


class Poll(models.Model): class PollAdmin(admin.ModelAdmin):
# ... # ...
class Admin: list_display = ('question', 'pub_date')
# ...
list_display = ('question', 'pub_date')


Just for good measure, let's also include the ``was_published_today`` custom Just for good measure, let's also include the ``was_published_today`` custom
method from Tutorial 1:: method from Tutorial 1::


list_display = ('question', 'pub_date', 'was_published_today') class PollAdmin(admin.ModelAdmin):
# ...
list_display = ('question', 'pub_date', 'was_published_today')


Now the poll change list page looks like this: Now the poll change list page looks like this:


Expand All @@ -318,9 +320,8 @@ method a ``short_description`` attribute::
return self.pub_date.date() == datetime.date.today() return self.pub_date.date() == datetime.date.today()
was_published_today.short_description = 'Published today?' was_published_today.short_description = 'Published today?'



Let's add another improvement to the Poll change list page: Filters. Add the Let's add another improvement to the Poll change list page: Filters. Add the
following line to ``Poll.Admin``:: following line to ``PollAdmin``::


list_filter = ['pub_date'] list_filter = ['pub_date']


Expand Down

0 comments on commit 15497ee

Please sign in to comment.