From c65c986b25f54f3db184db126ab54b4b4516dfc3 Mon Sep 17 00:00:00 2001 From: AdrienLSH Date: Mon, 11 Mar 2024 15:21:47 +0100 Subject: [PATCH] settings: avatar & username (& little stuff too) --- accounts/locale/fr/LC_MESSAGES/django.po | 7 +- accounts/serializers/update_user.py | 20 ++ accounts/urls.py | 7 +- accounts/views/delete.py | 11 +- accounts/views/edit.py | 45 ---- accounts/views/logged.py | 6 +- accounts/views/login.py | 3 +- accounts/views/logout.py | 4 +- accounts/views/register.py | 2 + accounts/views/update_profile.py | 14 ++ frontend/static/css/index.css | 8 - frontend/static/css/settings.css | 11 - frontend/static/js/api/Account.js | 45 ++-- frontend/static/js/api/MyProfile.js | 32 ++- frontend/static/js/views/SettingsView.js | 261 ++++++++++++++++------- profiles/models.py | 3 +- profiles/serializers.py | 6 +- profiles/urls.py | 3 +- profiles/viewsets.py | 28 ++- 19 files changed, 302 insertions(+), 214 deletions(-) create mode 100644 accounts/serializers/update_user.py delete mode 100644 accounts/views/edit.py create mode 100644 accounts/views/update_profile.py delete mode 100644 frontend/static/css/settings.css diff --git a/accounts/locale/fr/LC_MESSAGES/django.po b/accounts/locale/fr/LC_MESSAGES/django.po index eed9cf2..73661a4 100644 --- a/accounts/locale/fr/LC_MESSAGES/django.po +++ b/accounts/locale/fr/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-02-01 13:59+0100\n" +"POT-Creation-Date: 2024-03-11 11:02+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,6 +17,11 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: serializers/update_user.py:15 +msgid "You dont have permission for this user." +msgstr "Vous n'avez pas de permissions pour cet utilisateur." + #: views/login.py:22 msgid "Invalid username or password." msgstr "Nom d'utilisateur ou mot de passe incorect." diff --git a/accounts/serializers/update_user.py b/accounts/serializers/update_user.py new file mode 100644 index 0000000..25c91f5 --- /dev/null +++ b/accounts/serializers/update_user.py @@ -0,0 +1,20 @@ +from rest_framework.serializers import ModelSerializer, ValidationError +from django.contrib.auth.models import User +from django.utils.translation import gettext as _ + + +class UpdateUserSerializer(ModelSerializer): + class Meta: + model = User + fields = ['username'] + + def update(self, instance, validated_data): + user = self.context['request'].user + + if user.pk != instance.pk: + raise ValidationError({'authorize': _('You dont have permission for this user.')}) + + instance.username = validated_data.get('username', instance.username) + + instance.save() + return instance diff --git a/accounts/urls.py b/accounts/urls.py index 73ae6d7..dcac548 100644 --- a/accounts/urls.py +++ b/accounts/urls.py @@ -1,6 +1,6 @@ from django.urls import path -from .views import register, login, logout, delete, edit, logged +from .views import register, login, logout, delete, logged, update_profile urlpatterns = [ path("register", register.RegisterView.as_view(), name="register"), @@ -8,6 +8,5 @@ urlpatterns = [ path("logout", logout.LogoutView.as_view(), name="logout"), path("logged", logged.LoggedView.as_view(), name="logged"), path("delete", delete.DeleteView.as_view(), name="delete"), - path("edit", edit.EditView.as_view(), name="change_password") - -] \ No newline at end of file + path('update_profile', update_profile.UpdateProfileView.as_view(), name='update_profile') +] diff --git a/accounts/views/delete.py b/accounts/views/delete.py index 1333f7e..66fd084 100644 --- a/accounts/views/delete.py +++ b/accounts/views/delete.py @@ -5,17 +5,18 @@ from django.contrib.auth import logout from django.http import HttpRequest from rest_framework.authentication import SessionAuthentication + class DeleteView(APIView): permission_classes = (permissions.IsAuthenticated,) authentication_classes = (SessionAuthentication,) + def delete(self, request: HttpRequest): data: dict = request.data password: str = data["password"] - if (password is None): - return Response({"password": ["This field may not be blank."]}) - if (request.user.check_password(password) == False): - return Response({"password": ["Password wrong."]}) + if (request.user.check_password(password) is False): + return Response({"password": ["Password incorrect."]}, + status.HTTP_401_UNAUTHORIZED) request.user.delete() logout(request) - return Response("user deleted", status=status.HTTP_200_OK) \ No newline at end of file + return Response(status=status.HTTP_200_OK) diff --git a/accounts/views/edit.py b/accounts/views/edit.py deleted file mode 100644 index c62a201..0000000 --- a/accounts/views/edit.py +++ /dev/null @@ -1,45 +0,0 @@ -from rest_framework.views import APIView -from rest_framework.response import Response -from rest_framework import permissions, status -from django.http import HttpRequest -from django.contrib.auth import login -from rest_framework.authentication import SessionAuthentication -from django.contrib.auth.models import User -import re - -class EditView(APIView): - - permission_classes = (permissions.IsAuthenticated,) - authentication_classes = (SessionAuthentication,) - - def get(self, request: HttpRequest): - return Response({"username": request.user.username, "id": request.user.pk}) - - def patch(self, request: HttpRequest): - data: dict = request.data - - current_password: str = data.get("current_password") - if (current_password is None): - return Response({"current_password": ["This field may not be blank."]}) - - user_object = request.user - - if (user_object.check_password(current_password) == False): - return Response({"current_password": ["Password is wrong."]}) - - new_username = data.get("username", user_object.username) - if (new_username != user_object.username): - if (User.objects.filter(username=new_username).exists()): - return Response({"username": ["A user with that username already exists."]}) - if (set(new_username) == {' '}): - return Response({"username": ["This field may not be blank."]}) - if (re.search('^([a-z]||\@||\+||\-||\_)+$', new_username) is None): - return Response({"username":["Enter a valid username. This value may contain only letters, numbers, and @/./+/-/_ characters."]}) - - new_password: str = data.get("password") - if (new_password is not None): - user_object.set_password(new_password) - - user_object.save() - - return Response("data has been alterate") \ No newline at end of file diff --git a/accounts/views/logged.py b/accounts/views/logged.py index b67abe4..ff7f54d 100644 --- a/accounts/views/logged.py +++ b/accounts/views/logged.py @@ -2,15 +2,13 @@ from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import permissions, status from django.http import HttpRequest -from django.contrib.auth import login from rest_framework.authentication import SessionAuthentication -from ..serializers.login import LoginSerializer class LoggedView(APIView): - + permission_classes = (permissions.AllowAny,) authentication_classes = (SessionAuthentication,) def get(self, request: HttpRequest): - return Response(status = (status.HTTP_200_OK if request.user.is_authenticated else status.HTTP_400_BAD_REQUEST)) + return Response(status=status.HTTP_200_OK if request.user.is_authenticated else status.HTTP_400_BAD_REQUEST) diff --git a/accounts/views/login.py b/accounts/views/login.py index d981f41..0f10c7a 100644 --- a/accounts/views/login.py +++ b/accounts/views/login.py @@ -8,8 +8,9 @@ from django.utils.translation import gettext as _ from ..serializers.login import LoginSerializer + class LoginView(APIView): - + permission_classes = (permissions.AllowAny,) authentication_classes = (SessionAuthentication,) diff --git a/accounts/views/logout.py b/accounts/views/logout.py index cee8868..f4d7ad1 100644 --- a/accounts/views/logout.py +++ b/accounts/views/logout.py @@ -5,9 +5,11 @@ from rest_framework.response import Response from django.http import HttpRequest from rest_framework.authentication import SessionAuthentication + class LogoutView(APIView): permission_classes = (permissions.IsAuthenticated,) authentication_classes = (SessionAuthentication,) + def get(self, request: HttpRequest): logout(request) - return Response("user unlogged", status=status.HTTP_200_OK) \ No newline at end of file + return Response("user logged out", status.HTTP_200_OK) diff --git a/accounts/views/register.py b/accounts/views/register.py index 903609a..926a560 100644 --- a/accounts/views/register.py +++ b/accounts/views/register.py @@ -5,8 +5,10 @@ from rest_framework.response import Response from django.http import HttpRequest from django.contrib.auth import login + class RegisterView(APIView): permission_classes = (permissions.AllowAny,) + def post(self, request: HttpRequest): data = request.data serializer = RegisterSerialiser(data=data) diff --git a/accounts/views/update_profile.py b/accounts/views/update_profile.py new file mode 100644 index 0000000..eca35f0 --- /dev/null +++ b/accounts/views/update_profile.py @@ -0,0 +1,14 @@ +from ..serializers.update_user import UpdateUserSerializer +from rest_framework.generics import UpdateAPIView +from rest_framework.permissions import IsAuthenticated +from django.contrib.auth.models import User + + +class UpdateProfileView(UpdateAPIView): + + queryset = User.objects.all() + permission_classes = (IsAuthenticated,) + serializer_class = UpdateUserSerializer + + def get_object(self): + return self.queryset.get(pk=self.request.user.pk) diff --git a/frontend/static/css/index.css b/frontend/static/css/index.css index 6c04bb5..816eaec 100644 --- a/frontend/static/css/index.css +++ b/frontend/static/css/index.css @@ -1,11 +1,3 @@ -#app #avatar { - max-height: 10em; - max-width: 10em; - min-height: 6em; - min-width: 6em; -} - - #popup { position: fixed; font-size: 1.2em; diff --git a/frontend/static/css/settings.css b/frontend/static/css/settings.css deleted file mode 100644 index 6879d41..0000000 --- a/frontend/static/css/settings.css +++ /dev/null @@ -1,11 +0,0 @@ -#app * { - font-size: 30px; -} - - -#app #main -{ - width: 60%; - display: flex; - flex-direction: column; -} diff --git a/frontend/static/js/api/Account.js b/frontend/static/js/api/Account.js index 66c8e0c..b3604a2 100644 --- a/frontend/static/js/api/Account.js +++ b/frontend/static/js/api/Account.js @@ -48,41 +48,24 @@ class Account } /** - * Get account data (username) - * @returns {?Promise} - */ - async get() + * @param {String} newUsername + * @returns {?Promise} + */ + async updateUsername(newUsername) { - let response = await this.client._get("/api/accounts/edit"); - let response_data = await response.json(); + const data = { + username: newUsername + }; + const response = await this.client._patch_json(`/api/accounts/update_profile`, data); + const respondeData = await response.json(); - if (response.status === 403) - { - this.client._update_logged(false); + if (response.status === 200) { + this.client.me.username = respondeData.username; + document.getElementById('navbarDropdownButton').innerHTML = respondeData.username; + document.getElementById('myProfileLink').href = '/profiles/' + respondeData.username; return null; } - return response_data; - } - - /** - * - * @param {*} data - * @param {Number} password - * @returns {?Object} - */ - async update(data, password) - { - data.current_password = password; - let response = await this.client._patch_json("/api/accounts/edit", data); - let response_data = await response.json(); - - if (response.status === 403) - { - this.client._update_logged(false); - return null; - } - - return response_data; + return respondeData['authorize'] || respondeData['detail'] || respondeData['username']?.join(' ') || 'Error.'; } } diff --git a/frontend/static/js/api/MyProfile.js b/frontend/static/js/api/MyProfile.js index a866fea..0773c5f 100644 --- a/frontend/static/js/api/MyProfile.js +++ b/frontend/static/js/api/MyProfile.js @@ -14,15 +14,35 @@ class MyProfile extends Profile /** * - * @param {*} form_data - * @returns {Promise} + * @param {File} selectedFile + * @returns {Promise} */ - async change_avatar(form_data) + async changeAvatar(selectedFile) { - let response = await this.client._patch_file(`/api/profiles/settings`, form_data); - let response_data = await response.json(); + const formData = new FormData(); + formData.append('avatar', selectedFile); - return response_data; + const response = await this.client._patch_file(`/api/profiles/settings`, formData); + const responseData = await response.json(); + + if (response.ok) { + console.log('save', responseData); + this.avatar_url = responseData.avatar.substr(responseData.avatar.indexOf('static') - 1); + return null; + } + return responseData; + } + + async deleteAvatar() { + const response = await this.client._delete('/api/profiles/settings'); + const responseData = await response.json(); + + if (response.ok) { + console.log('delete', responseData); + this.avatar_url = responseData.avatar.substr(responseData.avatar.indexOf('static') - 1); + return null; + } + return responseData; } } diff --git a/frontend/static/js/views/SettingsView.js b/frontend/static/js/views/SettingsView.js index ffb7662..387cf80 100644 --- a/frontend/static/js/views/SettingsView.js +++ b/frontend/static/js/views/SettingsView.js @@ -1,55 +1,82 @@ -import { client, navigateTo } from "../index.js"; -import { clear, fill_errors } from "../utils/formUtils.js"; -import AbstractAuthenticatedView from "./abstracts/AbstractAuthenticatedView.js"; +import { client, navigateTo } from '../index.js'; +import { clear, fill_errors } from '../utils/formUtils.js'; +import AbstractAuthenticatedView from './abstracts/AbstractAuthenticatedView.js'; export default class extends AbstractAuthenticatedView { constructor(params) { - super(params, "Settings"); + super(params, 'Settings'); this.PROFILE_PICTURE_MAX_SIZE = 2 * 1024 * 1024; // 2MB } async postInit() { - this.display_avatar(); - document.getElementById("save-account-button").onclick = () => this.save_account(); - document.getElementById("delete-account-button").onclick = () => this.delete_account(); - document.getElementById("save-profile-button").onclick = () => this.save_profile(); + this.avatarInit(); + this.usernameInit(); + + // document.getElementById('delete-account-button').onclick = () => this.delete_account(); } - async display_avatar() { - let profile = await client.profiles.getProfile(client.me.username); - if (profile !== undefined || profile !== null) { - if (document.getElementById("avatar") != undefined) - document.getElementById("avatar").remove(); - let avatar = document.createElement("img"); - avatar.id = "avatar"; - avatar.src = profile.avatar_url + '?t=' +new Date().getTime(); - document.getElementsByClassName("avatar")[0].appendChild(avatar); + usernameInit() { + const usernameInput = document.getElementById('usernameInput'); + const usernameSave = document.getElementById('usernameSave'); + + usernameInput.oninput = e => { + const value = e.target.value; + if (value != client.me.username && value.length) + usernameSave.classList.remove('disabled'); + else + usernameSave.classList.add('disabled'); } + usernameSave.onclick = _ => this.saveUsername(); + } + + avatarInit() { + const avatar = document.getElementById('avatar'); + const avatarInput = document.getElementById('avatarInput'); + const avatarUpload = document.getElementById('avatarUpload'); + const avatarDelete = document.getElementById('avatarDelete'); + + avatar.onclick = _ => avatarInput.click(); + avatarInput.onchange = function () { + const selectedFile = this.files[0]; + if (!selectedFile) + return; + + avatar.src = URL.createObjectURL(selectedFile); + avatarUpload.classList.remove('d-none'); + } + avatarUpload.onclick = _ => this.saveAvatar(); + avatarDelete.onclick = _ => this.deleteAvatar(); + } + + async displayAvatar() { + let avatar = document.getElementById('avatar'); + avatar.src = client.me.avatar_url + '?t=' + new Date().getTime(); + console.log(avatar.src); } async delete_account() { - let current_password = document.getElementById("current_password-input").value; + let current_password = document.getElementById('current_password-input').value; let response_data = await client.account.delete(current_password); - if (response_data === null || response_data === "user deleted") + if (response_data === null || response_data === 'user deleted') { - navigateTo("/login"); + navigateTo('/login'); return; } - clear("innerHTML", ["current_password-input"]); - fill_errors({"current_password-input": response_data.password}, "innerHTML"); + clear('innerHTML', ['current_password-input']); + fill_errors({'current_password-input': response_data.password}, 'innerHTML'); } async save_account() { - let username = document.getElementById("username-input").value; - let new_password = document.getElementById("new_password-input").value; - let current_password = document.getElementById("current_password-input").value; + let username = document.getElementById('username-input').value; + let new_password = document.getElementById('new_password-input').value; + let current_password = document.getElementById('current_password-input').value; let data = {}; @@ -61,67 +88,139 @@ export default class extends AbstractAuthenticatedView if (response_data === null) { - navigateTo("/login"); + navigateTo('/login'); return; } - if (response_data === "data has been alterate") - response_data = {"save-account": "saved"}; + if (response_data === 'data has been alterate') + response_data = {'save-account': 'saved'}; - clear("innerHTML", ["username", "new_password", "current_password", "save-account", "delete-account"]); - fill_errors(response_data, "innerHTML"); - } - - async save_profile() - { - let avatar = document.getElementById("avatar-input"); - - if (avatar.files[0] !== undefined) - { - if (avatar.files[0].size > this.PROFILE_PICTURE_MAX_SIZE) { - document.getElementById("save-profile").classList.add('text-danger'); - document.getElementById("save-profile").innerHTML = "Image too large :/"; - return; - } - let form_data = new FormData(); - form_data.append("avatar", avatar.files[0]); - await client.me.change_avatar(form_data); - this.display_avatar(); - } - document.getElementById("save-profile").classList.remove('text-danger'); - document.getElementById("save-profile").innerHTML = "Saved"; + clear('innerHTML', ['username', 'new_password', 'current_password', 'save-account', 'delete-account']); + fill_errors(response_data, 'innerHTML'); } - async getHtml() - { - return /* HTML */ ` - -

ME

-
-
-
- -
-

Profile

- - - -
- Logout -
- `; - } + if (!username.length || username === client.me.username) + return; + + const error = await client.account.updateUsername(username); + if (!error) { + usernameDetail.classList.remove('text-danger'); + usernameDetail.classList.add('text-success'); + usernameDetail.innerHTML = 'Username Saved.'; + setTimeout(_ => usernameDetail.innerHTML = '', 2000); + document.getElementById('usernameSave').classList.add('disabled'); + } else { + usernameDetail.classList.remove('text-success'); + usernameDetail.classList.add('text-danger'); + usernameDetail.innerHTML = error; + document.getElementById('usernameSave').classList.add('disabled'); + console.log(error); + } + } + + async saveAvatar() + { + const avatarInput = document.getElementById('avatarInput'); + const selectedFile = avatarInput.files[0]; + const avatarDetail = document.getElementById('avatarDetail'); + + if (!selectedFile) + return; + + if (selectedFile.size > this.PROFILE_PICTURE_MAX_SIZE) { + avatarDetail.classList.remove('text-success'); + avatarDetail.classList.add('text-danger'); + avatarDetail.innerHTML = 'Image is too large.'; //to translate + return; + } + + const error = await client.me.changeAvatar(selectedFile); + if (!error) { + avatarDetail.classList.remove('text-danger'); + avatarDetail.classList.add('text-success'); + avatarDetail.innerHTML = 'Avatar saved.'; //to translate + setTimeout(_ => avatarDetail.innerHTML = '', 2000); + document.getElementById('avatarDelete').classList.remove('d-none'); + document.getElementById('avatarUpload').classList.add('d-none'); + avatarInput.value = null; + } else { + avatarDetail.classList.remove('text-success'); + avatarDetail.classList.add('text-danger'); + avatarDetail.innerHTML = error.avatar[0]; + document.getElementById('avatarUpload').classList.add('d-none'); + avatarInput.value = null; + console.log(error); + } + this.displayAvatar(); + } + + async deleteAvatar() { + const avatarDetail = document.getElementById('avatarDetail'); + + const error = await client.me.deleteAvatar(); + if (!error) { + avatarDetail.classList.remove('text-danger'); + avatarDetail.classList.add('text-success'); + avatarDetail.innerHTML = 'Avatar deleted.'; //to translate + setTimeout(_ => avatarDetail.innerHTML = '', 2000); + document.getElementById('avatarDelete').classList.add('d-none'); + } else { + avatarDetail.classList.remove('text-success'); + avatarDetail.classList.add('text-danger'); + avatarDetail.innerHTML = 'Something went wrong.'; //to translate + } + this.displayAvatar(); + } + + async getHtml() + { + const avatarUnchanged = client.me.avatar_url === '/static/avatars/default.avif'; + + return /* HTML */ ` +
+
+

Avatar

+ + +
+ + + +
+
+
+

Account

+
+
+
+ + +
+ +
+ +
+
+
+ `; + // + //

Settings

+ // + // + // + // + // + // + // + // + // + // + // + } } diff --git a/profiles/models.py b/profiles/models.py index d2c4e1e..566e664 100644 --- a/profiles/models.py +++ b/profiles/models.py @@ -3,16 +3,17 @@ from django.db import models from django.contrib.auth.models import User from django.db.models.signals import post_save, pre_delete from django.dispatch import receiver -from django.conf import settings from django.db.models import IntegerField from games.consumers import game_manager from os.path import splitext + def upload_to(instance, filename: str): return f"./profiles/static/avatars/{instance.pk}{splitext(filename)[1]}" + # Create your models here. class ProfileModel(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) diff --git a/profiles/serializers.py b/profiles/serializers.py index 893093b..7a9fe97 100644 --- a/profiles/serializers.py +++ b/profiles/serializers.py @@ -1,6 +1,8 @@ from rest_framework import serializers from .models import ProfileModel from django.conf import settings +from django.utils.translation import gettext as _ + class ProfileSerializer(serializers.ModelSerializer): @@ -16,5 +18,5 @@ class ProfileSerializer(serializers.ModelSerializer): Check that the image is not too large ''' if value.size > settings.PROFILE_PICTURE_MAX_SIZE: - raise serializers.ValidationError('Image is too large.'); - return value; + raise serializers.ValidationError(_('Image is too large.')) + return value diff --git a/profiles/urls.py b/profiles/urls.py index c7e9236..1335989 100644 --- a/profiles/urls.py +++ b/profiles/urls.py @@ -4,7 +4,7 @@ from . import viewsets from . import views urlpatterns = [ - path("settings", viewsets.MyProfileViewSet.as_view({'patch': 'partial_update'}), name="my_profile_page"), + path("settings", viewsets.MyProfileViewSet.as_view({'patch': 'partial_update', 'delete': 'delete_avatar'}), name="my_profile_page"), path("me", viewsets.MyProfileViewSet.as_view({'get': 'retrieve'}), name="my_profile_page"), path("", viewsets.ProfileViewSet.as_view({'get': 'list'}), name="profiles_list"), path("block", views.BlocksView.as_view(), name="block_page"), @@ -12,5 +12,4 @@ urlpatterns = [ path("friend", views.FriendsView.as_view(), name="friend_page"), path("user/", viewsets.ProfileViewSet.as_view({'get': 'retrieve'}), name="profile_page"), path("id/", viewsets.ProfileViewSet.as_view({'get': 'retrieve_id'}), name="profile_page"), - ] diff --git a/profiles/viewsets.py b/profiles/viewsets.py index 38e9755..ad8078e 100644 --- a/profiles/viewsets.py +++ b/profiles/viewsets.py @@ -1,4 +1,3 @@ -from rest_framework import permissions from rest_framework.parsers import MultiPartParser, FormParser from rest_framework import permissions, status from rest_framework import viewsets @@ -42,28 +41,35 @@ class ProfileViewSet(viewsets.ModelViewSet): profile["avatar"] = profile["avatar"][profile["avatar"].find("static") - 1:] return Response(serializer.data) -class MyProfileViewSet(viewsets.ModelViewSet): +class MyProfileViewSet(viewsets.ModelViewSet): permission_classes = (permissions.IsAuthenticated,) authentication_classes = (SessionAuthentication,) serializer_class = ProfileSerializer - queryset = ProfileModel.objects.all + queryset = ProfileModel.objects.all() def get_object(self): - obj = self.queryset().get(pk=self.request.user.pk) + obj = self.queryset.get(pk=self.request.user.pk) return obj - def perform_update(self, serializer, pk=None): - serializer.is_valid(raise_exception=True); - profile: ProfileModel = self.get_object(); - avatar = serializer.validated_data.get('avatar'); + def perform_update(self, serializer: ProfileSerializer, pk=None): + serializer.is_valid(raise_exception=True) + avatar = serializer.validated_data.get('avatar') + profile: ProfileModel = self.get_object() if (avatar is not None): if (profile.avatar.name != "./profiles/static/avatars/default.avif"): profile.avatar.storage.delete(profile.avatar.name) - profile.avatar = avatar - profile.save() - + serializer.save() + + def delete_avatar(self, request, pk=None): + profile = self.get_object() + if (profile.avatar.name != './profiles/static/avatars/default.avif'): + profile.avatar.storage.delete(profile.avatar.name) + profile.avatar.name = './profiles/static/avatars/default.avif' + profile.save() + return Response(ProfileSerializer(profile).data) + def retrieve(self, request: HttpRequest, pk=None): instance: ProfileModel = self.get_object() instance.avatar.name = instance.avatar.name[instance.avatar.name.find("static") - 1:]