如何在用户配置文件模型中上传图像,同时与 django rest 框架中的用户模型具有一对一关系?
How to upload image in user profile model while has one to one relationship with user model in django rest framework?
我有一个 UserProfile
模型,它与 User
模型有一对一的关系。
I am facing problem in uploading image by rest-framework API endpoint to nested model.
Problem is: Though I am explicitly using @action() decorator, then also it is calling inbuilt create() method for saving image by POST.
那我已经在urls.py中明确提到要调用哪个方法了。现在它显示错误。
There are errors in the implementation in serializer.py and views.py.
So if possible then please correct my mistakes and direct me in the correct direction. Its more than two days but still I am struck.
在models.py:
def user_image_upload_file_path(instance, filename):
"""Generates file path for uploading user images"""
extension = filename.split('.')[-1]
file_name = f'{uuid.uuid4()}.{extension}'
date = datetime.date.today()
initial_path = f'pictures/uploads/user/{date.year}/{date.month}/{date.day}/'
full_path = os.path.join(initial_path, file_name)
return full_path
class UserManager(BaseUserManager):
def create_user(self, email, password, username, **extra_kwargs):
"""Creates and saves a new user"""
if not email:
raise ValueError(_('Email cannot be empty'))
user = self.model(email=self.normalize_email(email), **extra_kwargs)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password, username, **extra_kwargs):
"""Creates and saves a new user with superuser permission"""
user = self.create_user(
email, password, username, **extra_kwargs)
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
return user
class User(AbstractBaseUser, PermissionsMixin):
"""Creates user model that supports using email as username"""
email = models.EmailField(_('Email'), max_length=255, unique=True)
created_date = models.DateTimeField(
_('Created Date'), default=timezone.now, editable=False)
objects = UserManager()
USERNAME_FIELD = 'email'
def __str__(self):
"""String representation of user model"""
return self.email
class UserProfile(models.Model, Languages):
"""Creates user profile model"""
user = models.OneToOneField(
'User',
related_name='profile',
on_delete=models.CASCADE
)
first_name = models.CharField(
_('First Name'), max_length=255, blank=True)
last_name = models.CharField(
_('Last Name'), max_length=255, blank=True)
image = models.ImageField(
_('Image'),
upload_to=user_image_upload_file_path,
null=True,
blank=True,
max_length=1024
)
@receiver(post_save, sender=User)
def user_is_created(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
else:
instance.profile.save()
在serializer.py:
class UserSerializer(serializers.ModelSerializer):
"""Minimal serializer for supporting user image upload field"""
class Meta:
model = get_user_model()
fields = ('username', )
class UserImageUploadSerializer(serializers.ModelSerializer):
"""Serializer for user profile"""
user = UserSerializer(read_only=True)
class Meta:
model = UserProfile
fields = ('id', 'user', 'image', )
read_only_fields = ('id', 'user', )
在views.py:
class UserImageUploadView(viewsets.ModelViewSet):
serializer_class = serializer.UserImageUploadSerializer
authentication_classes = [authentication.TokenAuthentication, ]
permission_classes = [permissions.IsAuthenticated, ]
queryset = get_user_model().objects.all()
def get_queryset(self):
"""Return object for only authenticated user"""
return self.queryset.filter(id=self.request.user)
@action(detail=True, methods=['POST'], url_path='user-upload-image')
def image_upload(self, pk=None):
"""Save the uploaded picture and profile data"""
user = self.get_object()
profile = user.profile
data = {'user': user, 'id': profile.pk, 'data': request.data}
serializer_ = serializer.UserImageUploadSerializer(data=data)
if serializer_.is_valid():
serializer_.save()
return Response(serializer_.data, status=status.HTTP_200_OK)
else:
return Response(serializer_.errors, status=status.HTTP_400_BAD_REQUEST)
在urls.py:
urlpatterns = [
path('<int:pk>/upload-image/', views.UserImageUploadView.as_view(
{'get': 'list', 'post': 'image_upload', }), name='user-image-upload')
]
为了完成这个任务,我使用 APIView 手动创建了图像保存功能。
Please update if any efficient code exists
在我的 urls.py 文件中:
urlpatterns = [
path('upload-image/', views.UserImageUploadView.as_view(), name='user-image-upload'),
]
在views.py:
from rest_framework.parsers import FormParser, MultiPartParser, JSONParser
from rest_framework.views import APIView
from . import serializer
from core import models
class UserImageUploadView(APIView):
"""View to upload or view image for user"""
serializer_class = serializer.TempSerializer
authentication_classes = [authentication.TokenAuthentication, ]
permission_classes = [permissions.IsAuthenticated, ]
parser_classes = [JSONParser, MultiPartParser]
def get(self, request, format=None):
"""To get user profile picture"""
user = get_user_model().objects.get(email=request.user)
user_profile = models.UserProfile.objects.get(user=user)
# Preparing the data manually as per our serializer
data = {'user': {'username': user.username},
'image': user_profile.image or None}
# Serializing our prepared data
ser = serializer.TempSerializer(
user_profile, data=data, context={"request": request})
# Returning appropriate response
if ser.is_valid():
return_ser_data = {'id': ser.data.get('id'),
'image': ser.data.get('image')}
return Response(return_ser_data, status=status.HTTP_200_OK)
else:
return Response(ser.errors, status=status.HTTP_400_BAD_REQUEST)
def post(self, request, format=None):
"""To save the profile picture"""
user = get_user_model().objects.get(email=request.user)
user_profile = models.UserProfile.objects.get(user=user)
# Formatting the data to as per our defined serializer
data = {'user': {'username': user.username},
'image': request.data.get('image')}
# Serializing our data
ser = serializer.TempSerializer(
user_profile, data=data, context={"request": request})
if ser.is_valid():
if ser.validated_data:
# Deleting the old image before uploading new image
if user_profile.image:
user_profile.image.delete()
# Saving the model
ser.save(user=user)
return_ser_data = {'id': ser.data.get('id'),
'image': ser.data.get('image')}
return Response(return_ser_data, status=status.HTTP_200_OK)
else:
return Response(ser.errors, status=status.HTTP_400_BAD_REQUEST)
在serializer.py:
class UserSerializer(serializers.ModelSerializer):
"""Minimal serializer for supporting user image upload field"""
class Meta:
model = get_user_model()
fields = ('username', )
class TempSerializer(serializers.ModelSerializer):
"""Serializer for user image upload"""
user = UserSerializer(read_only=True)
image = serializers.ImageField(allow_null=True, use_url=True)
class Meta:
model = UserProfile
fields = ('id', 'user', 'image')
read_only_fields = ('id', 'user')
我有一个 UserProfile
模型,它与 User
模型有一对一的关系。
I am facing problem in uploading image by rest-framework API endpoint to nested model.
Problem is: Though I am explicitly using @action() decorator, then also it is calling inbuilt create() method for saving image by POST.
那我已经在urls.py中明确提到要调用哪个方法了。现在它显示错误。
There are errors in the implementation in serializer.py and views.py.
So if possible then please correct my mistakes and direct me in the correct direction. Its more than two days but still I am struck.
在models.py:
def user_image_upload_file_path(instance, filename):
"""Generates file path for uploading user images"""
extension = filename.split('.')[-1]
file_name = f'{uuid.uuid4()}.{extension}'
date = datetime.date.today()
initial_path = f'pictures/uploads/user/{date.year}/{date.month}/{date.day}/'
full_path = os.path.join(initial_path, file_name)
return full_path
class UserManager(BaseUserManager):
def create_user(self, email, password, username, **extra_kwargs):
"""Creates and saves a new user"""
if not email:
raise ValueError(_('Email cannot be empty'))
user = self.model(email=self.normalize_email(email), **extra_kwargs)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password, username, **extra_kwargs):
"""Creates and saves a new user with superuser permission"""
user = self.create_user(
email, password, username, **extra_kwargs)
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
return user
class User(AbstractBaseUser, PermissionsMixin):
"""Creates user model that supports using email as username"""
email = models.EmailField(_('Email'), max_length=255, unique=True)
created_date = models.DateTimeField(
_('Created Date'), default=timezone.now, editable=False)
objects = UserManager()
USERNAME_FIELD = 'email'
def __str__(self):
"""String representation of user model"""
return self.email
class UserProfile(models.Model, Languages):
"""Creates user profile model"""
user = models.OneToOneField(
'User',
related_name='profile',
on_delete=models.CASCADE
)
first_name = models.CharField(
_('First Name'), max_length=255, blank=True)
last_name = models.CharField(
_('Last Name'), max_length=255, blank=True)
image = models.ImageField(
_('Image'),
upload_to=user_image_upload_file_path,
null=True,
blank=True,
max_length=1024
)
@receiver(post_save, sender=User)
def user_is_created(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
else:
instance.profile.save()
在serializer.py:
class UserSerializer(serializers.ModelSerializer):
"""Minimal serializer for supporting user image upload field"""
class Meta:
model = get_user_model()
fields = ('username', )
class UserImageUploadSerializer(serializers.ModelSerializer):
"""Serializer for user profile"""
user = UserSerializer(read_only=True)
class Meta:
model = UserProfile
fields = ('id', 'user', 'image', )
read_only_fields = ('id', 'user', )
在views.py:
class UserImageUploadView(viewsets.ModelViewSet):
serializer_class = serializer.UserImageUploadSerializer
authentication_classes = [authentication.TokenAuthentication, ]
permission_classes = [permissions.IsAuthenticated, ]
queryset = get_user_model().objects.all()
def get_queryset(self):
"""Return object for only authenticated user"""
return self.queryset.filter(id=self.request.user)
@action(detail=True, methods=['POST'], url_path='user-upload-image')
def image_upload(self, pk=None):
"""Save the uploaded picture and profile data"""
user = self.get_object()
profile = user.profile
data = {'user': user, 'id': profile.pk, 'data': request.data}
serializer_ = serializer.UserImageUploadSerializer(data=data)
if serializer_.is_valid():
serializer_.save()
return Response(serializer_.data, status=status.HTTP_200_OK)
else:
return Response(serializer_.errors, status=status.HTTP_400_BAD_REQUEST)
在urls.py:
urlpatterns = [
path('<int:pk>/upload-image/', views.UserImageUploadView.as_view(
{'get': 'list', 'post': 'image_upload', }), name='user-image-upload')
]
为了完成这个任务,我使用 APIView 手动创建了图像保存功能。
Please update if any efficient code exists
在我的 urls.py 文件中:
urlpatterns = [
path('upload-image/', views.UserImageUploadView.as_view(), name='user-image-upload'),
]
在views.py:
from rest_framework.parsers import FormParser, MultiPartParser, JSONParser
from rest_framework.views import APIView
from . import serializer
from core import models
class UserImageUploadView(APIView):
"""View to upload or view image for user"""
serializer_class = serializer.TempSerializer
authentication_classes = [authentication.TokenAuthentication, ]
permission_classes = [permissions.IsAuthenticated, ]
parser_classes = [JSONParser, MultiPartParser]
def get(self, request, format=None):
"""To get user profile picture"""
user = get_user_model().objects.get(email=request.user)
user_profile = models.UserProfile.objects.get(user=user)
# Preparing the data manually as per our serializer
data = {'user': {'username': user.username},
'image': user_profile.image or None}
# Serializing our prepared data
ser = serializer.TempSerializer(
user_profile, data=data, context={"request": request})
# Returning appropriate response
if ser.is_valid():
return_ser_data = {'id': ser.data.get('id'),
'image': ser.data.get('image')}
return Response(return_ser_data, status=status.HTTP_200_OK)
else:
return Response(ser.errors, status=status.HTTP_400_BAD_REQUEST)
def post(self, request, format=None):
"""To save the profile picture"""
user = get_user_model().objects.get(email=request.user)
user_profile = models.UserProfile.objects.get(user=user)
# Formatting the data to as per our defined serializer
data = {'user': {'username': user.username},
'image': request.data.get('image')}
# Serializing our data
ser = serializer.TempSerializer(
user_profile, data=data, context={"request": request})
if ser.is_valid():
if ser.validated_data:
# Deleting the old image before uploading new image
if user_profile.image:
user_profile.image.delete()
# Saving the model
ser.save(user=user)
return_ser_data = {'id': ser.data.get('id'),
'image': ser.data.get('image')}
return Response(return_ser_data, status=status.HTTP_200_OK)
else:
return Response(ser.errors, status=status.HTTP_400_BAD_REQUEST)
在serializer.py:
class UserSerializer(serializers.ModelSerializer):
"""Minimal serializer for supporting user image upload field"""
class Meta:
model = get_user_model()
fields = ('username', )
class TempSerializer(serializers.ModelSerializer):
"""Serializer for user image upload"""
user = UserSerializer(read_only=True)
image = serializers.ImageField(allow_null=True, use_url=True)
class Meta:
model = UserProfile
fields = ('id', 'user', 'image')
read_only_fields = ('id', 'user')