3. Django admin site - LiVanych/locallibrary GitHub Wiki

Read original article

All the configuration required to include the admin application in your website was done automatically when you created the skeleton project (for information about actual dependencies needed, see the Django docs here). As a result, all you must do to add your models to the admin application is to register them. At the end of this article we'll provide a brief demonstration of how you might further configure the admin area to better display our model data.

After registering the models we'll show how to create a new "superuser", login to the site, and create some books, authors, book instances, and genres. These will be useful for testing the views and templates we'll start creating in the next tutorial.

Registering models

vim catalog/admin.py
from django.contrib import admin

# Register your models here.
from catalog.models import Author, Genre, Book, BookInstance, Language

admin.site.register(Book)
admin.site.register(Author)
admin.site.register(Genre)
admin.site.register(BookInstance)
admin.site.register(Language)

Creating a superuser

python3 manage.py createsuperuser
python3 manage.py runserver

Logging in and using the site

http://127.0.0.1:8000/admin/

Add books, authors, genres, languages and bookInstances.

Advanced configuration

You can further customise the interface to make it even easier to use. Some of the things you can do are:

List views: Add additional fields/information displayed for each record. Add filters to select which records are listed, based on date or some other selection value (e.g. Book loan status). Add additional options to the actions menu in list views and choose where this menu is displayed on the form.

Detail views: Choose which fields to display (or exclude), along with their order, grouping, whether they are editable, the widget used, orientation etc. Add related fields to a record to allow inline editing (e.g. add the ability to add and edit book records while you're creating their author record).

You can find a complete reference of all the admin site customisation choices in The Django Admin site (Django Docs).

Register a ModelAdmin class

To change how a model is displayed in the admin interface you define a ModelAdmin class (which describes the layout) and register it with the model.

Let's start with the Author model. Open admin.py in the catalog application (/locallibrary/catalog/admin.py). Comment out your original registration (prefix it with a #) for the Author model:

# admin.site.register(Author)

Now add a new AuthorAdmin and registration as shown below.

# Define the admin class
class AuthorAdmin(admin.ModelAdmin):
    pass

# Register the admin class with the associated model
admin.site.register(Author, AuthorAdmin)

Now we'll add ModelAdmin classes for Book, and BookInstance. We again need to comment out the original registrations:

# admin.site.register(Book)
# admin.site.register(BookInstance)

Now to create and register the new models; for the purpose of this demonstration, we'll instead use the @register decorator to register the models (this does exactly the same thing as the admin.site.register() syntax):

# Register the Admin classes for Book using the decorator
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
    pass


# Register the Admin classes for BookInstance using the decorator
@admin.register(BookInstance) 
class BookInstanceAdmin(admin.ModelAdmin):
    pass

Currently all of our admin classes are empty (see pass) so the admin behaviour will be unchanged! We can now extend these to define our model-specific admin behaviour.

Configure list views

Replace your AuthorAdmin class with the code below. The field names to be displayed in the list are declared in a tuple in the required order, as shown (these are the same names as specified in your original model).

class AuthorAdmin(admin.ModelAdmin):
    list_display = ('last_name', 'first_name', 'date_of_birth', 'date_of_death')

For our Book model we'll additionally display the author and genre. The author is a ForeignKey field (one-to-one) relationship, and so will be represented by the __str__() value for the associated record. Replace the BookAdmin class with the version below.

class BookAdmin(admin.ModelAdmin):
    list_display = ('title', 'author', 'display_genre')

Unfortunately we can't directly specify the genre field in list_display because it is a ManyToManyField (Django prevents this because there would be a large database access "cost" in doing so). Instead we'll define a display_genre function to get the information as a string (this is the function we've called above; we'll define it below).

Add the following code into your Book model (models.py). This creates a string from the first three values of the genre field (if they exist) and creates a short_description that can be used in the admin site for this method.

def display_genre(self):
        """Create a string for the Genre. 
           This is required to display genre in Admin.
        """
        return ', '.join(genre.name for genre in self.genre.all()[:3])

display_genre.short_description = 'Genre'

Add list filters

Once you've got a lot of items in a list, it can be useful to be able to filter which items are displayed. This is done by listing fields in the list_filter attribute. Replace your current BookInstanceAdmin class with the code fragment below.

class BookInstanceAdmin(admin.ModelAdmin):
    list_display = ('book','id', 'imprint', 'status', 'due_back')
    list_filter = ('status', 'due_back')

Organise detail view layout

By default, the detail views lay out all fields vertically, in their order of declaration in the model. You can change the order of declaration, which fields are displayed (or excluded), whether sections are used to organise the information, whether fields are displayed horizontally or vertically, and even what edit widgets are used in the admin forms.

Controlling which fields are displayed and laid out

Update your AuthorAdmin class to add the fields line, as shown below:

class AuthorAdmin(admin.ModelAdmin):
    list_display = (
                    'last_name', 'first_name', 
                    'date_of_birth', 'date_of_death'
                   )    
    fields = ['first_name', 'last_name', ('date_of_birth', 'date_of_death')]

Note: You can also use the exclude attribute to declare a list of attributes to be excluded from the form (all other attributes in the model will be displayed).

class AuthorAdmin(admin.ModelAdmin):
    list_display = ('last_name', 'first_name', 'date_of_birth', 'date_of_death')
    exclude = ('date_of_death',)

Sectioning the detail view

You can add "sections" to group related model information within the detail form, using the fieldsets attribute.

In the BookInstance model we have information related to what the book is (i.e. name, imprint, and id) and when it will be available (status, due_back). We can add these in different sections by adding the text to our BookInstanceAdmin class.

@admin.register(BookInstance)
class BookInstanceAdmin(admin.ModelAdmin):
    list_display = ('book','id', 'imprint', 'status', 'due_back')
    list_filter = ('status', 'due_back')
    
    fieldsets = (
        (None, {
            'fields': ('book', 'imprint', 'id')
        }),
        ('Availability', {
            'fields': ('status', 'due_back')
        }),
    )

Inline editing of associated records

Sometimes it can make sense to be able to add associated records at the same time. For example, it may make sense to have both the book information and information about the specific copies you've got on the same detail page.

You can do this by declaring inlines, of type TabularInline (horizonal layout) or StackedInline (vertical layout, just like the default model layout). You can add the BookInstance information inline to our Book detail by adding the lines below near your BookAdmin:

class BooksInstanceInline(admin.TabularInline):
    model = BookInstance 
    extra = 0

@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
    list_display = ('title', 'author', 'display_genre')
    inlines = [BooksInstanceInline]

The same for Book and Author models:

class BookInline(admin.TabularInline):
    model = Book
    extra = 0
# Define the admin class
class AuthorAdmin(admin.ModelAdmin):
    list_display = ('last_name', 'first_name', 'date_of_birth', 'date_of_death')
    inlines = [BookInline]

Further reading

Writing your first Django app, part 2: Introducing the Django Admin (Django docs)

The Django Admin site (Django Docs)

Read original article on MDN site