Невозможно обновить профиль пользователя с помощью метода 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),
]
У меня...
- Вошли под пользователем, которому нужно изменить информацию: /users/login/
- Скопировал токен доступа и поместил его в заголовок авторизации
- /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 работает хорошо.
Кроме того, реализация has_object_permission в классе разрешения, когда требуется проверка на уровне объекта, является хорошим решением.