Customization

While Grappelli is mainly about the look & feel of the admin interface, it also adds some features.

Available Settings

New in version 2.4.1: Added setting AUTOCOMPLETE_LIMIT

GRAPPELLI_ADMIN_TITLE
The Site Title of your admin interface. Change this instead of changing index.html
AUTOCOMPLETE_LIMIT
Number of items to show with autocomplete drop-downs.

Collapsibles

Changed in version 2.4.0: Added namespace grp-.

Use the classes property in order to define collapsibles for a ModelAdmin or an InlineModelAdmin. Possible values are grp-collapse grp-open and grp-collapse grp-closed.

A ModelAdmin example:

class ModelOptions(admin.ModelAdmin):
    fieldsets = (
        ('', {
            'fields': ('title', 'subtitle', 'slug', 'pub_date', 'status',),
        }),
        ('Flags', {
            'classes': ('grp-collapse grp-closed',),
            'fields' : ('flag_front', 'flag_sticky', 'flag_allow_comments', 'flag_comments_closed',),
        }),
        ('Tags', {
            'classes': ('grp-collapse grp-open',),
            'fields' : ('tags',),
        }),
    )

With StackedInlines, an additional property inline_classes is available to define the default collapsible state of the inline items (in contrast to the whole group):

class NavigationItemInline(admin.StackedInline):
    classes = ('grp-collapse grp-open',)
    inline_classes = ('grp-collapse grp-open',)

Inline Sortables

New in version 2.3.

For using drag/drop with Inlines, you need to add a PositiveIntegerField to your Model:

class MyInlineModel(models.Model):
    mymodel = models.ForeignKey(MyModel)
    # position field
    position = models.PositiveSmallIntegerField("Position")
    class Meta:
        ordering = ['position']

Now, define the sortable_field_name with your InlineModelAdmin:

class MyInlineModelOptions(admin.TabularInline):
    fields = (... , "position",)
    # define the sortable
    sortable_field_name = "position"

The inline-rows are being reordered based on the sortable-field (with a templatetag formsetsort). When submitting a form, the values of the sortable-field are re-indexed according to the position of each row. In case of errors (somewhere within the form), the position of inline-rows are being preserved. This applies to rows prepeared for deletion as well. Empty rows are moved to the end of the formset.

Sortable Excludes

New in version 2.4.

You may want to define sortable_excludes (either list or tuple) in order to exclude certain fields from having an effect on the position field. This is especially useful if a field has a default value:

class MyInlineModelOptions(admin.TabularInline):
    fields = (... , "position",)
    # define the sortable
    sortable_field_name = "position"
    # define sortable_excludes
    sortable_excludes = ("field_1", "field_2",)

Autocomplete Lookups

Changed in version 2.3.5: staticmethod autocomplete_search_fields is required, related_autocomplete_lookup has been removed.

New in version 2.3.4: autocomplete_lookup_fields.

Autocomplete Lookups are an alternative to Related Lookups (for Foreign Keys, Many-to-Many relations and Generic relations).

Add the staticmethod autocomplete_search_fields to all models you want to search for:

class MyModel(models.Model):
    name = models.CharField(u"Name", max_length=50)

    @staticmethod
    def autocomplete_search_fields():
        return ("id__iexact", "name__icontains",)

Defining autocomplete lookups is very similar to related lookups:

class MyModel(models.Model):
    related_fk = models.ForeignKey(RelatedModel, verbose_name=u"Related Lookup (FK)")
    related_m2m = models.ManyToManyField(RelatedModel, verbose_name=u"Related Lookup (M2M)")

class MyModelOptions(admin.ModelAdmin):
    # define the raw_id_fields
    raw_id_fields = ('related_fk','related_m2m',)
    # define the autocomplete_lookup_fields
    autocomplete_lookup_fields = {
        'fk': ['related_fk'],
        'm2m': ['related_m2m'],
    }

This also works with generic relations:

from django.contrib.contenttypes import generic
from django.contrib.contenttypes.models import ContentType
from django.db import models

class MyModel(models.Model):
    # first generic relation
    content_type = models.ForeignKey(ContentType, blank=True, null=True, related_name="content_type")
    object_id = models.PositiveIntegerField(blank=True, null=True)
    content_object = generic.GenericForeignKey("content_type", "object_id")
    # second generic relation
    relation_type = models.ForeignKey(ContentType, blank=True, null=True, related_name="relation_type")
    relation_id = models.PositiveIntegerField(blank=True, null=True)
    relation_object = generic.GenericForeignKey("relation_type", "relation_id")

class MyModelOptions(admin.ModelAdmin):
    # define the autocomplete_lookup_fields
    autocomplete_lookup_fields = {
        'generic': [['content_type', 'object_id'], ['relation_type', 'relation_id']],
    }

If your generic relation points to a model using a custom primary key, you need to add a property id:

class RelationModel(models.Model):
    cpk  = models.IntegerField(primary_key=True, unique=True, editable=False)

    @property
    def id(self):
        return self.cpk

For the represantation of an object, we first check for a callable related_label. If not given, __unicode__ is being used:

def __unicode__(self):
    return u"%s" % self.name

def related_label(self):
    return u"%s (%s)" % (self.name, self.id)

Warning

Due to a bug in Django 1.4, raw_id_fields (including autocomplete-lookups) are not working with list_editables.

Using TinyMCE

Changed in version 2.4: The admin media URLs has been changed to use a static URLs in compliance with Django 1.4

Grappelli already comes with TinyMCE and a minimal theme as well. In order to use TinyMCE, you need to copy tinymce_setup.js to your static-directory, adjust the setup (see TinyMCE Configuration) and add the necessary javascripts to your ModelAdmin definition (see ModelAdmin Media definitions):

class Media:
    js = [
        '/static/admin/tinymce/jscripts/tiny_mce/tiny_mce.js',
        '/static/path/to/your/tinymce_setup.js',
    ]

Using TinyMCE with Inlines is a bit more tricky because of the hidden empty-form. You need to write a custom template and use the inline-callbacks to

  • onInit: remove TinyMCE instances from the the empty-form.
  • onAfterAdded: initialize TinyMCE instance(s) from the form.
  • onBeforeRemoved: remove TinyMCE instance(s) from the form.

Note

TinyMCE with Inlines is not supported by default.

Changelist Templates

New in version 2.4.2.

Grappelli comes with 2 different change–list templates. The standard template shows filters with a drop–down, the alternative template shows filters on the right hand side of the results (similar to djangos admin interface).

To use the alternative template, you need to add change_list_template to your ModelAdmin definition:

class MyModelOptions(admin.ModelAdmin):
    change_list_template = "admin/change_list_filter_sidebar.html"

Changelist Filters

New in version 2.4.2.

Grappelli comes with 2 different change–list filters. The standard filters are drop–downs, the alternative filters are list of options (similar to djangos admin interface).

To use the alternative filters, you need to add change_list_filter_template to your ModelAdmin definition:

class MyModelOptions(admin.ModelAdmin):
    change_list_filter_template = "admin/filter_list.html"