Opeyemi Odedeyi
Opeyemi Odedeyi

Reputation: 770

Adding a user to a many to many field from user input

I am creating a functionality where a project/showcase model has an administrators ManyToManyField, which would contain a list of users that can control a project.

I am having issues adding users to this field (the user will be gotten from user input from the front end). I was able to set the person that creates a project as an administrator by default, but adding other administrators has not been successful.

models.py

class Showcase(models.Model):
    title = models.CharField(max_length=50)
    description = models.TextField(null=True)
    skill_type = models.ForeignKey(Skill, on_delete=models.CASCADE)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING, related_name="Showcases")
    content = models.TextField(null=True)
    created_on = models.DateTimeField(auto_now_add=True)
    updated_on = models.DateTimeField(auto_now=True)
    voters = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name="upvotes")
    slug = models.SlugField(max_length=255, unique=True)
    administrator = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name="administrators", blank=True)

serializers.py

class ShowcaseAdminSerializer(serializers.ModelSerializer):

    class Meta:
        model = Showcase
        fields = ['administrator',]

With the view below I wanted only administrators to be able to add new administrators to the showcase. The user to be added will be gotten from the front end input instead of the URL (I hope this part is understood). views.py

class showcaseAddAdminAPIView(APIView):
    '''
    Add a user as an admin to a showcase
    '''
    serializer_class = ShowcaseAdminSerializer
    permission_classes = [IsAdmin]

    def post(self, request, slug):
        showcase = get_object_or_404(Showcase, slug=slug)

        if request.user in showcase.administrator.all():
            showcase.administrator.add(user.slug)
            showcase.save()

            serializer = self.serializer_class(showcase)

            return Response(serializer.data, status=status.HTTP_200_OK)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

with the view above, I was wonder if the check for if the user is an administrator before he can make the action should also be done using permissions. I have permission below which is not working though.

urls.py

path("<slug:slug>/addadmin/", qv.showcaseAddAdminApiview.as_view(), name="add-administrator-to-showcase"),

permissions

class IsAdmin(permissions.BasePermission):

    def has_object_permission(self, request, view, obj):
        if request.method in permissions.SAFE_METHODS:
            return False
        return obj.administrator == request.user

i tried using this permission to make suer only administrators can add admin to the showcase. but it doesnt work.

Upvotes: 0

Views: 104

Answers (2)

ruddra
ruddra

Reputation: 51988

Your permission won't work because as per documentation:

Note that the generic views will check the appropriate object level permissions, but if you're writing your own custom views, you'll need to make sure you check the object level permission checks yourself. You can do so by calling self.check_object_permissions(request, obj) from the view once you have the object instance. This call will raise an appropriate APIException if any object-level permission checks fail, and will otherwise simply return.

So the view should look like this:

class showcaseAddAdminAPIView(APIView):  # Use CamelCase for naming class
    '''
    Add a user as an admin to a showcase
    '''
    ...

    def post(self, request, slug):
        showcase = get_object_or_404(Showcase, slug=slug)
        self.check_object_permissions(request, showcase)

Update the permission class:

class IsAdmin(permissions.BasePermission):

    def has_object_permission(self, request, view, obj):
        if request.method in permissions.SAFE_METHODS:
            return False
        return request.user.administrators.filter(pk=obj.pk).exists()

And update the view:

# rest of the code
def post(self, request, slug):  # PUT is more suited for updating instance
    showcase = get_object_or_404(Showcase, slug=slug)
    try:
       self.check_object_permissions(request, showcase)
       serializer = self.serializer_class(showcase, data=request.data, partial=True)
       if serializer.is_valid():
          serializer.save()
          return Response(serializer.data, status=status.HTTP_201_CREATED)
       else:
          return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    except APIException:
        return Response(status=status.HTTP_403_FORBIDDEN)

Update

Override the update method in serializer:

class ShowcaseAdminSerializer(serializers.ModelSerializer):

   def update(self, instance, validated_data):
      users = validated_data.get('administrator')
      for user in users:
          instance.administrator.add(user)
      return instance

   # rest of the code

Upvotes: 1

Gustavo F Martins
Gustavo F Martins

Reputation: 31

As Opeyemi said if request.user in showcase.administrator.all(): will never add new administrators, if the user making the request is not an administrator it will be denied and if the user making a request is already an administrator it wont add anything new. You need to pass in an user id and use it to add a new user while being an administrator, showcase.administrator.add(user.id)

Upvotes: 1

Related Questions