Making the most of the Django admin Simon Willison PyCon UK, 13th September 2008 ============================== Topics * What's new in the admin in Django 1.0 * Configuring the admin * Customisation options * Inline editing * Advanced customisation * Future plans ============================== New in 1.0: Decoupling! Old style: class Author(models.Model): first_name = models.CharField(maxlength=30) last_name = models.CharField(maxlength=30) slug = models.CharField(maxlength=60, prepopulate_from=('first_name', 'last_name') ) class Admin: list_display = ['first_name', 'last_name'] def __str__(self): return self.first_name + ' ' + self.last_name ============================== New in 1.0: Decoupling! New style: class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) slug = models.CharField(max_length=60) def __unicode__(self): return u'%s %s' % (self.first_name, self.last_name) # admin.py from django.contrib import admin from models import Author admin.site.register(Author, list_display = ('first_name', 'last_name'), prepopulated_fields = { 'slug': ('first_name', 'last_name') } ) ============================== Configuring the Admin 1. Add 'django.contrib.admin' to INSTALLED_APPS, syncdb 2. Create admin.py files to register your models 3. Call admin.autodiscover() from urls.py 4. Add (r'^admin/(.*)', admin.site.root) to urls.py ============================== Customisation options: * Configure the changelist view list_display list_filter search_fields date_hierarchy list_per_page * Configure the modify interface fields, exclude, fieldsets filter_horizontal, filter_vertical prepopulated_fields radio_fields raw_id_fields inlines ============================== Inline editing * Common case: parent object, multiple child objects * For example: * A menu has multiple menu items * A conference track has multiple sessions * A football team has multiple players * "Inlines" let you edit an object and its child objects all on the same page. ============================== Inline editing class MaterialInline(admin.StackedInline): model = Material admin.site.register(Talk, inlines = [MaterialInline], ) ============================== Custom form validation from django import forms class EntryForm(forms.ModelForm): model = Entry def clean_title(self): if 'badword' in self.cleaned_data['title']: raise forms.ValidationError('Bad word!') return self.cleaned_data['title'] admin.site.register(Entry, form = EntryForm) ============================== Advanced customisation admin.site.register(Author, list_display = ('first_name', 'last_name'), prepopulated_fields = { 'slug': ('first_name', 'last_name') } ) Is equivalent to: class AuthorAdmin(admin.ModelAdmin): list_display = ('first_name', 'last_name') prepopulated_fields = { 'slug': ('first_name', 'last_name') } admin.site.register(Author, AuthorAdmin) ============================== Advanced customisation * You can over-ride methods on the ModelAdmin class queryset has_add_permission has_change_permission has_delete_permission save_model ... ============================== Become one with the source code * django/contrib/admin/options.py * django/contrib/admin/sites.py * django/contrib/admin/templates/... ============================== Custom JavaScript class MenuForm(forms.ModelForm): model = Menu class Media: js = ( '/static/js/jquery-latest.js', '/static/js/ui.base.js', '/static/js/ui.sortable.js', '/static/js/menu-sort.js', ) admin.site.register(Menu, form = MenuForm) ============================== Custom templates templates/admin/multiblog/entry/change_form.html {% extends "admin/change_form.html" %} {% block after_field_sets %} {% if original %} Created on {{ original.created|date:"jS F Y H:i" }} {% endif %} {% endblock %} ============================== Don't be afraid to add your own custom views: urlpatterns = patterns('', (r'^admin/reorder-everything/$', my_reorder_view), (r'^admin/(.*)', admin.site.root), ) ============================== Future Plans * Fix filters * Class-based views and inheritance hooks are a Good Idea - we should do more of those! * "Class-based generic views" * Eventual aim: construct the admin entirely from class-based generic views, all of which can be used elsewhere * The changelist is going to be particularly useful here