Как проверить с помощью декоратора, имеет ли пользователь доступ к некоторым объектам в Django

Я работаю над проектом, структура которого выглядит следующим образом. И я пытался проверить, является ли пользователь членом запрашиваемой команды и может ли он видеть ее детали. Я пытался добиться этого с помощью декораторов, но все равно получаю ошибку http 500.

Я не уверен, что это правильное решение и просто плохая реализация, или лучше сделать это по-другому.

Я буду благодарен за Вашу помощь.

Team/Models.py

roles = (('admin', 'admin'), ('member', 'member'), ('guest', 'guest'))

class Team(models.Model):
    name = models.CharField(max_length=150)
    description = models.TextField(blank=True)
    members = models.ManyToManyField(User, through='Membership')
    cppTables = models.JSONField(default=list, blank=True)
    
    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return f'teams/{self.pk}/'

    def has_user(self, user_id):
        return self.members.get(pk=user_id).exists()

class Membership(models.Model):
    member = models.ForeignKey(User, on_delete=models.CASCADE)
    team = models.ForeignKey(Team, on_delete=models.CASCADE)
    role = models.CharField(max_length=15, choices=roles, default='member')

    def __str__(self):
        return str(self.member)

    class Meta:
        unique_together = [['member', 'team']]

Team/Decorators.py

def required_membership(view_func):
    def wrapper(request, *args, **kwargs):
        team_id = kwargs['team_id']
        team = Team.objects.get(pk=team_id)
        if team.has_user(request.user.id):
            return view_func(request, *args, **kwargs)
        else:
            raise HttpResponseForbidden
    return wrapper

Team/Views.py

@method_decorator(required_membership, name="dispatch")
class TeamDetail(APIView):
    def get_object(self, pk):
        try:
            return Team.objects.get(pk=int(pk))
        except Team.DoesNotExist:
            raise Http404

    def get(self, request, pk, format=None):
        team = self.get_object(pk)
        serializer = TeamSerializer(team, many=False)
        return Response(serializer.data)

в декораторе поместите обертку внутрь внутренней функции. ваш код будет выглядеть примерно так и плюс не нужно помещать ее внутрь декоратора методов просто используйте вот так @required_membership()

from django.core.exceptions import PermissionDenied
def required_membership():
    def inner(view_func):
        def wrapper(request, *args, **kwargs):
            team_id = kwargs['team_id']
            team = Team.objects.get(pk=team_id)
            if team.has_user(request.user.id):
                return view_func(request, *args, **kwargs)
            else:
                raise PermissionDenied
        return wrapper


    return inner

UPDATE

поместите декоратор над методом get или post вместо класса

Самым простым способом, вероятно, является просто фильтрация в представлении:

from django.shortcuts import get_object_or_404

class TeamDetail(APIView):
    
    def get_object(self, pk):
        return get_object_or_404(Team, pk=pk, members=self.request.user)

    def get(self, request, pk, format=None):
        team = self.get_object(pk)
        serializer = TeamSerializer(team, many=False)
        return Response(serializer.data)

В случае, если пользователь не является членом команды, возвращается 404, что дает меньше информации для использования, когда пользователь стремится посетить команду, членом которой он не является.

Вы также можете поднять HttpResponseForbidden вместо этого, проверив членство с помощью:

from django.shortcuts import get_object_or_404

class TeamDetail(APIView):
    
    def get_object(self, pk):
        team = get_object_or_404(Team, pk=pk)
        if not team.members.filter(pk=self.request.user.pk).exists():
            raise HttpResponseForbidden
        return team

    def get(self, request, pk, format=None):
        team = self.get_object(pk)
        serializer = TeamSerializer(team, many=False)
        return Response(serializer.data)

Ваш метод has_user(…) имеет ошибку: он должен использовать .filter(…), так как у объекта модели нет метода .exists(), а .get(…) вызовет ошибку, если не сможет найти элемент:

class Team(models.Model):
    # …

    def has_user(self, user_id):
        return self.members.filter(pk=user_id).exists()
Вернуться на верх