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

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.

Related

DRF: make a POST query without data

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.

Password not getting encrypted while registering a user in Django

serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email')
class RegisterSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email', 'password')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = User(validated_data['username'], validated_data['email'])
user.set_password(validated_data['password'])
user.save()
return user
views.py
class RegisterAPI(generics.GenericAPIView):
serializer_class = RegisterSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
print("user = ",user)
return Response({
"user": UserSerializer(user, context=self.get_serializer_context()).data,
"token": AuthToken.objects.create(user)[1]
})
# Create your views here.
class LoginAPI(KnoxLoginView):
permission_classes = (permissions.AllowAny,)
def post(self, request, format=None):
serializer = AuthTokenSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
login(request, user)
return super(LoginAPI, self).post(request, format=None)
# Get User API
class UserAPI(generics.RetrieveAPIView):
permission_classes = [permissions.IsAuthenticated,]
serializer_class = UserSerializer
def get_object(self):
return self.request.user
The password is not getting encrypted in specified format, and hence the login API is also not working. It shows -
"non_field_errors": [
"Unable to log in with provided credentials."
]
Also, in the admin panel of django, it shows this invalid password format
Update your "RegisterSerializer" class "create" method.
def create(self, validated_data):
user = User(validated_data['username'], validated_data['email'])
user.password = make_password(validated_data['password'])
user.save()
return user
And don't forget to import make_password method.
from django.contrib.auth.hashers import make_password
And you create method is not properly indented.
Serializer.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email')
class RegisterSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email', 'password')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = User(validated_data['username'], validated_data['email'])
user.set_password(validated_data['password'])
user.save()
return user

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'

create() takes 1 positional argument but 3 were given

It's my first time creating an api rest with django rest framework, I got to the point of Register users and log them with token, my problem is that all this I could do with the default user of Django, in my case I need a personalized user that has another boolean variable called is_technical (is_technical). I will put the relevant code that I made so far but to be clear, I want to make a login system with DRF and One-To-One Link...
models.py (i tried override several times the create method but it doesnt work)
class Usuario(models.Model):
user = models.OneToOneField(User, on_delete = models.CASCADE)
es_tecnico = models.BooleanField(name = 'es_tecnico', default = False, blank = True)
views.py
class UsuarioViewSet(viewsets.ModelViewSet):
lookup_field = 'id'
serializer_class = UsuarioSerializer
queryset = Usuario.objects.all().filter(es_tecnico = False)
class TecnicoViewSet(viewsets.ModelViewSet):
lookup_field = 'id'
serializer_class = UsuarioSerializer
queryset = Usuario.objects.all().filter(es_tecnico = True)
class PedidoViewSet(viewsets.ModelViewSet):
lookup_field = 'id'
serializer_class = PedidoSerializer
queryset = Pedido.objects.all()
# class PedidoMiUsuarioSet(viewsets.ModelViewSet):
# serializer_class = PedidoSerializer
# queryset = Pedido.objects.all().order_by('-id').filter(autor = "3")
class Registrar(mixins.CreateModelMixin, viewsets.GenericViewSet):
serializer_class = UsuarioSerializer
def create(self, request, *args, **kwargs):
# Creando un nuevo usuario
username = request.POST.get('user.username')
password = request.POST.get('user.password')
es_tecnico = request.POST.get('user.es_tecnico')
print(username)
user = User.objects.create_user(username, password)
user.save()
token = Token.objects.create(user=user)
# usuario = Usuario.objects.create(user, es_tecnico)
# usuario.save()
return Response({'detail': 'El usuario fue creado con el token: ' + token.key})
class LoginView(mixins.CreateModelMixin, viewsets.GenericViewSet):
serializer_class = LoginSerializer
def create(self, request):
serializer = LoginSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data["usuario"]
django_login(request, user)
token, created = Token.objects.get_or_create(user=user)
return Response({"token": token.key}, status=200)
class LogoutView(mixins.CreateModelMixin, viewsets.GenericViewSet):
authentication_classes = (TokenAuthentication, )
def create(self, request):
django_logout(request)
return Response(status=204)
enter code here
serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username',
'password')
class UsuarioSerializer(serializers.ModelSerializer):
user = UserSerializer(required=True)
class Meta:
model = Usuario
fields = ('user',
'es_tecnico')
# def create(self, validated_data):
# """
# Overriding the default create method of the Model serializer.
# :param validated_data: data containing all the details of student
# :return: returns a successfully created student record
# """
# user_data = validated_data.pop('user')
# user = UserSerializer.create(UserSerializer(), validated_data=user_data)
# usuario, created = Usuario.objects.update_or_create(user=user,
# es_tecnico=validated_data.pop('es_tecnico'))
# return usuario
# class TecnicoSerializer(serializers.ModelSerializer):
# class Meta:
# model = Tecnico
# fields = ('id',
# 'email',
# 'password')
class PedidoSerializer(serializers.ModelSerializer):
class Meta:
model = Pedido
fields = ('id',
'tipo_de_pedido',
'autor',
'tecnico_asignado',
'asunto',
'detalles',
'prioridad',
'sistema',
'fecha',
'archivo_adjunto')
class LoginSerializer(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField()
def validate(self, data):
username = data.get("username", "")
password = data.get("password", "")
if username and password:
user = authenticate(username=username, password=password)
if user:
if user.is_active:
data["user"] = user
else:
msg = "Usuario desactivado"
raise exceptions.ValidationError(msg)
else:
msg = "Imposible loguear con los parametros dados"
raise exceptions.ValidationError(msg)
else:
msg = "Se necesita el username y password"
raise exceptions.ValidationError(msg)
return data
If you need more info or code, let me know i will try to reply as soon as possible. Or if you recommend another framework to work with "custom users" i will apreciate it
change
usuario = Usuario.objects.create(user, es_tecnico)
to
usuario = Usuario.objects.create(user=user, es_tecnico=es_tecnico)
in /misitio/pedidos/views.py, line 58

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'}})

Categories