2

I'm new to the Django and the Django Rest Framework, and having some issues figuring this one out.

I've got a Model of Workouts, which contains a title and a many to many relationship field exercises. When making the api for the model, I'm inheriting the viewsets.ModelViewSet class - which from my understanding setups the common uses for a rest api (List, Create, Update etc.).

This is fine however, when accessing /api/workouts/ I get a list of all the workouts including all the exercises assigned to the workout, I feel it's a bit of a waste to pull in all that exercise data and the user might never see it.

I would prefer to have a the /api/workouts/ not return exercises and it only does so if a user accesses a workout with /api/workouts/1 the exercises will show up.

My serializer class,

class WorkoutSerializer(serializers.ModelSerializer):
    exercises = ExerciseSerializer(read_only=True, many=True)

    class Meta:
        model = Workout
        fields = ('id', 'title', 'exercises')

My view,

class WorkoutView(viewsets.ModelViewSet):
    queryset = Workout.objects.all()
    serializer_class = WorkoutSerializer

    def list(self, request):
        workouts_list = self.get_queryset().defer('exercises');
        serializer = self.get_serializer(workouts_list, many=True)
        return response.Response(serializer.data)

My latest attempt is to overwrite the list method and remove the field with the queryset, but it seems anything I do remove the field has no affect.

TL;DR I would like to keep using the ModelViewSet but remove the field exercises from the main list view /api/workouts/ and keep it in the detail view /api/workouts/1/

Thanks in advance, for any suggestions.

3 Answers 3

1

Instead of inhering from ModelViewSet, you can create your own custom class by inhereting from all parent classes of ModelViewSet except ListModelMixin.

from rest_framework import mixins

class MyCustomModelViewSet(
        mixins.CreateModelMixin,
        mixins.RetrieveModelMixin,
        mixins.UpdateModelMixin,
        mixins.DestroyModelMixin,
        mixins.ListModelMixin,
        GenericViewSet,
    ):
        pass
Sign up to request clarification or add additional context in comments.

Comments

0
exercises = ExerciseSerializer(read_only=True, many=True)

will always give you a queryset for your excercises as it will be like calling it as

@property
def excercises(self):
   return self.excercises.all()
   and then its always going to give you a queryset with all fields

I assume your model has a string method and all you want is to at least a name of the excercise or id. If so then this:

exercises = serializers.StringRelatedField(many=True)

will help you

Comments

0

One possible solution is to create a separate viewset and serializer for the detail view vs. the list view.

in api/viewsets.py

Class WorkoutListView(viewsets.ModelViewSet):
    queryset = Workout.objects.all()
    serializer_class = WorkoutListSerializer


Class WorkoutDetailView(viewsets.ModelViewSet):
    queryset = Workout.objects.all()
    serializer_class = WorkoutDetailSerializer

in api/serializers.py

class WorkoutListSerializer(serializers.ModelSerializer):

class Meta:
    model = Workout
    fields = ('id', 'title')


class WorkoutDetailSerializer(serializers.ModelSerializer):
    exercises = ExerciseSerializer(read_only=True, many=True)

class Meta:
    model = Workout
    fields = ('id', 'title', 'exercises')

in api/urls.py - this is the important part. Do the router.register() as normal for the list view

router.register(r'workouts', viewsets.WorkoutListView)

but then you'll need to define a path for the detail view with the pk.

from django.urls import path
urlpatterns = [
    path('workouts/<pk>/', viewsets.WorkoutDetailView.as_view({'get': 'retrieve'}))
]

at this point just make sure you include your url_patterns to your api and you should be good to go!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.