Search code examples
djangodjango-rest-frameworkdjango-viewsdjango-serializer

making an API that adds instances to ManyToMany fields of a model in django rest framework


I am making a movie watching website in which there are users and films and the user model has a ManyToMany Field that references the film model. it's called WatchList and an authenticated user can add any movie they want to this watchlist.

My problem is that I want an API that only gets the ID of a film and adds it to the user's watch list.

these are my models and serializers and I am trying to make a view to implement this API.

# models.py
class Film(models.Model):
    filmID = models.AutoField(primary_key=True)
    title = models.CharField(max_length=150)
    # ...

class User(AbstractBaseUser, PermissionsMixin):
    userID = models.AutoField(primary_key=True)
    username = models.CharField(max_length=100, unique=True, validators=[RegexValidator(regex="^(?=[a-z0-9._]{5,20}$)(?!.*[_.]{2})[^_.].*[^_.]$")])
    email= models.EmailField(max_length=100, unique=True,  validators=[EmailValidator()])
    name = models.CharField(max_length=100)

    watchList = models.ManyToManyField(Film)

    objects = UserManager()

    USERNAME_FIELD = 'username'
# serializers.py
class WatchListSerializer(serializers.ModelSerializer):
    class FilmSerializer(serializers.ModelSerializer):
        model = Film
        fields = ('filmID', 'title',)
        read_only_fields = ('filmID', 'title')

    film_set = FilmSerializer(read_only=True, many=True)

    class Meta:
        model = get_user_model()
        fields = ('userID', 'film_set')
        read_only_fields = ('userID',)
# views.py
class WatchListAddView(...):
    pass

The serializer can be changed. but this kind of shows what I want the api to be. the authentication validation part is already taken care of, so imagine that any request to the view is from an authenticated user.


Solution

  • I would not recommend patching this directly and instead create a separate endpoint for adding removing data to this field.

    In your case it would look like this. I show just a small working example, you can adjust it to your needs

    from django.shortcuts import get_object_or_404
    from rest_framework import viewsets
    from rest_framework.decorators import action
    from rest_framework.response import Response
    
    class UserViewSet(viewsets.ModelViewSet):
        queryset = User.objects.all()
    
        @action(detail=True,
                methods=['POST'])
        def add_film_to_watch_list(self, request, **kwargs):
            film = get_object_or_404(klass=Film, filmID=kwargs.get('filmID'))
            user = self.get_object()
            user.watchList.add(film)
            return Response("Success")