[Django]-Djangorestframework: Filtering in a related field

39👍

I’ll be curious to see a better solution as well. I’ve used a custom method in my serializer to do that. It’s a bit more verbose but at least it’s explicit.

Some pseudo code where a GarageSerializer would filter the nested relation of cars:

class MyGarageSerializer(...):
    users = serializers.SerializerMethodField('get_cars')

    def get_cars(self, garage):
        cars_queryset = Car.objects.all().filter(Q(garage=garage) | ...).select_related()
        serializer = CarSerializer(instance=cars_queryset, many=True, context=self.context)

        return serializer.data

Obviously replace the queryset with whatever you want. You don’t always need the to give the context (I used it to retrieve some query parameters in the nested serializer) and you probably don’t need the .select_related (that was an optimisation).

5👍

One way to do this is to create a method on the Model itself and reference it in the serializer:

#Models.py
class MyModel(models.Model):
    #...
    def my_filtered_field (self):
            return self.othermodel_set.filter(field_a = 'value_a').order_by('field_b')[:10]
#Serialziers.py
class MyModelSerialzer(serializers.ModelSerializer):
    my_filtered_field = OtherModelSerializer (many=True, read_only=True)
    class Meta:
        model   = MyModel
        fields  = [
            'my_filtered_field'             ,
            #Other fields ...
        ]

3👍

Another way to avoid the SerializerMethodField solution and therefore still allow writing to the serializer as well would be to subclass the RelatedField and do the filtering there.

To only allow active users as values for the field, the example would look like:

class ActiveUsersPrimaryKeyField(serializers.PrimaryKeyRelatedField):
    def get_queryset(self):
        return super().get_queryset().filter(active=True)

class MySerializer(serializers.ModelSerializer):
    users = ActiveUsersPrimaryKeyField(many=True)
    class Meta:
        model = MyModel
        fields = ('users',)

Also see this response.

Note that this only restricts the set of input values to active users, though, i.e. only when creating or updating model instances, inactive users will be disallowed.


If you also use your serializer for reading and MyModel already has a relation to a user that has become inactive in the meantime, it will still be serialized. To prevent this, one way is to filter the relation using django’s Prefetch objects. Basically, you’ll filter out inactive users before they even get into the serializer:

from django.db.models import Prefetch

# Fetch a model instance, eagerly prefetching only those users that are active
model_with_active_users = MyModel.objects.prefetch_related(
    Prefetch("users", queryset=User.objects.filter(active=True))
).first()

# serialize the data with the serializer defined above and see that only active users are returned
data = MyModelSerializer(model_with_active_users).data

Leave a comment