Charles Haro
Charles Haro

Reputation: 1886

Django-REST Serializer: Queryset does not filter PrimaryKeyRelatedField results

So I have a serializer that looks like this

class BuildingsSerializer(serializers.ModelSerializer):
    masterlisting_set = serializers.PrimaryKeyRelatedField(many=True, 
                                    queryset=Masterlistings.objects.all())

and it works great

serializer = BuildingsSerializer(Buildings.objects.get(pk=1))
serializer.data 

produces

OrderedDict([
    ("masterlistings_set", [
        "0a06e3d7-87b7-4526-a877-c10f54fa5bc9",
        "343643ac-681f-4597-b8f5-ff7e5be65eef",
        "449a3ad2-c76c-4cb8-bb86-1be72fafcf64",
    ])
])

but if I change the queryset in the serializer to

class BuildingsSerializer(serializers.ModelSerializer):
     masterlistings_set = serializers.PrimaryKeyRelatedField(many=True, queryset=[])

I still get the same exact result back.

 OrderedDict([
    ("masterlistings_set", [
        "0a06e3d7-87b7-4526-a877-c10f54fa5bc9",
        "343643ac-681f-4597-b8f5-ff7e5be65eef",
        "449a3ad2-c76c-4cb8-bb86-1be72fafcf64",
    ])
])

Is this supposed to be happening? Am I using querysets incorrectly? I used [] as an easy example to show that no matter what I put in nothing changes.

Please any insight would be invaluable

It should be noted that masterlistings has a primary key relationship that points to buildings. So a masterlisting belong to a building.

Upvotes: 9

Views: 3999

Answers (2)

zymud
zymud

Reputation: 2249

queryset in related field limits only acceptable values. So with queryset=[] you will not be able to add new values to masterlisting_set or create new Buildings.

UPDATE. How to use queryset for filtering

This is a little bi tricky - you need to rewrite ManyRelatedField and many_init method in your RelatedField.

# re-define ManyRelatedField `to_representation` method to filter values
# based on queryset
class FilteredManyRelatedField(serializers.ManyRelatedField):
    def to_representation(self, iterable):
        iterable = self.child_relation.queryset.filter(
            pk__in=[value.pk for value in iterable])
        return super(FilteredManyRelatedField, self).to_representation(iterable)


# use overridden FilteredManyRelatedField in `many_init`
class FilteredPrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField):
    @classmethod
    def many_init(cls, *args, **kwargs):
        kwargs['child_relation'] = cls(queryset=kwargs.pop('queryset'))
        return FilteredManyRelatedField(*args, **kwargs)

Upvotes: 4

Amit Jaiswal
Amit Jaiswal

Reputation: 1015

As pointed out by @zymud, queryset argument in PrimaryKeyRelatedField is used for validating field input for creating new entries. Another solution for filtering out masterlistings_set is to use serializers.SerializerMethodField() as follows:

class BuildingsSerializer(serializers.ModelSerializer):
    masterlisting_set = serializers.SerializerMethodField()

    def get_masterlisting_set(self, obj):
        return MasterListing.objects.filter(building=obj).values_list('pk',flat=True)

Upvotes: 12

Related Questions