Filter by other fields in Django Rest Framework's SlugRelatedField

I have a model with multiple ForeignKey relationships and am looking to build a POST API that allows me to post their verbose names rather than ids. This seems like a job for SlugRelatedField, however this does not allow me to provide a queryset that is filtered based on other fields within the JSON post. The problem is that the fields need to be filtered based on other fields within the JSON request.

class GenericReportSerializer(serializers.ModelSerializer):
    """
    GenericReportSerializer for POST.
    """
    organization = serializers.SlugRelatedField(slug_field='name', queryset=models.Organization.objects.all())
    subdivision = serializers.SlugRelatedField(slug_field='name', queryset=models.Subdivision.objects.all())  # queryset needs to be filtered by organization
    prediction_model = serializers.SlugRelatedField(slug_field='name', queryset=models.PredictionModel.objects.all())  # queryset needs to be filtered by both organization and subdivision

    class Meta:
        model = models.GenericReport
        fields = '__all__'  # use all fields

Organizations are unique by name, so SlugField is fine. However, subdivisions do not have unique names and need to be filtered by the organization, and the prediction_model needs to be filtered by both the organization AND the subdivision. I haven't found a convenient way to chain filters together in Django Rest Framework and extending the SlugRelatedField class doesn't give me access to the entire data structure. Is there a good way to accomplish this?

You could create a Serializer for each model and then build the relation from GenericReport to organization to subdivision to prediction_model.

I assume they are related with ForeignKey or ManyToMany Relationsships.

class PredictionModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.PredictionModel
        fields = '__all__'

class SubdivisionSerializer(serializers.ModelSerializer):
    # TODO: adjust name prediction_model to the name of the related field
    prediction_model = PredictionModelSerializer(read_only=True, many=True)

    class Meta:
        model = models.Subdivision
        fields = '__all__'

class OrganizationSerializer(serializers.ModelSerializer):
    # TODO: adjust name subdivision to the name of the related field
    subdivision = SubdivisionSerializer(read_only=True, many=True)

    class Meta:
        model = models.Organization
        fields = '__all__'

class GenericReportSerializer(serializers.ModelSerializer):
    name = OrganizationSerializer(read_only=True, many=True)

    class Meta:
        model = models.GenericReport
        fields = '__all__'  # use all fields

If you serialize now a GenericReport you will receive a nested json POST response with all related information and your're quite flexible in chaging the behavior fields etc.

You can customize the slug Related, you just pass request in context and you can access request_data and then you can filter

class SubDivisionSlugRelatedField(serializers.SlugRelatedField):
    def get_queryset(self):
        organisation_name = self.context['request'].data.get('organization')
        return SubDivision.objects.filter(organisation__name=organisation_name)

class PredictionModelSlugRelatedField(serializers.SlugRelatedField):
    def get_queryset(self):
        organisation_name = self.context['request'].data.get('organization')
        subdivision_name = self.context['request'].data.get('subdivision')
        return PredictionModel.objects.filter(organisation__name=organisation_name,
        subdivision__name=subdivision_name)

class ReportSerializer(serializers.ModelSerializer):
    organization = serializers.SlugRelatedField(slug_field='name', queryset=Organisation.objects.all())
    subdivision = SubDivisionSlugRelatedField(slug_field='name', queryset=SubDivision.objects.all())  # queryset needs to be filtered by organization
    prediction_model = PredictionModelSlugRelatedField(slug_field='name', queryset=PredictionModel.objects.all())  # queryset needs to be filtered by both organization and subdivision

    class Meta:
        model = Report
        fields = '__all__'  # use all field
Back to Top