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 id
s. 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?
CodePudding user response:
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.
CodePudding user response:
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