rosenbrock
rosenbrock

Reputation: 1

Django filter based on joined model

Let's say I have the following design

database table

A track has a song and a song has a singer. I would like the track allows filtering based on singer name too.

So, I need the track model extract the singer name. I got stuck with the filters.

I receive the following error message:

File ".../lib/python3.6/site-packages/django_filters/filterset.py", line 352, in get_filters
    "%s" % ', '.join(undefined)
TypeError: 'Meta.fields' contains fields that are not defined on this FilterSet: singer

I have heard from this to use __ but I have no idea how to apply that.

Here is the code

class TrackSerializer(MyModelSerializer):
    singer = serializers.SerializerMethodField()

    def get_singer(self, track): # Is there any shortcut?
        song = Song.objects.get(id=track.song_id)
        if song is not None:
            return Channel.objects.get(id=song.singer_id).name
        else:
            return ''

    class Meta:
        model = Track
        fields = (
            'id',
            'name',
            'song',
            'singer',
        )

class TrackFilterSet(MyFilterSet):
    singer = CharFilter(method='singer_filter')
    song = RefineModelChoiceFilter(
        queryset=Song.objects.all(),
        refine_choices=lambda qs, keywords, request: qs.filter(name__icontains=keywords)
    )

    def singer_filter(self, queryset, name, value):
        # print('queryset:', TrackSerializer(queryset, many=True))
        return queryset.filter(**{
            name: value,  # ???????????
        })

    class Meta:
        model = Track
        fields = (
            'singer',
            'song',
        )


class TrackViewSet(MyViewSet):
    queryset = Track.objects.all()
    serializer_class = TrackSerializer
    filterset_fields = ('singer', 'song')

    def filter_refine_choices_singer(self, qs, keywords, request):
        return qs.filter(name__icontains=keywords)

    def filter_refine_choices_song(self, qs, keywords, request):
        return qs.filter(name__icontains=keywords)

Upvotes: 0

Views: 469

Answers (2)

Rodrigo Urban
Rodrigo Urban

Reputation: 19

try putting filterset_fields as an array, between [] rather that ().

filterset_fields = ['singer', 'song']

Upvotes: 1

adonis__simo
adonis__simo

Reputation: 89

I think i the method singer_filter you should so something like :

def singer_filter(self, queryset, name, value):
    return queryset.filter(song_id__singer_id__name_icontains=value)

I didn't test this but i think something like that should work unless third relation __ is not allowed. Take a look here: https://django-filter.readthedocs.io/en/master/ref/filters.html?highlight=method

Upvotes: 0

Related Questions