| 1 |
===================== |
|---|
| 2 |
The Django admin site |
|---|
| 3 |
===================== |
|---|
| 4 |
|
|---|
| 5 |
One of the most powerful parts of Django is the automatic admin interface. It |
|---|
| 6 |
reads metadata in your model to provide a powerful and production-ready |
|---|
| 7 |
interface that content producers can immediately use to start adding content to |
|---|
| 8 |
the site. In this document, we discuss how to activate, use and customize |
|---|
| 9 |
Django's admin interface. |
|---|
| 10 |
|
|---|
| 11 |
.. admonition:: Note |
|---|
| 12 |
|
|---|
| 13 |
The admin site has been refactored significantly since Django 0.96. This |
|---|
| 14 |
document describes the newest version of the admin site, which allows for |
|---|
| 15 |
much richer customization. If you follow the development of Django itself, |
|---|
| 16 |
you may have heard this described as "newforms-admin." |
|---|
| 17 |
|
|---|
| 18 |
Overview |
|---|
| 19 |
======== |
|---|
| 20 |
|
|---|
| 21 |
There are five steps in activating the Django admin site: |
|---|
| 22 |
|
|---|
| 23 |
1. Add ``django.contrib.admin`` to your ``INSTALLED_APPS`` setting. |
|---|
| 24 |
|
|---|
| 25 |
2. Determine which of your application's models should be editable in the |
|---|
| 26 |
admin interface. |
|---|
| 27 |
|
|---|
| 28 |
3. For each of those models, optionally create a ``ModelAdmin`` class that |
|---|
| 29 |
encapsulates the customized admin functionality and options for that |
|---|
| 30 |
particular model. |
|---|
| 31 |
|
|---|
| 32 |
4. Instantiate an ``AdminSite`` and tell it about each of your models and |
|---|
| 33 |
``ModelAdmin`` classes. |
|---|
| 34 |
|
|---|
| 35 |
5. Hook the ``AdminSite`` instance into your URLconf. |
|---|
| 36 |
|
|---|
| 37 |
``ModelAdmin`` objects |
|---|
| 38 |
====================== |
|---|
| 39 |
|
|---|
| 40 |
The ``ModelAdmin`` class is the representation of a model in the admin |
|---|
| 41 |
interface. These are stored in a file named ``admin.py`` in your application. |
|---|
| 42 |
Let's take a look at a very simple example the ``ModelAdmin``:: |
|---|
| 43 |
|
|---|
| 44 |
from django.contrib import admin |
|---|
| 45 |
from myproject.myapp.models import Author |
|---|
| 46 |
|
|---|
| 47 |
class AuthorAdmin(admin.ModelAdmin): |
|---|
| 48 |
pass |
|---|
| 49 |
admin.site.register(Author, AuthorAdmin) |
|---|
| 50 |
|
|---|
| 51 |
``ModelAdmin`` Options |
|---|
| 52 |
---------------------- |
|---|
| 53 |
|
|---|
| 54 |
The ``ModelAdmin`` is very flexible. It has several options for dealing with |
|---|
| 55 |
customizing the interface. All options are defined on the ``ModelAdmin`` |
|---|
| 56 |
subclass:: |
|---|
| 57 |
|
|---|
| 58 |
class AuthorAdmin(admin.ModelAdmin): |
|---|
| 59 |
date_hierarchy = 'pub_date' |
|---|
| 60 |
|
|---|
| 61 |
``date_hierarchy`` |
|---|
| 62 |
~~~~~~~~~~~~~~~~~~ |
|---|
| 63 |
|
|---|
| 64 |
Set ``date_hierarchy`` to the name of a ``DateField`` or ``DateTimeField`` in |
|---|
| 65 |
your model, and the change list page will include a date-based drilldown |
|---|
| 66 |
navigation by that field. |
|---|
| 67 |
|
|---|
| 68 |
Example:: |
|---|
| 69 |
|
|---|
| 70 |
date_hierarchy = 'pub_date' |
|---|
| 71 |
|
|---|
| 72 |
``form`` |
|---|
| 73 |
~~~~~~~~ |
|---|
| 74 |
|
|---|
| 75 |
The default ``forms.ModelForm`` class used to generate the form on the |
|---|
| 76 |
add/change pages for models. You can easily change this to your own |
|---|
| 77 |
``ModelForm`` to override the default form behavior of the add/change pages. |
|---|
| 78 |
|
|---|
| 79 |
``fieldsets`` |
|---|
| 80 |
~~~~~~~~~~~~~ |
|---|
| 81 |
|
|---|
| 82 |
Set ``fieldsets`` to control the layout of admin "add" and "change" pages. |
|---|
| 83 |
|
|---|
| 84 |
``fieldsets`` is a list of two-tuples, in which each two-tuple represents a |
|---|
| 85 |
``<fieldset>`` on the admin form page. (A ``<fieldset>`` is a "section" of the |
|---|
| 86 |
form.) |
|---|
| 87 |
|
|---|
| 88 |
The two-tuples are in the format ``(name, field_options)``, where ``name`` is a |
|---|
| 89 |
string representing the title of the fieldset and ``field_options`` is a |
|---|
| 90 |
dictionary of information about the fieldset, including a list of fields to be |
|---|
| 91 |
displayed in it. |
|---|
| 92 |
|
|---|
| 93 |
A full example, taken from the ``django.contrib.flatpages.FlatPage`` model:: |
|---|
| 94 |
|
|---|
| 95 |
class FlatPageAdmin(admin.ModelAdmin): |
|---|
| 96 |
fieldsets = ( |
|---|
| 97 |
(None, { |
|---|
| 98 |
'fields': ('url', 'title', 'content', 'sites') |
|---|
| 99 |
}), |
|---|
| 100 |
('Advanced options', { |
|---|
| 101 |
'classes': ('collapse',), |
|---|
| 102 |
'fields': ('enable_comments', 'registration_required', 'template_name') |
|---|
| 103 |
}), |
|---|
| 104 |
) |
|---|
| 105 |
|
|---|
| 106 |
This results in an admin page that looks like: |
|---|
| 107 |
|
|---|
| 108 |
.. image:: http://media.djangoproject.com/img/doc/flatfiles_admin.png |
|---|
| 109 |
|
|---|
| 110 |
If ``fieldsets`` isn't given, Django will default to displaying each field |
|---|
| 111 |
that isn't an ``AutoField`` and has ``editable=True``, in a single fieldset, |
|---|
| 112 |
in the same order as the fields are defined in the model. |
|---|
| 113 |
|
|---|
| 114 |
The ``field_options`` dictionary can have the following keys: |
|---|
| 115 |
|
|---|
| 116 |
``fields`` |
|---|
| 117 |
A tuple of field names to display in this fieldset. This key is required. |
|---|
| 118 |
|
|---|
| 119 |
Example:: |
|---|
| 120 |
|
|---|
| 121 |
{ |
|---|
| 122 |
'fields': ('first_name', 'last_name', 'address', 'city', 'state'), |
|---|
| 123 |
} |
|---|
| 124 |
|
|---|
| 125 |
To display multiple fields on the same line, wrap those fields in their own |
|---|
| 126 |
tuple. In this example, the ``first_name`` and ``last_name`` fields will |
|---|
| 127 |
display on the same line:: |
|---|
| 128 |
|
|---|
| 129 |
{ |
|---|
| 130 |
'fields': (('first_name', 'last_name'), 'address', 'city', 'state'), |
|---|
| 131 |
} |
|---|
| 132 |
|
|---|
| 133 |
``classes`` |
|---|
| 134 |
A list containing extra CSS classes to apply to the fieldset. |
|---|
| 135 |
|
|---|
| 136 |
Example:: |
|---|
| 137 |
|
|---|
| 138 |
{ |
|---|
| 139 |
'classes': ['wide', 'extrapretty'], |
|---|
| 140 |
} |
|---|
| 141 |
|
|---|
| 142 |
Two useful classes defined by the default admin-site stylesheet are |
|---|
| 143 |
``collapse`` and ``wide``. Fieldsets with the ``collapse`` style will be |
|---|
| 144 |
initially collapsed in the admin and replaced with a small "click to expand" |
|---|
| 145 |
link. Fieldsets with the ``wide`` style will be given extra horizontal space. |
|---|
| 146 |
|
|---|
| 147 |
``description`` |
|---|
| 148 |
A string of optional extra text to be displayed at the top of each fieldset, |
|---|
| 149 |
under the heading of the fieldset. |
|---|
| 150 |
|
|---|
| 151 |
Note that this value is *not* HTML-escaped when it's displayed in |
|---|
| 152 |
the admin interface. This lets you include HTML if you so desire. |
|---|
| 153 |
Alternatively you can use plain text and |
|---|
| 154 |
``django.utils.html.escape()`` to escape any HTML special |
|---|
| 155 |
characters. |
|---|
| 156 |
|
|---|
| 157 |
``fields`` |
|---|
| 158 |
~~~~~~~~~~ |
|---|
| 159 |
|
|---|
| 160 |
Use this option as an alternative to ``fieldsets`` if the layout does not |
|---|
| 161 |
matter and if you want to only show a subset of the available fields in the |
|---|
| 162 |
form. For example, you could define a simpler version of the admin form for |
|---|
| 163 |
the ``django.contrib.flatpages.FlatPage`` model as follows:: |
|---|
| 164 |
|
|---|
| 165 |
class FlatPageAdmin(admin.ModelAdmin): |
|---|
| 166 |
fields = ('url', 'title', 'content') |
|---|
| 167 |
|
|---|
| 168 |
In the above example, only the fields 'url', 'title' and 'content' will be |
|---|
| 169 |
displayed, sequencially, in the form. |
|---|
| 170 |
|
|---|
| 171 |
.. admonition:: Note |
|---|
| 172 |
|
|---|
| 173 |
This ``fields`` option should not be confused with the ``fields`` |
|---|
| 174 |
dictionary key that is within the ``fieldsets`` option, as described in |
|---|
| 175 |
the previous section. |
|---|
| 176 |
|
|---|
| 177 |
``filter_horizontal`` |
|---|
| 178 |
~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 179 |
|
|---|
| 180 |
Use a nifty unobtrusive JavaScript "filter" interface instead of the |
|---|
| 181 |
usability-challenged ``<select multiple>`` in the admin form. The value is a |
|---|
| 182 |
list of fields that should be displayed as a horizontal filter interface. See |
|---|
| 183 |
``filter_vertical`` to use a vertical interface. |
|---|
| 184 |
|
|---|
| 185 |
``filter_vertical`` |
|---|
| 186 |
~~~~~~~~~~~~~~~~~~~ |
|---|
| 187 |
|
|---|
| 188 |
Same as ``filter_horizontal``, but is a vertical display of the filter |
|---|
| 189 |
interface. |
|---|
| 190 |
|
|---|
| 191 |
``list_display`` |
|---|
| 192 |
~~~~~~~~~~~~~~~~ |
|---|
| 193 |
|
|---|
| 194 |
Set ``list_display`` to control which fields are displayed on the change list |
|---|
| 195 |
page of the admin. |
|---|
| 196 |
|
|---|
| 197 |
Example:: |
|---|
| 198 |
|
|---|
| 199 |
list_display = ('first_name', 'last_name') |
|---|
| 200 |
|
|---|
| 201 |
If you don't set ``list_display``, the admin site will display a single column |
|---|
| 202 |
that displays the ``__unicode__()`` representation of each object. |
|---|
| 203 |
|
|---|
| 204 |
A few special cases to note about ``list_display``: |
|---|
| 205 |
|
|---|
| 206 |
* If the field is a ``ForeignKey``, Django will display the |
|---|
| 207 |
``__unicode__()`` of the related object. |
|---|
| 208 |
|
|---|
| 209 |
* ``ManyToManyField`` fields aren't supported, because that would entail |
|---|
| 210 |
executing a separate SQL statement for each row in the table. If you |
|---|
| 211 |
want to do this nonetheless, give your model a custom method, and add |
|---|
| 212 |
that method's name to ``list_display``. (See below for more on custom |
|---|
| 213 |
methods in ``list_display``.) |
|---|
| 214 |
|
|---|
| 215 |
* If the field is a ``BooleanField`` or ``NullBooleanField``, Django will |
|---|
| 216 |
display a pretty "on" or "off" icon instead of ``True`` or ``False``. |
|---|
| 217 |
|
|---|
| 218 |
* If the string given is a method of the model, Django will call it and |
|---|
| 219 |
display the output. This method should have a ``short_description`` |
|---|
| 220 |
function attribute, for use as the header for the field. |
|---|
| 221 |
|
|---|
| 222 |
Here's a full example model:: |
|---|
| 223 |
|
|---|
| 224 |
class Person(models.Model): |
|---|
| 225 |
name = models.CharField(max_length=50) |
|---|
| 226 |
birthday = models.DateField() |
|---|
| 227 |
|
|---|
| 228 |
def decade_born_in(self): |
|---|
| 229 |
return self.birthday.strftime('%Y')[:3] + "0's" |
|---|
| 230 |
decade_born_in.short_description = 'Birth decade' |
|---|
| 231 |
|
|---|
| 232 |
class PersonAdmin(admin.ModelAdmin): |
|---|
| 233 |
list_display = ('name', 'decade_born_in') |
|---|
| 234 |
|
|---|
| 235 |
* If the string given is a method of the model, Django will HTML-escape the |
|---|
| 236 |
output by default. If you'd rather not escape the output of the method, |
|---|
| 237 |
give the method an ``allow_tags`` attribute whose value is ``True``. |
|---|
| 238 |
|
|---|
| 239 |
Here's a full example model:: |
|---|
| 240 |
|
|---|
| 241 |
class Person(models.Model): |
|---|
| 242 |
first_name = models.CharField(max_length=50) |
|---|
| 243 |
last_name = models.CharField(max_length=50) |
|---|
| 244 |
color_code = models.CharField(max_length=6) |
|---|
| 245 |
|
|---|
| 246 |
def colored_name(self): |
|---|
| 247 |
return '<span style="color: #%s;">%s %s</span>' % (self.color_code, self.first_name, self.last_name) |
|---|
| 248 |
colored_name.allow_tags = True |
|---|
| 249 |
|
|---|
| 250 |
class PersonAdmin(admin.ModelAdmin): |
|---|
| 251 |
list_display = ('first_name', 'last_name', 'colored_name') |
|---|
| 252 |
|
|---|
| 253 |
* If the string given is a method of the model that returns True or False |
|---|
| 254 |
Django will display a pretty "on" or "off" icon if you give the method a |
|---|
| 255 |
``boolean`` attribute whose value is ``True``. |
|---|
| 256 |
|
|---|
| 257 |
Here's a full example model:: |
|---|
| 258 |
|
|---|
| 259 |
class Person(models.Model): |
|---|
| 260 |
first_name = models.CharField(max_length=50) |
|---|
| 261 |
birthday = models.DateField() |
|---|
| 262 |
|
|---|
| 263 |
def born_in_fifties(self): |
|---|
| 264 |
return self.birthday.strftime('%Y')[:3] == 5 |
|---|
| 265 |
born_in_fifties.boolean = True |
|---|
| 266 |
|
|---|
| 267 |
class PersonAdmin(admin.ModelAdmin): |
|---|
| 268 |
list_display = ('name', 'born_in_fifties') |
|---|
| 269 |
|
|---|
| 270 |
|
|---|
| 271 |
* The ``__str__()`` and ``__unicode__()`` methods are just as valid in |
|---|
| 272 |
``list_display`` as any other model method, so it's perfectly OK to do |
|---|
| 273 |
this:: |
|---|
| 274 |
|
|---|
| 275 |
list_display = ('__unicode__', 'some_other_field') |
|---|
| 276 |
|
|---|
| 277 |
* Usually, elements of ``list_display`` that aren't actual database fields |
|---|
| 278 |
can't be used in sorting (because Django does all the sorting at the |
|---|
| 279 |
database level). |
|---|
| 280 |
|
|---|
| 281 |
However, if an element of ``list_display`` represents a certain database |
|---|
| 282 |
field, you can indicate this fact by setting the ``admin_order_field`` |
|---|
| 283 |
attribute of the item. |
|---|
| 284 |
|
|---|
| 285 |
For example:: |
|---|
| 286 |
|
|---|
| 287 |
class Person(models.Model): |
|---|
| 288 |
first_name = models.CharField(max_length=50) |
|---|
| 289 |
color_code = models.CharField(max_length=6) |
|---|
| 290 |
|
|---|
| 291 |
def colored_first_name(self): |
|---|
| 292 |
return '<span style="color: #%s;">%s</span>' % (self.color_code, self.first_name) |
|---|
| 293 |
colored_first_name.allow_tags = True |
|---|
| 294 |
colored_first_name.admin_order_field = 'first_name' |
|---|
| 295 |
|
|---|
| 296 |
class PersonAdmin(admin.ModelAdmin): |
|---|
| 297 |
list_display = ('first_name', 'colored_first_name') |
|---|
| 298 |
|
|---|
| 299 |
The above will tell Django to order by the ``first_name`` field when |
|---|
| 300 |
trying to sort by ``colored_first_name`` in the admin. |
|---|
| 301 |
|
|---|
| 302 |
``list_display_links`` |
|---|
| 303 |
~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 304 |
|
|---|
| 305 |
Set ``list_display_links`` to control which fields in ``list_display`` should |
|---|
| 306 |
be linked to the "change" page for an object. |
|---|
| 307 |
|
|---|
| 308 |
By default, the change list page will link the first column -- the first field |
|---|
| 309 |
specified in ``list_display`` -- to the change page for each item. But |
|---|
| 310 |
``list_display_links`` lets you change which columns are linked. Set |
|---|
| 311 |
``list_display_links`` to a list or tuple of field names (in the same format as |
|---|
| 312 |
``list_display``) to link. |
|---|
| 313 |
|
|---|
| 314 |
``list_display_links`` can specify one or many field names. As long as the |
|---|
| 315 |
field names appear in ``list_display``, Django doesn't care how many (or how |
|---|
| 316 |
few) fields are linked. The only requirement is: If you want to use |
|---|
| 317 |
``list_display_links``, you must define ``list_display``. |
|---|
| 318 |
|
|---|
| 319 |
In this example, the ``first_name`` and ``last_name`` fields will be linked on |
|---|
| 320 |
the change list page:: |
|---|
| 321 |
|
|---|
| 322 |
class PersonAdmin(admin.ModelAdmin): |
|---|
| 323 |
list_display = ('first_name', 'last_name', 'birthday') |
|---|
| 324 |
list_display_links = ('first_name', 'last_name') |
|---|
| 325 |
|
|---|
| 326 |
Finally, note that in order to use ``list_display_links``, you must define |
|---|
| 327 |
``list_display``, too. |
|---|
| 328 |
|
|---|
| 329 |
``list_filter`` |
|---|
| 330 |
~~~~~~~~~~~~~~~ |
|---|
| 331 |
|
|---|
| 332 |
Set ``list_filter`` to activate filters in the right sidebar of the change list |
|---|
| 333 |
page of the admin. This should be a list of field names, and each specified |
|---|
| 334 |
field should be either a ``BooleanField``, ``CharField``, ``DateField``, |
|---|
| 335 |
``DateTimeField``, ``IntegerField`` or ``ForeignKey``. |
|---|
| 336 |
|
|---|
| 337 |
This example, taken from the ``django.contrib.auth.models.User`` model, shows |
|---|
| 338 |
how both ``list_display`` and ``list_filter`` work:: |
|---|
| 339 |
|
|---|
| 340 |
class UserAdmin(admin.ModelAdmin): |
|---|
| 341 |
list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff') |
|---|
| 342 |
list_filter = ('is_staff', 'is_superuser') |
|---|
| 343 |
|
|---|
| 344 |
The above code results in an admin change list page that looks like this: |
|---|
| 345 |
|
|---|
| 346 |
.. image:: http://media.djangoproject.com/img/doc/users_changelist.png |
|---|
| 347 |
|
|---|
| 348 |
(This example also has ``search_fields`` defined. See below.) |
|---|
| 349 |
|
|---|
| 350 |
``list_per_page`` |
|---|
| 351 |
~~~~~~~~~~~~~~~~~ |
|---|
| 352 |
|
|---|
| 353 |
Set ``list_per_page`` to control how many items appear on each paginated admin |
|---|
| 354 |
change list page. By default, this is set to ``100``. |
|---|
| 355 |
|
|---|
| 356 |
``list_select_related`` |
|---|
| 357 |
~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 358 |
|
|---|
| 359 |
Set ``list_select_related`` to tell Django to use ``select_related()`` in |
|---|
| 360 |
retrieving the list of objects on the admin change list page. This can save you |
|---|
| 361 |
a bunch of database queries. |
|---|
| 362 |
|
|---|
| 363 |
The value should be either ``True`` or ``False``. Default is ``False``. |
|---|
| 364 |
|
|---|
| 365 |
Note that Django will use ``select_related()``, regardless of this setting, |
|---|
| 366 |
if one of the ``list_display`` fields is a ``ForeignKey``. |
|---|
| 367 |
|
|---|
| 368 |
For more on ``select_related()``, see `the select_related() docs`_. |
|---|
| 369 |
|
|---|
| 370 |
.. _the select_related() docs: ../db-api/#select-related |
|---|
| 371 |
|
|---|
| 372 |
``inlines`` |
|---|
| 373 |
~~~~~~~~~~~ |
|---|
| 374 |
|
|---|
| 375 |
See ``InlineModelAdmin`` objects below. |
|---|
| 376 |
|
|---|
| 377 |
``ordering`` |
|---|
| 378 |
~~~~~~~~~~~~ |
|---|
| 379 |
|
|---|
| 380 |
Set ``ordering`` to specify how objects on the admin change list page should be |
|---|
| 381 |
ordered. This should be a list or tuple in the same format as a model's |
|---|
| 382 |
``ordering`` parameter. |
|---|
| 383 |
|
|---|
| 384 |
If this isn't provided, the Django admin will use the model's default ordering. |
|---|
| 385 |
|
|---|
| 386 |
.. admonition:: Note |
|---|
| 387 |
|
|---|
| 388 |
Django will only honor the first element in the list/tuple; any others |
|---|
| 389 |
will be ignored. |
|---|
| 390 |
|
|---|
| 391 |
``prepopulated_fields`` |
|---|
| 392 |
~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 393 |
|
|---|
| 394 |
Set ``prepopulated_fields`` to a dictionary mapping field names to the fields |
|---|
| 395 |
it should prepopulate from:: |
|---|
| 396 |
|
|---|
| 397 |
class ArticleAdmin(admin.ModelAdmin): |
|---|
| 398 |
prepopulated_fields = {"slug": ("title",)} |
|---|
| 399 |
|
|---|
| 400 |
When set, the given fields will use a bit of JavaScript to populate from the |
|---|
| 401 |
fields assigned. The main use for this functionality is to automatically |
|---|
| 402 |
generate the value for ``SlugField`` fields from one or more other fields. The |
|---|
| 403 |
generated value is produced by concatenating the values of the source fields, |
|---|
| 404 |
and then by transforming that result into a valid slug (e.g. substituting |
|---|
| 405 |
dashes for spaces). |
|---|
| 406 |
|
|---|
| 407 |
``prepopulated_fields`` doesn't accept ``DateTimeField``, ``ForeignKey``, nor |
|---|
| 408 |
``ManyToManyField`` fields. |
|---|
| 409 |
|
|---|
| 410 |
``radio_fields`` |
|---|
| 411 |
~~~~~~~~~~~~~~~~ |
|---|
| 412 |
|
|---|
| 413 |
By default, Django's admin uses a select-box interface (<select>) for |
|---|
| 414 |
fields that are ``ForeignKey`` or have ``choices`` set. If a field is present |
|---|
| 415 |
in ``radio_fields``, Django will use a radio-button interface instead. |
|---|
| 416 |
Assuming ``group`` is a ``ForeignKey`` on the ``Person`` model:: |
|---|
| 417 |
|
|---|
| 418 |
class PersonAdmin(admin.ModelAdmin): |
|---|
| 419 |
radio_fields = {"group": admin.VERTICAL} |
|---|
| 420 |
|
|---|
| 421 |
You have the choice of using ``HORIZONTAL`` or ``VERTICAL`` from the |
|---|
| 422 |
``django.contrib.admin`` module. |
|---|
| 423 |
|
|---|
| 424 |
Don't include a field in ``radio_fields`` unless it's a ``ForeignKey`` or has |
|---|
| 425 |
``choices`` set. |
|---|
| 426 |
|
|---|
| 427 |
``raw_id_fields`` |
|---|
| 428 |
~~~~~~~~~~~~~~~~~ |
|---|
| 429 |
|
|---|
| 430 |
By default, Django's admin uses a select-box interface (<select>) for |
|---|
| 431 |
fields that are ``ForeignKey``. Sometimes you don't want to incur the |
|---|
| 432 |
overhead of having to select all the related instances to display in the |
|---|
| 433 |
drop-down. |
|---|
| 434 |
|
|---|
| 435 |
``raw_id_fields`` is a list of fields you would like to change |
|---|
| 436 |
into a ``Input`` widget for either a ``ForeignKey`` or ``ManyToManyField``:: |
|---|
| 437 |
|
|---|
| 438 |
class ArticleAdmin(admin.ModelAdmin): |
|---|
| 439 |
raw_id_fields = ("newspaper",) |
|---|
| 440 |
|
|---|
| 441 |
``save_as`` |
|---|
| 442 |
~~~~~~~~~~~ |
|---|
| 443 |
|
|---|
| 444 |
Set ``save_as`` to enable a "save as" feature on admin change forms. |
|---|
| 445 |
|
|---|
| 446 |
Normally, objects have three save options: "Save", "Save and continue editing" |
|---|
| 447 |
and "Save and add another". If ``save_as`` is ``True``, "Save and add another" |
|---|
| 448 |
will be replaced by a "Save as" button. |
|---|
| 449 |
|
|---|
| 450 |
"Save as" means the object will be saved as a new object (with a new ID), |
|---|
| 451 |
rather than the old object. |
|---|
| 452 |
|
|---|
| 453 |
By default, ``save_as`` is set to ``False``. |
|---|
| 454 |
|
|---|
| 455 |
``save_on_top`` |
|---|
| 456 |
~~~~~~~~~~~~~~~ |
|---|
| 457 |
|
|---|
| 458 |
Set ``save_on_top`` to add save buttons across the top of your admin change |
|---|
| 459 |
forms. |
|---|
| 460 |
|
|---|
| 461 |
Normally, the save buttons appear only at the bottom of the forms. If you set |
|---|
| 462 |
``save_on_top``, the buttons will appear both on the top and the bottom. |
|---|
| 463 |
|
|---|
| 464 |
By default, ``save_on_top`` is set to ``False``. |
|---|
| 465 |
|
|---|
| 466 |
``search_fields`` |
|---|
| 467 |
~~~~~~~~~~~~~~~~~ |
|---|
| 468 |
|
|---|
| 469 |
Set ``search_fields`` to enable a search box on the admin change list page. |
|---|
| 470 |
This should be set to a list of field names that will be searched whenever |
|---|
| 471 |
somebody submits a search query in that text box. |
|---|
| 472 |
|
|---|
| 473 |
These fields should be some kind of text field, such as ``CharField`` or |
|---|
| 474 |
``TextField``. You can also perform a related lookup on a ``ForeignKey`` with |
|---|
| 475 |
the lookup API "follow" notation:: |
|---|
| 476 |
|
|---|
| 477 |
search_fields = ['foreign_key__related_fieldname'] |
|---|
| 478 |
|
|---|
| 479 |
When somebody does a search in the admin search box, Django splits the search |
|---|
| 480 |
query into words and returns all objects that contain each of the words, case |
|---|
| 481 |
insensitive, where each word must be in at least one of ``search_fields``. For |
|---|
| 482 |
example, if ``search_fields`` is set to ``['first_name', 'last_name']`` and a |
|---|
| 483 |
user searches for ``john lennon``, Django will do the equivalent of this SQL |
|---|
| 484 |
``WHERE`` clause:: |
|---|
| 485 |
|
|---|
| 486 |
WHERE (first_name ILIKE '%john%' OR last_name ILIKE '%john%') |
|---|
| 487 |
AND (first_name ILIKE '%lennon%' OR last_name ILIKE '%lennon%') |
|---|
| 488 |
|
|---|
| 489 |
For faster and/or more restrictive searches, prefix the field name |
|---|
| 490 |
with an operator: |
|---|
| 491 |
|
|---|
| 492 |
``^`` |
|---|
| 493 |
Matches the beginning of the field. For example, if ``search_fields`` is |
|---|
| 494 |
set to ``['^first_name', '^last_name']`` and a user searches for |
|---|
| 495 |
``john lennon``, Django will do the equivalent of this SQL ``WHERE`` |
|---|
| 496 |
clause:: |
|---|
| 497 |
|
|---|
| 498 |
WHERE (first_name ILIKE 'john%' OR last_name ILIKE 'john%') |
|---|
| 499 |
AND (first_name ILIKE 'lennon%' OR last_name ILIKE 'lennon%') |
|---|
| 500 |
|
|---|
| 501 |
This query is more efficient than the normal ``'%john%'`` query, because |
|---|
| 502 |
the database only needs to check the beginning of a column's data, rather |
|---|
| 503 |
than seeking through the entire column's data. Plus, if the column has an |
|---|
| 504 |
index on it, some databases may be able to use the index for this query, |
|---|
| 505 |
even though it's a ``LIKE`` query. |
|---|
| 506 |
|
|---|
| 507 |
``=`` |
|---|
| 508 |
Matches exactly, case-insensitive. For example, if |
|---|
| 509 |
``search_fields`` is set to ``['=first_name', '=last_name']`` and |
|---|
| 510 |
a user searches for ``john lennon``, Django will do the equivalent |
|---|
| 511 |
of this SQL ``WHERE`` clause:: |
|---|
| 512 |
|
|---|
| 513 |
WHERE (first_name ILIKE 'john' OR last_name ILIKE 'john') |
|---|
| 514 |
AND (first_name ILIKE 'lennon' OR last_name ILIKE 'lennon') |
|---|
| 515 |
|
|---|
| 516 |
Note that the query input is split by spaces, so, following this example, |
|---|
| 517 |
it's currently not possible to search for all records in which |
|---|
| 518 |
``first_name`` is exactly ``'john winston'`` (containing a space). |
|---|
| 519 |
|
|---|
| 520 |
``@`` |
|---|
| 521 |
Performs a full-text match. This is like the default search method but uses |
|---|
| 522 |
an index. Currently this is only available for MySQL. |
|---|
| 523 |
|
|---|
| 524 |
``ModelAdmin`` media definitions |
|---|
| 525 |
-------------------------------- |
|---|
| 526 |
|
|---|
| 527 |
There are times where you would like add a bit of CSS and/or JavaScript to |
|---|
| 528 |
the add/change views. This can be accomplished by using a Media inner class |
|---|
| 529 |
on your ``ModelAdmin``:: |
|---|
| 530 |
|
|---|
| 531 |
class ArticleAdmin(admin.ModelAdmin): |
|---|
| 532 |
class Media: |
|---|
| 533 |
css = { |
|---|
| 534 |
"all": ("my_styles.css",) |
|---|
| 535 |
} |
|---|
| 536 |
js = ("my_code.js",) |
|---|
| 537 |
|
|---|
| 538 |
Keep in mind that this will be prepended with ``MEDIA_URL``. The same rules |
|---|
| 539 |
apply as `regular media definitions on forms`_. |
|---|
| 540 |
|
|---|
| 541 |
.. _regular media definitions on forms: ../forms/#media |
|---|
| 542 |
|
|---|
| 543 |
Adding custom validation to the admin |
|---|
| 544 |
------------------------------------- |
|---|
| 545 |
|
|---|
| 546 |
Adding custom validation of data in the admin is quite easy. The automatic |
|---|
| 547 |
admin interfaces reuses the Django `forms`_ module. The ``ModelAdmin`` class |
|---|
| 548 |
gives you the ability define your own form:: |
|---|
| 549 |
|
|---|
| 550 |
class ArticleAdmin(admin.ModelAdmin): |
|---|
| 551 |
form = MyArticleAdminForm |
|---|
| 552 |
|
|---|
| 553 |
``MyArticleAdminForm`` can be defined anywhere as long as you import where |
|---|
| 554 |
needed. Now within your form you can add your own custom validation for |
|---|
| 555 |
any field:: |
|---|
| 556 |
|
|---|
| 557 |
class MyArticleAdminForm(forms.ModelForm): |
|---|
| 558 |
def clean_name(self): |
|---|
| 559 |
# do something that validates your data |
|---|
| 560 |
return self.cleaned_data["name"] |
|---|
| 561 |
|
|---|
| 562 |
It is important you use a ``ModelForm`` here otherwise things can break. See |
|---|
| 563 |
the `forms`_ documentation on `custom validation`_ for more information. |
|---|
| 564 |
|
|---|
| 565 |
.. _forms: ../forms/ |
|---|
| 566 |
.. _custom validation: ../forms/#custom-form-and-field-validation |
|---|
| 567 |
|
|---|
| 568 |
``InlineModelAdmin`` objects |
|---|
| 569 |
============================ |
|---|
| 570 |
|
|---|
| 571 |
The admin interface has the ability to edit models on the same page as a |
|---|
| 572 |
parent model. These are called inlines. You can add them to a model by |
|---|
| 573 |
specifying them in a ``ModelAdmin.inlines`` attribute:: |
|---|
| 574 |
|
|---|
| 575 |
class BookInline(admin.TabularInline): |
|---|
| 576 |
model = Book |
|---|
| 577 |
|
|---|
| 578 |
class AuthorAdmin(admin.ModelAdmin): |
|---|
| 579 |
inlines = [ |
|---|
| 580 |
BookInline, |
|---|
| 581 |
] |
|---|
| 582 |
|
|---|
| 583 |
Django provides two subclasses of ``InlineModelAdmin`` and they are: |
|---|
| 584 |
|
|---|
| 585 |
* ``TabularInline`` |
|---|
| 586 |
* ``StackedInline`` |
|---|
| 587 |
|
|---|
| 588 |
The difference between these two is merely the template used to render them. |
|---|
| 589 |
|
|---|
| 590 |
``InlineModelAdmin`` options |
|---|
| 591 |
----------------------------- |
|---|
| 592 |
|
|---|
| 593 |
The ``InlineModelAdmin`` class is a subclass of ``ModelAdmin`` so it inherits |
|---|
| 594 |
all the same functionality as well as some of its own: |
|---|
| 595 |
|
|---|
| 596 |
``model`` |
|---|
| 597 |
~~~~~~~~~ |
|---|
| 598 |
|
|---|
| 599 |
The model in which the inline is using. This is required. |
|---|
| 600 |
|
|---|
| 601 |
``fk_name`` |
|---|
| 602 |
~~~~~~~~~~~ |
|---|
| 603 |
|
|---|
| 604 |
The name of the foreign key on the model. In most cases this will be dealt |
|---|
| 605 |
with automatically, but ``fk_name`` must be specified explicitly if there are |
|---|
| 606 |
more than one foreign key to the same parent model. |
|---|
| 607 |
|
|---|
| 608 |
``formset`` |
|---|
| 609 |
~~~~~~~~~~~ |
|---|
| 610 |
|
|---|
| 611 |
This defaults to ``BaseInlineFormset``. Using your own formset can give you |
|---|
| 612 |
many possibilities of customization. Inlines are built around |
|---|
| 613 |
`model formsets`_. |
|---|
| 614 |
|
|---|
| 615 |
.. _model formsets: ../modelforms/#model-formsets |
|---|
| 616 |
|
|---|
| 617 |
``form`` |
|---|
| 618 |
~~~~~~~~ |
|---|
| 619 |
|
|---|
| 620 |
The value for ``form`` is inherited from ``ModelAdmin``. This is what is |
|---|
| 621 |
passed through to ``formset_factory`` when creating the formset for this |
|---|
| 622 |
inline. |
|---|
| 623 |
|
|---|
| 624 |
``extra`` |
|---|
| 625 |
~~~~~~~~~ |
|---|
| 626 |
|
|---|
| 627 |
This controls the number of extra forms the formset will display in addition |
|---|
| 628 |
to the initial forms. See the `formsets documentation`_ for more information. |
|---|
| 629 |
|
|---|
| 630 |
.. _formsets documentation: ../forms/#formsets |
|---|
| 631 |
|
|---|
| 632 |
``max_num`` |
|---|
| 633 |
~~~~~~~~~~~ |
|---|
| 634 |
|
|---|
| 635 |
This controls the maximum number of forms to show in the inline. This doesn't |
|---|
| 636 |
directly corrolate to the number of objects, but can if the value is small |
|---|
| 637 |
enough. See `max_num in formsets`_ for more information. |
|---|
| 638 |
|
|---|
| 639 |
.. _max_num in formsets: ../modelforms/#limiting-the-number-of-objects-editable |
|---|
| 640 |
|
|---|
| 641 |
``raw_id_fields`` |
|---|
| 642 |
~~~~~~~~~~~~~~~~~ |
|---|
| 643 |
|
|---|
| 644 |
By default, Django's admin uses a select-box interface (<select>) for |
|---|
| 645 |
fields that are ``ForeignKey``. Sometimes you don't want to incur the |
|---|
| 646 |
overhead of having to select all the related instances to display in the |
|---|
| 647 |
drop-down. |
|---|
| 648 |
|
|---|
| 649 |
``raw_id_fields`` is a list of fields you would like to change |
|---|
| 650 |
into a ``Input`` widget for either a ``ForeignKey`` or ``ManyToManyField``:: |
|---|
| 651 |
|
|---|
| 652 |
class BookInline(admin.TabularInline): |
|---|
| 653 |
model = Book |
|---|
| 654 |
raw_id_fields = ("pages",) |
|---|
| 655 |
|
|---|
| 656 |
``template`` |
|---|
| 657 |
~~~~~~~~~~~~ |
|---|
| 658 |
|
|---|
| 659 |
The template used to render the inline on the page. |
|---|
| 660 |
|
|---|
| 661 |
``verbose_name`` |
|---|
| 662 |
~~~~~~~~~~~~~~~~ |
|---|
| 663 |
|
|---|
| 664 |
An override to the ``verbose_name`` found in the model's inner ``Meta`` class. |
|---|
| 665 |
|
|---|
| 666 |
``verbose_name_plural`` |
|---|
| 667 |
~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 668 |
|
|---|
| 669 |
An override to the ``verbose_name_plural`` found in the model's inner ``Meta`` |
|---|
| 670 |
class. |
|---|
| 671 |
|
|---|
| 672 |
Working with a model with two or more foreign keys to the same parent model |
|---|
| 673 |
--------------------------------------------------------------------------- |
|---|
| 674 |
|
|---|
| 675 |
It is sometimes possible to have more than one foreign key to the same model. |
|---|
| 676 |
Take this model for instance:: |
|---|
| 677 |
|
|---|
| 678 |
class Friendship(models.Model): |
|---|
| 679 |
to_person = models.ForeignKey(Person, related_name="friends") |
|---|
| 680 |
from_person = models.ForeignKey(Person, related_name="from_friends") |
|---|
| 681 |
|
|---|
| 682 |
If you wanted to display an inline on the ``Person`` admin add/change pages |
|---|
| 683 |
you need to explicitly define the foreign key since it is unable to do so |
|---|
| 684 |
automatically:: |
|---|
| 685 |
|
|---|
| 686 |
class FriendshipInline(admin.TabularInline): |
|---|
| 687 |
model = Friendship |
|---|
| 688 |
fk_name = "to_person" |
|---|
| 689 |
|
|---|
| 690 |
class PersonAdmin(admin.ModelAdmin): |
|---|
| 691 |
inlines = [ |
|---|
| 692 |
FriendshipInline, |
|---|
| 693 |
] |
|---|
| 694 |
|
|---|
| 695 |
Working with Many-to-Many Intermediary Models |
|---|
| 696 |
---------------------------------------------- |
|---|
| 697 |
|
|---|
| 698 |
By default, admin widgets for many-to-many relations will be displayed inline |
|---|
| 699 |
on whichever model contains the actual reference to the ``ManyToManyField``. |
|---|
| 700 |
However, when you specify an intermediary model using the ``through`` |
|---|
| 701 |
argument to a ``ManyToManyField``, the admin will not display a widget by |
|---|
| 702 |
default. This is because each instance of that intermediary model requires |
|---|
| 703 |
more information than could be displayed in a single widget, and the layout |
|---|
| 704 |
required for multiple widgets will vary depending on the intermediate model. |
|---|
| 705 |
|
|---|
| 706 |
However, we still want to be able to edit that information inline. Fortunately, |
|---|
| 707 |
this is easy to do with inline admin models. Suppose we have the following |
|---|
| 708 |
models:: |
|---|
| 709 |
|
|---|
| 710 |
class Person(models.Model): |
|---|
| 711 |
name = models.CharField(max_length=128) |
|---|
| 712 |
|
|---|
| 713 |
class Group(models.Model): |
|---|
| 714 |
name = models.CharField(max_length=128) |
|---|
| 715 |
members = models.ManyToManyField(Person, through='Membership') |
|---|
| 716 |
|
|---|
| 717 |
class Membership(models.Model): |
|---|
| 718 |
person = models.ForeignKey(Person) |
|---|
| 719 |
group = models.ForeignKey(Group) |
|---|
| 720 |
date_joined = models.DateField() |
|---|
| 721 |
invite_reason = models.CharField(max_length=64) |
|---|
| 722 |
|
|---|
| 723 |
The first step in displaying this intermediate model in the admin is to |
|---|
| 724 |
define an inline class for the ``Membership`` model:: |
|---|
| 725 |
|
|---|
| 726 |
class MembershipInline(admin.TabularInline): |
|---|
| 727 |
model = Membership |
|---|
| 728 |
extra = 1 |
|---|
| 729 |
|
|---|
| 730 |
This simple example uses the default ``InlineModelAdmin`` values for the |
|---|
| 731 |
``Membership`` model, and limits the extra add forms to one. This could be |
|---|
| 732 |
customized using any of the options available to ``InlineModelAdmin`` classes. |
|---|
| 733 |
|
|---|
| 734 |
Now create admin views for the ``Person`` and ``Group`` models:: |
|---|
| 735 |
|
|---|
| 736 |
class PersonAdmin(admin.ModelAdmin): |
|---|
| 737 |
inlines = (MembershipInline,) |
|---|
| 738 |
|
|---|
| 739 |
class GroupAdmin(admin.ModelAdmin): |
|---|
| 740 |
inlines = (MembershipInline,) |
|---|
| 741 |
|
|---|
| 742 |
Finally, register your ``Person`` and ``Group`` models with the admin site:: |
|---|
| 743 |
|
|---|
| 744 |
admin.site.register(Person, PersonAdmin) |
|---|
| 745 |
admin.site.register(Group, GroupAdmin) |
|---|
| 746 |
|
|---|
| 747 |
Now your admin site is set up to edit ``Membership`` objects inline from |
|---|
| 748 |
either the ``Person`` or the ``Group`` detail pages. |
|---|
| 749 |
|
|---|
| 750 |
``AdminSite`` objects |
|---|
| 751 |
===================== |
|---|
| 752 |
|
|---|
| 753 |
Hooking ``AdminSite`` instances into your URLconf |
|---|
| 754 |
------------------------------------------------- |
|---|
| 755 |
|
|---|
| 756 |
The last step in setting up the Django admin is to hook your ``AdminSite`` |
|---|
| 757 |
instance into your URLconf. Do this by pointing a given URL at the |
|---|
| 758 |
``AdminSite.root`` method. |
|---|
| 759 |
|
|---|
| 760 |
In this example, we register the default ``AdminSite`` instance |
|---|
| 761 |
``django.contrib.admin.site`` at the URL ``/admin/`` :: |
|---|
| 762 |
|
|---|
| 763 |
# urls.py |
|---|
| 764 |
from django.conf.urls.defaults import * |
|---|
| 765 |
from django.contrib import admin |
|---|
| 766 |
|
|---|
| 767 |
admin.autodiscover() |
|---|
| 768 |
|
|---|
| 769 |
urlpatterns = patterns('', |
|---|
| 770 |
('^admin/(.*)', admin.site.root), |
|---|
| 771 |
) |
|---|
| 772 |
|
|---|
| 773 |
Above we used ``admin.autodiscover()`` to automatically load the |
|---|
| 774 |
``INSTALLED_APPS`` admin.py modules. |
|---|
| 775 |
|
|---|
| 776 |
In this example, we register the ``AdminSite`` instance |
|---|
| 777 |
``myproject.admin.admin_site`` at the URL ``/myadmin/`` :: |
|---|
| 778 |
|
|---|
| 779 |
# urls.py |
|---|
| 780 |
from django.conf.urls.defaults import * |
|---|
| 781 |
from myproject.admin import admin_site |
|---|
| 782 |
|
|---|
| 783 |
urlpatterns = patterns('', |
|---|
| 784 |
('^myadmin/(.*)', admin_site.root), |
|---|
| 785 |
) |
|---|
| 786 |
|
|---|
| 787 |
There is really no need to use autodiscover when using your own ``AdminSite`` |
|---|
| 788 |
instance since you will likely be importing all the per-app admin.py modules |
|---|
| 789 |
in your ``myproject.admin`` module. |
|---|
| 790 |
|
|---|
| 791 |
Note that the regular expression in the URLpattern *must* group everything in |
|---|
| 792 |
the URL that comes after the URL root -- hence the ``(.*)`` in these examples. |
|---|
| 793 |
|
|---|
| 794 |
Multiple admin sites in the same URLconf |
|---|
| 795 |
---------------------------------------- |
|---|
| 796 |
|
|---|
| 797 |
It's easy to create multiple instances of the admin site on the same |
|---|
| 798 |
Django-powered Web site. Just create multiple instances of ``AdminSite`` and |
|---|
| 799 |
root each one at a different URL. |
|---|
| 800 |
|
|---|
| 801 |
In this example, the URLs ``/basic-admin/`` and ``/advanced-admin/`` feature |
|---|
| 802 |
separate versions of the admin site -- using the ``AdminSite`` instances |
|---|
| 803 |
``myproject.admin.basic_site`` and ``myproject.admin.advanced_site``, |
|---|
| 804 |
respectively:: |
|---|
| 805 |
|
|---|
| 806 |
# urls.py |
|---|
| 807 |
from django.conf.urls.defaults import * |
|---|
| 808 |
from myproject.admin import basic_site, advanced_site |
|---|
| 809 |
|
|---|
| 810 |
urlpatterns = patterns('', |
|---|
| 811 |
('^basic-admin/(.*)', basic_site.root), |
|---|
| 812 |
('^advanced-admin/(.*)', advanced_site.root), |
|---|
| 813 |
) |
|---|