DRF: make a POST query without data - python

cannot manage a POST method-query to url /users/{pk}/subscribe
Accesing this url should insert a record in db using Subscription model. No data is passed in body when accessing this url
model.Subscription
class Subscription(models.Model):
user = models.ForeignKey(User,
on_delete=models.CASCADE,
related_name='subscriber',
)
author = models.ForeignKey(User,
on_delete=models.CASCADE,
related_name='subscribing'
)
a router to users
router.register(r'users', CustomUserViewSet, basename='users')
and the additional URL that accepts POST query in the CustomUserViewSet
class CustomUserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = CreateUserSerializer
#action(
detail=True,
methods=['post', 'delete'],
permission_classes=[IsAuthenticated],
)
def subscribe(self, request, *args, **kwargs):
user = request.user
author_pk = int(kwargs.get('pk'))
author_to_subscribe = get_object_or_404(User, id=author_pk)
if request.method == 'DELETE':
return Response(status=status.HTTP_204_NO_CONTENT)
if user.pk == author_pk:
data = {
"errors": "Cannot subscribe to yourself"
}
return Response(status=status.HTTP_400_BAD_REQUEST,
data=data)
# q = Subscription.objects.filter(user=user)
serializer = SubscriptionCreateSerializer(data=request.data)
serializer.is_valid()
serializer.save(user=user, author=author_to_subscribe)
return Response(
status=status.HTTP_200_OK)
Method shoould return serialized data from another serializer (SubscriptionSerializer. So i have 2 serializer for subscription: for viewing and creating
At this point in Postman getting error
class SubscriptionCreateSerializer(serializers.ModelSerializer):
class Meta:
model = Subscription
fields = '__all__'
def create(self, validated_data):
pass
class SubscriptionSerializer(serializers.ModelSerializer):
email = serializers.EmailField(source='author.email')
id = serializers.IntegerField(source='author.id')
username = serializers.CharField(source='author.username')
first_name = serializers.CharField(source='author.first_name')
last_name = serializers.CharField(source='author.last_name')
is_subscribed = serializers.BooleanField(default=True)
recipes = serializers.SerializerMethodField()
def get_recipes(self, obj):
qs = obj.author.recipes.all()
return RecipePublicSerializer(qs, many=True).data
def to_representation(self, instance):
representation = super().to_representation(instance)
representation['recipes_count'] = instance.author.recipes.count()
return representation
def to_internal_value(self, data):
recipes_data = data['recipes']
return super().to_internal_value(recipes_data)
class Meta:
model = Subscription
fields = ('email',
'id',
'username',
'first_name',
'last_name',
'is_subscribed',
'recipes',)
class CreateUserSerializer(serializers.Serializer):
"""Serializer for creating user objects"""
username = serializers.CharField(
validators=[
UniqueValidator(queryset=User.objects.all())
]
)
email = serializers.EmailField(
validators=[
UniqueValidator(queryset=User.objects.all())
]
)
first_name = serializers.CharField()
last_name = serializers.CharField()
password = serializers.CharField()
is_subscribed = serializers.SerializerMethodField()
#classmethod
def get_is_subscribed(self, object):
return True
def validate_username(self, value):
if value.lower() == "me":
raise serializers.ValidationError("Username 'me' is not valid")
return value
def create(self, validate_data):
user = User(**validate_data)
user.set_password(validate_data['password'])
user.save()
return user
class Meta:
fields = ('username',
'email',
'first_name',
'last_name',
'id',
'is_subscribed',)
read_only_fields = ('id',)
model = User

As I understood from question you would like to get user from request and id of author to subscribe from url. You pass to serializer empty body of your post request, but according to your serializer you should pass user and author fields, so to get serializer works you need to pass this parameters as data to serializer instance.
subscription_data = {
'user': request.user,
'author': author_to_subscribe
}
serializer = SubscriptionCreateSerializer(data=subscription_data)
serializer.is_valid(raise_exception=True)
serializer.save()
And also remove def save() method from your SubscriptionCreateSerializer it overwrites method which should put info about subscription in DB, so in your case it does nothing instead.

Related

Django Rest Framework: how to get the current superuser in serialize?

CreateApiView :
class CreateEmployeeApiView(generics.CreateAPIView):
# authentication_classes = [TokenAuthentication, SessionAuthentication, ]
permission_classes = [IsAuthenticated]
queryset = Employee.objects.all()
serializer_class = CreateEmployeeApiSerializer
def post(self, request, *args, **kwargs):
return super(CreateEmployeeApiView, self).post(request, *args, **kwargs)
and serializer :
class CreateEmployeeApiSerializer(serializers.ModelSerializer):
# user = serializers.HiddenField(default=serializers.CurrentUserDefault())
username = serializers.CharField(source='user.username', required=True)
email = serializers.EmailField(source='user.email', required=True)
password = serializers.CharField(source='user.password',
style={'input_type': 'password', 'placeholder': 'Password'},
write_only=True, required=True)
class Meta:
model = Employee
fields = (
'username',
'email',
'password',
'is_delete',
'first_name',
'last_name',
'father_name',
'birth',
'avatar',
'status',
)
def to_representation(self, instance):
data = super(CreateEmployeeApiSerializer, self).to_representation(instance)
status = instance.status
data['status'] = Employee.USER_ROLE[status - 1][1]
data['author'] = instance.author.username
data['user'] = instance.user.username
return data
def create(self, validated_data):
# Create new user
print(validated_data)
user = User.objects.create(username=validated_data['user']['username'],
email=validated_data['user']['email'])
user.set_password(validated_data['user']['password'])
user.save()
# Create employee
# super_user = User.objects.filter(is_superuser=True)
employee = Employee(user=user)
employee.is_delete = validated_data['is_delete']
employee.first_name = validated_data['first_name']
employee.last_name = validated_data['last_name']
employee.first_name = validated_data['first_name']
employee.father_name = validated_data['father_name']
employee.birth = validated_data['birth']
employee.avatar = validated_data['avatar']
employee.status = validated_data['status']
employee.author = user
employee.save()
return employee
I need a superuser, not a simple user. When employee is created, the employee.author field must be assigned by the logged in user (i.e. the current superuser). How should I do it? I hope you understood me correctly!
You should restrict this view to only superusers. Create custom permission class as below:
from rest_framework.permissions import BasePermission
class IsSuperUser(BasePermission):
"""
Allows access only to superusers.
"""
def has_permission(self, request, view):
return bool(request.user and request.user.is_superuser)
And in your view:
permission_classes = (IsSuperUser,)
Read more about permissions in DRF.
In the view class, you can get the current user with request.user. You will need to pass this into your serializer in order to set the author.

How can I update user model extended via "OneToOneField"

I have some issues with updating my database, in my serializer I use extended via OneToOneField django user model with two extra fields with user image and his motto. So I think the problem with instance in my serializer, but I can't figure out how to do that.
#core.models
class MangaUser(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
user_image = models.ImageField(upload_to='upicks')
user_motto = models.CharField(max_length=256)
#api.serializers
class UserSerializer(serializers.ModelSerializer):
#mangauser_set = serializers.SerializerMethodField()
user_image = serializers.ImageField(source='mangauser.user_image')
user_moto = serializers.CharField(source='mangauser.user_motto')
class Meta:
model = User
fields = ['id', 'username', 'email', 'password', 'user_image', 'user_motto']
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
password = validated_data.pop('password', None)
instance = self.Meta.model(**validated_data)
if password is not None:
instance.set_password(password)
instance.save()
return instance
def update(self, instance, validated_data):
for attr, value in validated_data.items():
if attr == 'password':
instance.set_password(value)
else:
setattr(instance, attr, value)
instance.save()
return instance
#api.view
class GetUserInfo(APIView):
permission_classes = (IsAuthenticated,)
serializer_class = UserSerializer
def get(self, request, *args, **kwargs):
# serializer to handle turning our `User` object into something that
# can be JSONified and sent to the client.
serializer = self.serializer_class(request.user, context={"request":request})
return Response(serializer.data, status=status.HTTP_200_OK)
def put(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
#response
ValueError at /api/v1/userinfo/
Cannot assign "{'user_motto': 'js говно'}": "User.mangauser" must be a "MangaUser" instance.
What you did seems pretty complicated to me.
Did you read : https://docs.djangoproject.com/en/3.0/topics/auth/customizing/#extending-the-existing-user-model ?
This is what I use :
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
def get_profile(self):
return Profil.objects.get(user=self)
class Meta(AbstractUser.Meta):
db_table = 'auth_user'
class Profil (models.Model):
user = models.OneToOneField (settings.AUTH_USER_MODEL, unique=True, editable=False, help_text="1-to-1 to user", on_delete=models.CASCADE)
image_one = models.ImageField(upload_to="upload_to/images/", blank=True, null=True, help_text=_(u"Image qui sera accolée à votre profil."))
class Meta:
order_with_respect_to = 'user'

Django Rest Framework: Issue with extended User model and serialization

I' extending the default Django user model to make a customised user profile with additional fields.The following are the related components.
models.py
class CandidateProfile(models.Model):
user = models.OneToOneField(
User, on_delete=models.CASCADE, related_name="user")
exp = models.IntegerField(null=True, blank=True)
serilaizers.py
class CandidateProfileSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(source='pk', read_only=True)
username = serializers.CharField(source='user.username')
email = serializers.CharField(source='user.email')
groups = serializers.RelatedField(read_only=True)
password = serializers.CharField(max_length=128, source='user.password,read_only=True')
class Meta:
model = CandidateProfile
fields = ('id', 'username', 'password', 'email', 'groups')
depth = 1
def update(self, instance, validated_data):
print("In Update" + '*' * 50)
user = User.objects.get(pk=instance.user.pk)
user = instance.user
user.email = validated_data.get('user.email', user.email)
user.first_name = validated_data.get('user.first_name',
user.first_name)
user.last_name = validated_data.get('user.last_name', user.last_name)
user.save()
instance.gender = validated_data.get('gender', instance.gender)
instance.save()
return instance
def create(self, validated_data):
print('*' * 100)
print(validated_data)
user_data = validated_data.pop('user')
print(user_data)
user = User.objects.create_user(**user_data)
g = Group.objects.get(name="Candidate")
g.user_set.add(user)
user.save()
print(validated_data)
print('*' * 100)
profile = CandidateProfile.objects.create(user=user, **validated_data)
return user
views.py
class CandidateRegister(APIView):
def get(self, request, format=None):
candidate_list = User.objects.filter(groups=Group.objects.get(
name="Candidate"))
serializer = CandidateProfileSerializer(candidate_list, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = CandidateProfileSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
I've succcessfully created the user profile as well as the extended Candidate profile.But i'm encoutering an error on doing the same as follows :
Got AttributeError when attempting to get a value for field `username` on serializer `CandidateProfileSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `User` instance.
Original exception text was: 'CandidateProfile' object has no attribute 'username'.
Even with this execpiton the User profile and the related Candidate profile is created.
You can use the SerializerMethodField from docs like -
class CandidateProfileSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(source='pk', read_only=True)
username = serializers.SerializerMethodField()
def get_username(self, obj):
return obj.user.username

Django REST: create CRUD operations for OneToOne Field

I'm trying to create basic CRUD operations for OneToOne field.
The user is not required to set the profile when signing in. How do I create/update/delete profile when needed (assuming the user is already in the DB)?
My models are the default User models from Django REST and:
class UserProfile(models.Model):
user = models.OneToOneField(User)
location = models.CharField(max_length=50,blank=True)
title = models.CharField(max_length=80,blank=True)
#picture = models.ImageField(upload_to='user_imgs', blank=True)
website = models.URLField(blank=True)
My Viewsets are:
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
filter_fields = ['id', 'username', 'email', 'first_name', 'last_name']
class UserProfileViewSet(viewsets.ModelViewSet):
queryset = UserProfile.objects.all()
serializer_class = UserProfileSerializer
filter_fields = ['user_id', 'location', 'title', 'website']
And serializes:
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
email = serializers.EmailField()
fields = ('id','username', 'email', 'first_name', 'last_name')
class UserProfileSerializer(serializers.HyperlinkedModelSerializer):
user_id = serializers.CharField(source='user.id')
class Meta:
model = UserProfile
fields = ('user_id', 'location','title','website')
I belive you want to restrict the profile creation to the current logged in user. You can filter the queryset of profiles to the current user, this way only that user's profile will be accessible by the logged in user.
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
filter_fields = ['id', 'username', 'email', 'first_name', 'last_name']
class UserProfileViewSet(viewsets.ModelViewSet):
queryset = UserProfile.objects.all()
serializer_class = UserProfileSerializer
filter_fields = ['user_id', 'location', 'title', 'website']
def get_queryset(self):
return super(UserProfileViewSet, self).get_queryset().filter(
user=self.request.user)
def perform_create(self, serializer):
serializer.save(user=user)
You make the user field read only and is being saved in the above method perform_create and assigned always to the current user.
class UserProfileSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = UserProfile
fields = ('user', 'location','title','website')
read_only_fields = ('user',)
It should focus to define view how to receive request and process raw data, not the model and serializer of the field definition.
I give you a CRUD example for basic User operation as the reference:
lu = LibraryUser(library_membership_number= '...', user_id = user)
class ExampleAPIView(APIView):
def get(self, request):
username = request.query_params.get('username', '')
user = User.objects.get(username=username)
return Response(ExampleSerializer(user).data)
def post(self, request):
username = request.data.get('username', '')
email = request.data.get('email', '')
password = request.data.get('password', '')
user = User.objects.create_user(username=username, email=email, password=password)
user.save()
Response({'status': 'ok'}})
def put(self, request):
username = request.data.get('username', '')
old_password = request.data.get('old_password', '')
new_password = request.data.get('new_password', '')
user = authenticate(username=username, password=old_password)
if not user:
return Response({'status': 'fail'}})
user.set_password(new_password)
return Response({'status': 'ok'}})
def delete(self, request):
username = request.query_params.get('username', '')
user.objects.get(username=username).delete()
return Response({'status': 'ok'}})
Accord to the example, these are my definitions for each method:
GET: Retrieve the user profile
POST: Create a new user
PUT: Change the user of the password
DELETE: Delete the user
So, it will implement Basic CRUD api for user instance.
I hope that it can help you how to design api.
If you don't still understand how to operate model, I will more introduce the example:
class ExampleAPIView(APIView):
def get(self, request):
username = request.query_params.get('username', '')
userprofile = UserProfile.objects.get(user__username=username)
return Response(ExampleSerializer(userprofile).data)
def put(self, request):
username = request.data.get('username', '')
userprofile = UserProfile.objects.get(user__username=username)
if not userprofile :
return Response({'status': 'fail'}})
userprofile.location = ...
userprofile.title = ...
userprofile.website = ...
userprofile.save()
return Response({'status': 'ok'}})

django rest framework ModelSerializer post data error

first, in models.py
class UserComment(models.Model):
user = models.ForeignKey(User)
rate = models.IntegerField()
description = models.CharField(max_length=512)
createTime = models.DateTimeField(auto_now=True)
def __unicode__(self):
return '<UserComment {%s %d}>' % (self.user.username, self.rate)
then, serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email', )
class UserCommentSerializer(serializers.ModelSerializer):
user = UserSerializer(required=False)
class Meta:
model = UserComment
views.py
class UserCommentViewSet(viewsets.ModelViewSet):
queryset = UserComment.objects.all()
serializer_class = UserCommentSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly, )
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.DATA, files=request.FILES)
serializer.is_valid()
print serializer.errors
print serializer.data
return super(UserCommentViewSet, self).create(request, *args, **kwargs)
then i post json data
{"user":{"id":"1","username":"watsy"},"rate":"5","description":"hello"}
i think,it will work. and insert it to db, but i get errors.
{"user": [{"username": ["User with this Username already exists."]}]}
>_<, I have no idea.
You need to make few changes to your serializer:
class UserCommentSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = UserComment
depth = 1
Now pass this JSON dict in your request:
{"user":"1", "rate":"5", "description":"hello"}

Categories