Невозможно обновить профиль пользователя с помощью метода PUT (Django DRF)

В настоящее время я работаю над сервисом подбора наставников и подопечных, используя Django DRF. Я создал приложение 'users' и завершил создание пользователей&logging in. Но я не могу редактировать информацию о пользователе, используя метод PUT. Вот файлы моего проекта (models.py, serializers.py, views.py, urls.py, settings.py) 1. models.py

# users/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator, MinValueValidator


class User(AbstractUser):
  is_mentor = models.BooleanField(default=False)
  profile_pic = models.ImageField(upload_to='profile_pics/', default = 'default.png') # profile_pic dir 만들기, default이미지 업로드하기, 사진 첨부 루트 만들기
  name = models.CharField(max_length=50, verbose_name= ("이름"))
  birth_date = models.DateField(verbose_name= ("생년월일"), null=True, blank=True)
  agreed_to_terms = models.BooleanField(default=False, verbose_name= ("이용약관 동의"))
  @property
  def role(self):
    return "Mentor" if self.is_mentor else "Mentee"

class Interest(models.Model):
  INTEREST_CHOICES = (
    ('belief', '가치관'),
    ('finance', '재테크'),
    ('love', '사랑'),
    ('daily', '생활 지식'),
    ('relationship', '인간 관계'),
    ('career', '진로'),
  )
  name = models.CharField(max_length=20, choices=INTEREST_CHOICES, unique=True)

  def __str__(self):
    return self.get_name_display()

class Mentor(models.Model):
  user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='mentor')
  interests = models.ManyToManyField(Interest, through='MentorInterest')
  rating = models.FloatField(default=0, validators=[MinValueValidator(0), MaxValueValidator(5)])
  total_ratings = models.PositiveIntegerField(default=0)
  
  def clean(self):
    super().clean()
    if self.interests.count() > 3:
        raise ValidationError("mentors can choose under 3 interest.")
  def update_rating(self, new_rating):
    self.rating = ((self.rating * self.total_ratings) + new_rating) / (self.total_ratings + 1)
    self.total_ratings += 1
    self.save()

class MentorInterest(models.Model):
  mentor = models.ForeignKey(Mentor, on_delete=models.CASCADE)
  interest = models.ForeignKey(Interest, on_delete=models.CASCADE)
  
  class Meta:
    unique_together = ('mentor', 'interest')
  def __str__(self):
    return f'{self.mentor.user.username} - {self.interest.name}'

class Mentee(models.Model):
  user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='mentee')

2. serializers.py

3. views.py

4. urls.py (project/urls.py)

from django.contrib import admin
from django.urls import path, include

# ImageField를 위해
from django.conf import settings
from django.conf.urls.static import static

# 로그인을 위해
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
)

urlpatterns = [
    path('admin/', admin.site.urls),
    path('users/', include('users.urls')),
    path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),  # 로그인 엔드포인트
    path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),  # 토큰 갱신 엔드포인트
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

(users/urls.py)


from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import UserViewSet, CustomTokenObtainPairView, LogoutView

router = DefaultRouter()
router.register(r'users', UserViewSet)

urlpatterns = [
    path('', include(router.urls)),
    path('login/', CustomTokenObtainPairView.as_view(), name='token_obtain_pair'),  # 로그인 엔드포인트
    path('logout/', LogoutView.as_view(), name='logout'),  # 로그아웃 엔드포인트
]

Также я поместил в папку Migration файл для инициализации Interests

# Generated by Django 5.0.7 on 2024-07-29 12:38
# users/migrations/0002_add_initial_interests.py

from django.db import migrations

def create_initial_interests(apps, schema_editor):
    Interest = apps.get_model('users', 'Interest')
    interests = [
        ('belief', '가치관'),
        ('finance', '재테크'),
        ('love', '사랑'),
        ('daily', '생활 지식'),
        ('relationship', '인간 관계'),
        ('career', '진로'),
    ]
    for name, display_name in interests:
        Interest.objects.get_or_create(name=name)

class Migration(migrations.Migration):

    dependencies = [
        ('users', '0001_initial'),
    ]

    operations = [
        migrations.RunPython(create_initial_interests),
    ]

У меня...

  1. Вошли под пользователем, которому нужно изменить информацию: /users/login/
  2. Скопировал токен доступа и поместил его в заголовок авторизации
  3. /users/users/<user_id>/ при использовании метода PUT выдал мне 403 ошибку

Сначала, если клиент пытается изменить информацию о пользователе с помощью HTTP-метода PUT IsAuthentication назначается классам permission_classes.

Для проверки разрешения требуется процесс аутентификации.

Однако в UserViewSet не указаны классы authentication_classes, связанные с процессом аутентификации. (Конечно, в вопрошающем settings.py он может быть определен. Но пока я решил, что его нет.)

Поэтому я сначала назначил IsAuthenticated на UserViewSet как authentication_classes.

class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    authentication_classes = [JWTAuthentication]
    ...

Далее, если посмотреть на метод update, который будет вызываться при использовании PUT то в выражении if будут использоваться request.user.id и kwargs.get('pk'). Но это выражение if никогда не может быть установлено. Это происходит потому, что типы этих двух значений различны.

request.user.id  --> type int
kwargs.get('pk') --> type str

Как показано выше, значение request.user.id имеет тип int, а kwargs.get ('pk') - тип str. Поэтому необходимо преобразовать одно из двух значений так, чтобы оба типа совпадали. вот так

def update(self, request, *args, **kwargs):
    # 본인만 자신의 정보를 업데이트할 수 있도록 함
    
    if request.user.id != int(kwargs.get('pk')):
        return Response({"detail": "로그인 후 정보 수정하시길 바랍니다."},    status=status.HTTP_403_FORBIDDEN)

Когда я модифицировал эти два варианта, я подтвердил, что PUT работает хорошо.

enter image description here

Кроме того, реализация has_object_permission в классе разрешения, когда требуется проверка на уровне объекта, является хорошим решением.

Обратитесь к этому документу

Вернуться на верх