How to apply a django filter only if both field have a value

I have a simple django filters interface like this enter image description here

This is how I wrote the filters, but of course it is wrong because it adds the filter even when i choose only one field. Also I'm sharing the same FilterSet for both.

tipo_oid = filters.ChoiceFilter(label='Tipo Identificativo', 
    choices = TipoOid.objects.all().values_list("id","denominazione").exclude(annullato=True).filter(id__in=[2,3,12,22,9,49]).order_by('denominazione'), 
    method='filter_ide')

identificativo = filters.CharFilter(label='Identificativo', method='filter_ide')
    
def filter_ide(self, queryset, name, value):

    if name == 'tipo_oid':
        filtroPfIdInPfIde = PfIdentificativi.objects.filter(pf_id=OuterRef('id')).filter(tipo_oid=value).exclude(annullato=True)
        queryset = queryset.filter(Exists(filtroPfIdInPfIde))
    if name =='identificativo':
        filtroIdeInPfIde = PfIdentificativi.objects.filter(pf_id=OuterRef('id')).filter(identificativo=value).exclude(annullato=True)
        queryset = queryset.filter(Exists(filtroIdeInPfIde))

I need to ignore if only one of the fields have a value and to apply filter() to the queryset only once and only if both have a value.

How would you do that? Now even if I would use two separate filters, the result would not be the one I'd like. I think I could just do it in the viewSet, but it's not what I want because I want to do it in the Filter.

You can follow these steps to get the desired result.

  1. combine tipo_oid and identification into a single filter then check if both have values
  2. Use self.data to access both filter values within the method

Also better to create a separate class for that.

class MyFilterSet(filters.FilterSet):
tipo_oid = filters.ChoiceFilter(
    label='Tipo Identificativo', 
    choices=TipoOid.objects.all()
            .values_list("id", "denominazione")
            .exclude(annullato=True)
            .filter(id__in=[2, 3, 12, 22, 9, 49])
            .order_by('denominazione')
)
identificativo = filters.CharFilter(label='Identificativo')

def filter_queryset(self, queryset):
    tipo_oid = self.data.get('tipo_oid')
    identificativo = self.data.get('identificativo')

    
    if tipo_oid and identificativo:
        filtroPfIdInPfIde = PfIdentificativi.objects.filter(
            pf_id=OuterRef('id'), tipo_oid=tipo_oid, identificativo=identificativo
        ).exclude(annullato=True)
        queryset = queryset.filter(Exists(filtroPfIdInPfIde))

    return queryset

use django form.

# forms.py
from django import forms

class ChoiceForm(forms.Form):
 tipo_oid = forms.ModelChoiceField(
    label='Tipo Identificativo', 
    choices=TipoOid.objects.all().exclude(annullato=True).filter(id__in=[2,3,12,22,9,49]).order_by('denominazione'), 
    required=True,
)

 identificativo = forms.ModelChoiceField(
    label='Identificativo', 
    choices=PfIdentificativi.objects.all().exclude(annullato=True).filter(id__in=[2,3,12,22,9,49]).order_by('denominazione'), 
    required=True,
)
# views.py
from path.to.forms.py import ChoiceForm
...
if reqeust.method == 'POST':
   form = ChoiceForm(reqeust.POST)
   if form.is_valid():
      # valid action
   else:
      # invalid action
if request.method == 'GET':
    form = ChoiceForm()
    context['form'] = form
    return render(request, '{template}', context)
# template
<body>
...
{# rendering form object #}
{{ form }}
...
</body>
Back to Top