There’s one crucial part we haven’t done yet. Let’s add our own models to the admin site, so we can add, change and delete objects in our custom database tables using this nice interface. We’ll continue the
books example from Chapter 4, where we defined three models: Publisher, Author and Book. Within the
books directory (
startapp should have created a file called
admin.py, if not, simply create one yourself and type in the following lines of code:
from django.contrib import admin from .models import Publisher, Author, Book admin.site.register(Publisher) admin.site.register(Author) admin.site.register(Book)
This code tells the Django admin site to offer an interface for each of these models. Once you’ve done this, go to your admin home page in your web browser (
http://127.0.0.1:8000/admin/), and you should see a "Books" section with links for Authors, Books and Publishers. You might have to stop and start the development server for the changes to take effect.
You now have a fully functional admin interface for each of those three models. That was easy!
Take some time to add and change records, to populate your database with some data. If you followed Chapter 4’s examples of creating
Publisher objects (and you didn’t delete them), you’ll already see those records on the publisher change list page.
One feature worth mentioning here is the admin site’s handling of foreign keys and many-to-many relationships, both of which appear in the
Book model. As a reminder, here’s what the
Book model looks like:
class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField() def __str__(self): return self.title
On the Django admin site’s "Add book" page (
http://127.0.0.1:8000/admin/books/book/add/), the publisher (a
ForeignKey) is represented by a select box, and the authors field (a
ManyToManyField) is represented by a multiple-select box. Both fields sit next to a green plus sign icon that lets you add related records of that type.
For example, if you click the green plus sign next to the "Publisher" field, you’ll get a pop-up window that lets you add a publisher. After you successfully create the publisher in the pop-up, the "Add book" form will be updated with the newly created publisher. Slick.
Making Fields Optional
After you play around with the admin site for a while, you’ll probably notice a limitation – the edit forms require every field to be filled out, whereas in many cases you’d want certain fields to be optional. Let’s say, for example, that we want our
To specify that the
Author model (which, as you’ll recall from Chapter 4, lives in
mysite/books/models.py). Simply add
blank=True to the
class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField(blank=True)
This tells Django that a blank value is indeed allowed for authors’ e-mail addresses. By default, all fields have
blank=False, which means blank values are not allowed.
There’s something interesting happening here. Until now, with the exception of the
__str__() method, our models have served as definitions of our database tables – Pythonic expressions of SQL
CREATE TABLE statements, essentially. In adding
blank=True, we have begun expanding our model beyond a simple definition of what the database table looks like.
Now, our model class is starting to become a richer collection of knowledge about what
objects are and what they can do. Not only is the
column in the database; it’s also an optional field in contexts such as the Django admin site.
Once you’ve added that
blank=True, reload the "Add author" edit form (
http://127.0.0.1:8000/admin/books/author/add/), and you’ll notice the field’s label – "Email" – is no longer bolded. This signifies it’s not a required field. You can now add authors without needing to provide e-mail addresses; you won’t get the loud red "This field is required" message anymore, if the field is submitted empty.
Making Date and Numeric Fields Optional
A common gotcha related to
blank=True has to do with date and numeric fields, but it requires a fair amount of background explanation. SQL has its own way of specifying blank values – a special value called
NULL could mean "unknown," or "invalid," or some other application-specific meaning. In SQL, a value of
NULL is different than an empty string, just as the special Python object
None is different than an empty Python string (
This means it’s possible for a particular character field (e.g., a
VARCHAR column) to contain both
NULL values and empty string values. This can cause unwanted ambiguity and confusion: "Why does this record have a
NULL but this other one has an empty string? Is there a difference, or was the data just entered inconsistently?" And: "How do I get all the records that have a blank value – should I look for both
NULL records and empty strings, or do I only select the ones with empty strings?"
To help avoid such ambiguity, Django’s automatically generated
CREATE TABLE statements (which were covered in Chapter 4) add an explicit
NOT NULL to each column definition. For example, here’s the generated statement for our
Author model, from Chapter 4:
CREATE TABLE "books_author" ( "id" serial NOT NULL PRIMARY KEY, "first_name" varchar(30) NOT NULL, "last_name" varchar(40) NOT NULL, "email" varchar(75) NOT NULL );
In most cases, this default behavior is optimal for your application and will save you from data-inconsistency headaches. And it works nicely with the rest of Django, such as the Django admin site, which inserts an empty string (not a
NULL value) when you leave a character field blank.
But there’s an exception with database column types that do not accept empty strings as valid values – such as dates, times and numbers. If you try to insert an empty string into a date or integer column, you’ll likely get a database error, depending on which database you’re using. (PostgreSQL, which is strict, will raise an exception here; MySQL might accept it or might not, depending on the version you’re using, the time of day and the phase of the moon.)
In this case,
NULL is the only way to specify an empty value. In Django models, you specify that
NULL is allowed by adding
null=True to a field. So that’s a long way of saying this – if you want to allow blank values in a date field (e.g.,
DateTimeField) or numeric field (e.g.,
FloatField), you’ll need to use both
For example, let’s change our
Book model to allow a blank
publication_date. Here’s the revised code:
class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField(blank=True, null=True)
null=True is more complicated than adding
null=True changes the semantics of the database – that is, it changes the
CREATE TABLE statement to remove the
NOT NULL from the
publication_date field. To complete this change, we’ll need to update the database.
For a number of reasons, Django does not attempt to automate changes to database schemas, so it’s your own responsibility to execute the
python manage.py migrate command whenever you make such a change to a model. Bringing this back to the admin site, now the "Add book" edit form should allow for empty publication date values.
Customizing Field Labels
On the admin site’s edit forms, each field’s label is generated from its model field name. The algorithm is simple – Django just replaces underscores with spaces and capitalizes the first character, so, for example, the
publication_date field has the label "Publication date."
However, field names don’t always lend themselves to nice admin field labels, so in some cases you might want to customize a label. You can do this by specifying
verbose_name in the appropriate model field. For example, here’s how we can change the label of the
Author.email field to "e-mail," with a hyphen:
class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField(blank=True, verbose_name ='e-mail')
Make that change and reload the server, and you should see the field’s new label on the author edit form. Note that you shouldn’t capitalize the first letter of a
verbose_name unless it should always be capitalized (e.g.,
"USA state"). Django will automatically capitalize it when it needs to, and it will use the exact
verbose_name value in other places that don’t require capitalization.
Custom ModelAdmin classes
The changes we’ve made so far –
verbose_name – are really model-level changes, not admin-level changes. That is, these changes are a fundamental part of the model and just so happen to be used by the admin site; there’s nothing admin-specific about them.
Beyond these, the Django admin site offers a wealth of options that let you customize how the admin site works for a particular model. Such options live in ModelAdmin classes, which are classes that contain configuration for a specific model in a specific admin site instance.