Django Admin: How to Set a Default Filter Without Conflicting with User-Selected Filters?

I'm working with the Django Admin panel and need to set a default filter for a model's changelist view. When the user opens the changelist page for the first time, I want it to automatically filter by a specific value (e.g., status="A"). However, when a user selects a different filter (e.g., status="B"), the default filter (status="A") should not be applied.

What I Have Tried: I've overridden the changelist_view method in my ModelAdmin class to check if any status filter is present in the request. If not, I add the default filter (status=A). Here’s what my code looks like:

from django.contrib import admin
from django.shortcuts import redirect
from django.urls import reverse
from .models import Person  # Replace with your actual model

class PersonAdmin(admin.ModelAdmin):
    list_display = ('name', 'status')  # Adjust fields to match your model
    list_filter = ('status',)  # Adjust filters to match your model

    def get_queryset(self, request):
        # Get the original queryset without any default filtering
        qs = super().get_queryset(request)
        return qs

    def changelist_view(self, request, extra_context=None):
        # Check if any filters related to 'status' are already applied by looking at request.GET
        if 'status' not in request.GET and 'status__exact' not in request.GET:
            # Redirect to the same changelist URL with the default filter applied
            query = request.GET.copy()  # Make a mutable copy of GET parameters
            query['status'] = 'A'  # Set the default filter value for 'status' to 'A'
            
            # Redirect to the changelist URL with the correct query string
            return redirect(
                f"{reverse('admin:Human_Resource_Management_person_changelist')}?{query.urlencode()}"
            )

        # Call the original changelist_view method
        return super().changelist_view(request, extra_context=extra_context)

# Register your admin class with the associated model
admin.site.register(Person, PersonAdmin)

The code works initially: when the changelist view loads without any filters, it defaults to status=A. However, if a user selects another filter, such as status=B, the URL ends up looking like this: ?status=A&status__exact=B This results in both filters being applied simultaneously, which is incorrect. I want to only have the user-selected filter (e.g., status=B) applied and not keep the default filter (status=A) in the URL.

What I've Tried: I tried modifying request.GET directly, but that caused an AttributeError since request.GET is immutable. I also tried more complex condition checks, but I can't seem to prevent the status=A filter from staying in the URL when another filter is selected. What I'm Looking For: I'm looking for a solution that:

Applies the default filter (status=A) only when no other status filter is present. Removes the default filter when the user selects a different filter (status=B) to avoid conflicting filters in the URL. Any insights or suggestions on how to achieve this would be greatly appreciated!

Thank you!

The filter throws out the previous one with the same name. But you use status, not status__exact, hence the problem. Use status__exact instead:

class PersonAdmin(admin.ModelAdmin):
    # …

    def changelist_view(self, request, extra_context=None):
        if 'status' not in request.GET and 'status__exact' not in request.GET:
            # Redirect to the same changelist URL with the default filter applied
            query = request.GET.copy()  # Make a mutable copy of GET parameters
            query['status__exact'] = 'A'  # Set the default filter value for 'status' to 'A'

            # Redirect to the changelist URL with the correct query string
            return redirect(
                f"{reverse('admin:Human_Resource_Management_person_changelist')}?{query.urlencode()}"
            )

        # Call the original changelist_view method
        return super().changelist_view(request, extra_context=extra_context)
Back to Top