Django’s InlineModelAdmin provides a convenient way to view and edit related objects directly on a parent model’s admin page. When dealing with models that have a large number of related objects, you may want to limit how many are displayed to improve performance and usability. While this is possible, unfortunately Django does not provide a straightforward way to accomplish this.

Attempt #1: Setting InlineModelAdmin.max_num

You might think that setting max_num on the InlineModelAdmin would limit the number of related objects shown:

class BookInline(admin.TabularInline):
    model = Book
    max_num = 10  # Does not work, all books are displayed!


class AuthorAdmin(admin.ModelAdmin):
    inlines = [
        BookInline,
    ]

However, max_num only limits the maximum number of forms to show in the inline and does not prevent existing objects from being displayed. Even with max_num set to a lower value than the number of objects, Django will still show all related objects.

Attempt #2: Overriding InlineModelAdmin.get_queryset()

Another approach you might consider is overriding the get_queryset() method on the InlineModelAdmin:

class BookInline(admin.TabularInline):
    model = Book

    def get_queryset(self, request):
        queryset = super().get_queryset(request)
        return queryset[:10]  # Raises TypeError!

But this approach leads to an error when you visit the admin page:

TypeError: Cannot filter a query once a slice has been taken

This is because when the inline and the accompanying inline formset are instantiated by the ModelAdmin, the queryset that is passed to the formset is filtered by the parent object:

# Excerpt from `django/forms/models.py`

class BaseInlineFormSet(BaseModelFormSet):
    def __init__(self, ...):
        ...
        if self.instance.pk is not None:
            qs = queryset.filter(**{self.fk.name: self.instance})
        ...

Since the queryset is already sliced, you cannot call .filter() on it. There is a workaround for this:

class BookInline(admin.TabularInline):
    model = Book

    def get_queryset(self, request):
        queryset = super().get_queryset(request)
        ids = queryset.values_list('id', flat=True)[:10]
        # First 10 books, but not filtered by the current author!
        return queryset.filter(id__in=ids)

Unfortunately, this approach also fails but in a more subtle way. The queryset returned by the get_queryset() method of the superclass is not filtered by the parent object. This means you’re retrieving the first 10 objects from all instances of the related model, not just those linked to the current parent.

Solution: Overriding ModelAdmin.get_formset_kwargs()

To properly solve this problem, we need to find a place to override the inline queryset where both the parent object and the queryset are accessible. The get_formset_kwargs() method of ModelAdmin provides exactly what we need:

class BookInline(admin.TabularInline):
    model = Book


class AuthorAdmin(admin.ModelAdmin):
    inlines = [
        BookInline,
    ]

    def get_formset_kwargs(self, request, obj, inline, prefix) -> dict:
        formset_kwargs = super().get_formset_kwargs(request, obj, inline, prefix)
        # Only apply the queryset limiting to the BookInline
        if isinstance(inline, BookInline):
            queryset = formset_kwargs['queryset']
            # At this point, `queryset` is equivalent to `Book.objects.all()`.
            # Filter by the current author and retrieve the first 10 object IDs
            ids = queryset.filter(author=obj).values_list('id', flat=True)[:10]
            # Create a new queryset which is effectively limited
            # to the first 10 related objects.
            formset_kwargs['queryset'] = queryset.filter(id__in=ids)
        return formset_kwargs