django-avatar/avatar/api/serializers.py
2023-05-07 13:37:53 +03:30

118 lines
4.4 KiB
Python

import os
from PIL import Image, ImageOps
from avatar.models import Avatar
from avatar.conf import settings
from rest_framework import serializers
from avatar.conf import settings as api_setting
from django.utils.translation import gettext_lazy as _
from django.template.defaultfilters import filesizeformat
class AvatarSerializer(serializers.ModelSerializer):
avatar_url = serializers.HyperlinkedIdentityField(view_name='avatar-detail', )
user = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = Avatar
fields = ['id', 'avatar_url', 'avatar', 'primary', 'user']
extra_kwargs = {'avatar': {'required': True}}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
request = kwargs.get('context').get('request', None)
self.user = request.user
def get_fields(self, *args, **kwargs):
fields = super(AvatarSerializer, self).get_fields(*args, **kwargs)
request = self.context.get('request', None)
# remove avatar url field in detail page
if bool(self.context.get('view').kwargs):
fields.pop('avatar_url')
# remove avatar field in put method
if request and getattr(request, 'method', None) == "PUT":
# avatar updates only when primary=true and API_AVATAR_CHANGE_IMAGE = True
if not api_setting.API_AVATAR_CHANGE_IMAGE or self.instance and not self.instance.primary:
fields.pop('avatar')
else:
fields.get('avatar', None).required = False
return fields
def validate_avatar(self, value):
data = value
if settings.AVATAR_ALLOWED_MIMETYPES:
try:
import magic
except ImportError:
raise ImportError(
"python-magic library must be installed in order to use uploaded file content limitation"
)
# Construct 256 bytes needed for mime validation
magic_buffer = bytes()
for chunk in data.chunks():
magic_buffer += chunk
if len(magic_buffer) >= 256:
break
# https://github.com/ahupp/python-magic#usage
mime = magic.from_buffer(magic_buffer, mime=True)
if mime not in settings.AVATAR_ALLOWED_MIMETYPES:
raise serializers.ValidationError(
_(
"File content is invalid. Detected: %(mimetype)s Allowed content types are: %(valid_mime_list)s"
)
% {
"valid_mime_list": ", ".join(settings.AVATAR_ALLOWED_MIMETYPES),
"mimetype": mime,
}
)
if settings.AVATAR_ALLOWED_FILE_EXTS:
root, ext = os.path.splitext(data.name.lower())
if ext not in settings.AVATAR_ALLOWED_FILE_EXTS:
valid_exts = ", ".join(settings.AVATAR_ALLOWED_FILE_EXTS)
error = _(
"%(ext)s is an invalid file extension. "
"Authorized extensions are : %(valid_exts_list)s"
)
raise serializers.ValidationError(
error % {"ext": ext, "valid_exts_list": valid_exts}
)
if data.size > settings.AVATAR_MAX_SIZE:
error = _(
"Your file is too big (%(size)s), "
"the maximum allowed size is %(max_valid_size)s"
)
raise serializers.ValidationError(
error
% {
"size": filesizeformat(data.size),
"max_valid_size": filesizeformat(settings.AVATAR_MAX_SIZE),
}
)
try:
image = Image.open(data)
ImageOps.exif_transpose(image)
except TypeError:
raise serializers.ValidationError(_("Corrupted image"))
count = Avatar.objects.filter(user=self.user).count()
if 1 < settings.AVATAR_MAX_AVATARS_PER_USER <= count:
error = _(
"You already have %(nb_avatars)d avatars, "
"and the maximum allowed is %(nb_max_avatars)d."
)
raise serializers.ValidationError(
error
% {
"nb_avatars": count,
"nb_max_avatars": settings.AVATAR_MAX_AVATARS_PER_USER,
}
)
return data