Reputation: 1916
I get the following error on GET /api/stories/169/
in StorySerializer, noted below in a comment:
AttributeError at /api/stories/169/
'ManyRelatedField' object has no attribute 'queryset'
Upon inspection of the object, I discovered that if I change the line from...
fields['feature'].queryset = fields['feature'].queryset.filter(user=user)
to
fields['photos'].child_relation.queryset = fields['photos'].child_relation.queryset.filter(user=user)
...it seems to work. But this approach is undocumented, and I'm sure isn't the right way to do it.
I have a these models:
class Story(CommonInfo):
user = models.ForeignKey(User)
text = models.TextField(max_length=5000,blank=True)
feature = models.ForeignKey("Feature", blank=True, null=True)
tags = models.ManyToManyField("Tag")
class Feature(CommonInfo):
user = models.ForeignKey(User)
name = models.CharField(max_length=50)
class Photo(CommonInfo):
user = models.ForeignKey(User)
image = ImageField(upload_to='photos')
story = models.ForeignKey("Story", blank=True, null=True, related_name='photos', on_delete=models.SET_NULL)
And a StorySerializer
:
class StorySerializer(serializers.HyperlinkedModelSerializer):
user = serializers.CharField(read_only=True)
comments = serializers.HyperlinkedRelatedField(read_only=True, view_name='comment-detail', many=True)
def get_fields(self, *args, **kwargs):
user = self.context['request'].user
fields = super(StorySerializer, self).get_fields(*args, **kwargs)
## Restrict the options that the user can pick to the Features
## and Photos that they own
# This line works:
fields['feature'].queryset = fields['feature'].queryset.filter(user=user)
# This line throws the error:
fields['photos'].queryset = fields['photos'].queryset.filter(user=user)
return fields
class Meta:
model = Story
fields = ('url', 'user', 'text', 'comments', 'photos', 'feature', 'tags')
What am I doing wrong? I feel like it's something to do with the direction of the ForeignKey
relationships.
Upvotes: 16
Views: 3766
Reputation: 20419
Two important points.
When a queryset is filtered from the view for a user, all the foreign key objects retrieval will yield only the objects for the particular user. So no need to filter for the user inside get_fields
.
class StoryList(generics.ListAPIView):
serializer_class = StorySerializer
def get_queryset(self):
# consider there is login check before code is reaching here
# since this filtered by the user and any susbquent
# foreign key objects will belong only to this user
return Story.objects.filter(user=self.request.user)
Once the filtering for a user happens, then you can use another serializer or SerializerMethodField
to construct the data accordingly. The below code should work for your case.
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
# Allow only url and id
fields = ['id', 'url']
extra_kwargs = {'url': {'view_name': 'user-detail'}}
class FeatureSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
fields = ['id', 'url']
extra_kwargs = {'url': {'view_name': 'feature-detail'}}
class PhotoSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
fields = ['id', 'url']
extra_kwargs = {'url': {'view_name': 'photo-detail'}}
class StorySerializer(serializers.HyperlinkedModelSerializer):
user = UserSerializer(read_only=True)
comments = serializers.HyperlinkedRelatedField(read_only=True,
view_name='comment-detail', many=True)
# this work because of related names
features = FeatureSerializers(many=True)
photos = PhotoSerializers(many=True)
# add tags serializer as well
text = serializers.CharField()
class Meta:
fields = ['id', 'users', 'photos', 'features', ...]
Upvotes: 1
Reputation: 1208
Instead of overriding get_fields method, you can try drf-writable-nested package, this will help to serialize nested relationships much easier and hand orm fields directions better.
Upvotes: 0
Reputation: 151
you can user django-filter
i think it not practical solution to edit get in serialiser
Upvotes: 0