Search code examples
pythondjangodjango-admindjango-filtersdjango-admin-filters

Django admin - using autocomplete and filter on choices fields (not ManyToMany or ForeignKey)


This is my model:

class Academic(models.Model):

SCHOOL_COLLEGE_SERVICE = [
    ('School Of Humanities', 'School Of Humanities'),
    ('School Of Culture & Creative Arts', 'School Of Culture & Creative Arts'),
    ('School Of Modern Languages & Cultures', 'School Of Modern Languages & Cultures'),
    ('School Of Critical Studies', 'School Of Critical Studies'),
    ('Arts  College Of Arts Administration', 'Arts  College Of Arts Administration'),
    ]

school = models.CharField(choices=SCHOOL_COLLEGE_SERVICE, max_length=50, blank=True, null=True)

I'd like to have a nice autocomplete / filter in my Django administration interface. Unfortunately it seems that it is not possible to have autocomplete if the dataset doesn't come from a ManyToMany or ForeignKey relationship. This is what I tried:

from django.contrib import admin
from .models import Academic, Partner, Project
from admin_auto_filters.filters import AutocompleteFilter
import django_filters

@admin.register(Academic)
class AcademicAdmin(admin.ModelAdmin):
    search_fields = ['surname', 'forename']
    #school = django_filters.ChoiceFilter(choices=Academic.SCHOOL_COLLEGE_SERVICE)
    #autocomplete_fields = ['school']

I know I can also set a queryset like so:

class SchoolFilter(django_filters.FilterSet):
    class Meta:
        model = Academic
        fields = ['school',]

But Django still complains that The value of 'autocomplete_fields[0]' must be a foreign key or a many-to-many field. How can I achieve what I want?


Solution

  • You can make a form choicefield as a autocomplete dropdown easily using django-autocomplete-light package. This package also supports queryset.

    In forms.py,

    class AcademicForm(forms.ModelForm):
        SCHOOL_COLLEGE_SERVICE = [
        ('School Of Humanities', 'School Of Humanities'),
        ('School Of Culture & Creative Arts', 'School Of Culture & Creative Arts'),
        ('School Of Modern Languages & Cultures', 'School Of Modern Languages & Cultures'),
        ('School Of Critical Studies', 'School Of Critical Studies'),
        ('Arts  College Of Arts Administration', 'Arts  College Of Arts Administration'),
        ]
        school = forms.ChoiceField(choices=SCHOOL_COLLEGE_SERVICE,
                                   widget=autocomplete.ListSelect2(url='school_autocomplete'))
        
        class Meta:
            model = Academic
            fields = ['school', 'other_your_model_fields']
    

    In urls.py,

    urlpatterns = [
        path('school-autocomplete', SchoolAutocompleteView.as_view(), name='school_autocomplete'),
        ]
    

    In views.py,

    from dal import autocomplete
    
    class SchoolAutocompleteView(autocomplete.Select2ListView):
    
        def get_list(self):
            if not self.request.user.is_authenticated:
                return []
    
            SCHOOL_COLLEGE_SERVICE = ['School Of Humanities',
                                      'School Of Culture & Creative Arts',
                                      'School Of Modern Languages & Cultures',
                                      'School Of Critical Studies',
                                      'Arts  College Of Arts Administration']
    
            return SCHOOL_COLLEGE_SERVICE
    

    Now you use modelform in your admin class,

    @admin.register(Academic)
    class AcademicAdmin(admin.ModelAdmin):
        form = AcademicForm
        ....
        ....
    

    for more details, doc_link- https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html