56

I have these models:

class Entity(models.Model):
       name=models.CharField(max_length=100)
      
class Theme(models.Model):
   name=models.CharField(max_length=100)
   entity=models.OneToOneField(Entity)

class Company(models.Model):
    name=models.CharField(max_length=100)
    theme=models.OneToOneField(Theme,null=True,blank=True)

I want to filter the theme field when adding a Company in the admin, something like this:

class CompanyAdmin(admin.ModelAdmin):
   def queryset(self, request):
      qs = super(CompanyAdmin, self).queryset(request)
      qs.theme.queryset = Theme.objects.filter(name__iexact='company')
      return qs
      
admin.site.register(Company, CompanyAdmin)

I've tried many things, but nothing worked! How can I do this?

djvg
  • 11,722
  • 5
  • 72
  • 103
Asma Gheisari
  • 5,794
  • 9
  • 30
  • 51
  • 2
    See the answer to [this question](http://stackoverflow.com/questions/3064368/how-to-limit-the-foreignkey-dropdown-with-constraints) – Burhan Khalid Apr 16 '12 at 19:14
  • 1
    The `formfield_for_foreignkey`-[example in the documentation](https://docs.djangoproject.com/en/3.0/ref/contrib/admin/#django.contrib.admin.ModelAdmin.formfield_for_foreignkey) describes exactly how to do this (on the `ModelAdmin` side). Extending `formfield_for_foreignkey()` also works for a `OneToOneField`. – djvg Jul 20 '20 at 19:48

7 Answers7

64

I actually prefer to do it in get_form like so:

Django < 2:

class CompanyAdmin(admin.ModelAdmin):
    def get_form(self, request, obj=None, **kwargs):
        form = super(CompanyAdmin, self).get_form(request, obj, **kwargs)
        form.fields['theme'].queryset = Theme.objects.filter(name__iexact='company')
        return form

Django >= 2

class CompanyAdmin(admin.ModelAdmin):
    def get_form(self, request, obj=None, **kwargs):
        form = super(CompanyAdmin, self).get_form(request, obj, **kwargs)
        form.base_fields['theme'].queryset = Theme.objects.filter(name__iexact='company')
        return form
GabLeRoux
  • 16,715
  • 16
  • 63
  • 81
Moritz
  • 3,235
  • 26
  • 17
63

Use the render_change_form method:

class CompanyAdmin(admin.ModelAdmin):
    def render_change_form(self, request, context, *args, **kwargs):
         context['adminform'].form.fields['theme'].queryset = Theme.objects.filter(name__iexact='company')
         return super(CompanyAdmin, self).render_change_form(request, context, *args, **kwargs)
Swagner
  • 53
  • 8
BenH
  • 894
  • 8
  • 7
24

look here http://books.agiliq.com/projects/django-admin-cookbook/en/latest/filter_fk_dropdown.html

@admin.register(Hero)
class HeroAdmin(admin.ModelAdmin, ExportCsvMixin):
    ...
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "category":
            kwargs["queryset"] = Category.objects.filter(name__in=['God', 'Demi God'])
        return super().formfield_for_foreignkey(db_field, request, **kwargs)
slavugan
  • 1,463
  • 17
  • 17
  • 4
    docs link : https://docs.djangoproject.com/fr/3.0/ref/contrib/admin/#django.contrib.admin.ModelAdmin.formfield_for_foreignkey – maxbellec Mar 04 '20 at 15:29
7

In Django 3 it is easy :

class CompanyAdmin(admin.ModelAdmin):
    list_display = ('name','theme')
    list_filter = ('theme__name',)

admin.site.register(Company,CompanyAdmin)

This will show you a filter on the right of your screen with the list of your theme's name.

G.Lebret
  • 2,826
  • 2
  • 16
  • 27
  • 2
    This doesn't answer the question. List filters have been available on ModelAdmins since the earliest versions of Django – TimmyGee Mar 17 '21 at 13:01
4

Another option is to create a custom model form where the queryset attribute of the theme field will be fine tuned to meet your needs.

class CompanyForm(ModelForm):
    class Meta:
        model = CompanyForm
        fields = __all__ # or a tuple of fields

    def __init__(self, *args, **kwargs):
        super(CompanyForm, self).__init__(*args, **kwargs)
        if self.instance: # Editing and existing instance
            self.fields['theme'].queryset = Theme.objects.filter(name__iexact='company')

This model form can be also reused outside of the django admin area.

Raiyan
  • 1,589
  • 1
  • 14
  • 28
2

I faced with the need to add filter to the foreignKey queryset of the parent ModelAdmin class (which all other ModelAdmins inherit from), that is, I can’t know exactly which model I need, this is my solution: db_field.related_model.objects.filter()

class TSModelAdmin(admin.ModelAdmin):
   exclude = ('site',)
    ...

      def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.related_model:
            kwargs["queryset"] = 
            db_field.related_model.objects.filter(site=request.user.user_profile.site)
        return super().formfield_for_foreignkey(db_field, request, **kwargs)

used django version 2.2.10

Webdma
  • 714
  • 6
  • 16
0

A bit unrelated, but similar to this so I'll post this here.

I was looking for a way to remove the NULL choice selection on a ModelForm foreignkey field. I first thought I could filter the queryset as is done in other answers here, but that didn't work.

I found that I can filter the entry where the pk value is NULL like this in the get_form method:


class CompanyAdmin(admin.ModelAdmin):
    def get_form(self, request, obj=None, **kwargs):
        form = super().get_form(request, obj, **kwargs)
        # remove null choice
        form.base_fields["theme"].choices = ((pk, display) for pk, display in form.base_fields["theme"].choices if pk)
        return form
monkut
  • 42,176
  • 24
  • 124
  • 155