Search code examples
python-3.xdjangodjango-modelsdjango-admin

Restrict output of fields in Django admin according to previous selection


I have this model.py:

class Funder(models.Model):
    name = models.CharField(max_length=200)
    scheme = models.ManyToManyField('Scheme', blank=True)
    
class Scheme(models.Model):
    name = models.CharField(max_length=200))

class Project(models.Model):

    title = models.CharField(max_length=200)
    funder = models.ForeignKey(Funder)
    scheme = models.ForeignKey(Scheme, on_delete=models.SET_NULL)

A funder can have 0, 1, or many schemes attached to it. I'd like to limit the choice of schemes that can be selected from the admin form for project to only the schemes that belong to a specific funder. Is that possible?

Example:

in 'new project', people select funder1, and only see scheme1, scheme3, scheme5 in the drop-down for scheme, because scheme2 and scheme4 are associated to funder2.

Is this something that can be obtained with a QuerySet?

My ModelAdmin:

from import_export.admin import ImportExportModelAdmin
class FunderAdmin(ImportExportModelAdmin):
    search_fields = ['name',]
    autocomplete_fields = ['scheme']
    list_display = ("name",)

Solution

  • I can recommend smart-selects. I can immediately warn you in settings.py use USE_DJANGO_JQUERY = True instead of: JQUERY_URL = True. I checked your models, the scheme selection field is limited depending on which funder is selected.

    models.py

    from smart_selects.db_fields import ChainedForeignKey
    
    
    class Project(models.Model):
        title = models.CharField(max_length=200)
        funder = models.ForeignKey(Funder, on_delete=models.PROTECT)
        scheme = ChainedForeignKey(
            Scheme,
            chained_field="funder",
            chained_model_field="funder",
            show_all=False,
            auto_choose=True,
            sort=True, null=True)
    
    
        def __str__(self):
            return self.title
    

    urls.py

    urlpatterns = [
        path('admin/', admin.site.urls),
        path(r'^chaining/', include('smart_selects.urls')),
    ]
    

    Alternatively, you can pass the data to the javascript function and try to filter it. But, so far I’ve only managed to register event processing in the form widget and get the selected funder, but I can’t pass the data to the javascript(Just in case, I provide the form below, it does not apply to the first working version):

    class ProjectForm(forms.ModelForm):
        class Meta:
            model = Project
            fields = '__all__'
    
            widgets = {
                'funder': forms.Select(attrs={'onchange': "Load();"})
            }