Had implemented a basic authentication system using function based views in django.
Trying to upgrade it to class based views.
Creating a UserProfile by inheriting from a django User model.
Need to serialize UserProfile and return to client side
User model :
from django.contrib.auth.models import User
UserProfile model :
class UserProfile(models.Model):
id = models.AutoField(primary_key=True)
user = models.OneToOneField(User)
profile_picture = models.ImageField(upload_to='documents', blank=True)
def __str__(self):
return self.user.username
UserSerializer:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username','password', 'first_name', 'last_name', 'email',)
write_only_fields = ('password',)
read_only_fields = ('is_staff', 'is_superuser', 'is_active', 'date_joined',)
def restore_object(self, attrs, instance=None):
user = super(UserSerializer, self).restore_object(attrs, instance)
user.set_password(attrs['password'])
return user
UserProfileSerializer:
class UserProfileSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = UserProfile
fields = ('id','user','profile_picture',)
views.py:
class AuthView(APIView):
authentication_classes = (BasicAuthentication,)
def post(self, request, *args, **kwargs):
login(request, request.user)
content={ 'user':UserProfileSerializer(request.user).data,'token':csrf.get_token(request)}
return Response(content)
UserProfileSerializer(request.user).data in views.py is not working.
but instead if i use:
UserSerializer(request.user).data, it gives me result(as expected) :
{'first_name': '', 'username': 'admin', 'email': 'a#a.com', 'last_name': '', 'password': 'pbkdf2_'}
But i also want additional attributes to the user also serialized like profile_picture, hence something like
UserProfileSerializer(request.user).data
should work for me.
Questions:
Is it possible to serialize a model containing FileField ?
How to serialize a nested object and return its data ?
Kinda beginner here.
yes it is possible to serialize a FileField. The problem is that your profile serializer needs a UserProfile model and not a User model. Try this:
content={ 'user':UserProfileSerializer(request.user.user_profile).data,'token':csrf.get_token(request)}
Related
I am facing an issue in DRF serializers. I have a model named Issue which has a foreign Key of User Model to save the user who has created the particular Issue. Now the Get request is working fine when I request to get the issues I get it perfectly fine with username who has created the issue, but when I do the post request I get the error on "created_by" field that "This field is requied" even though I am providing this field.
Following is my code:
Model
class Issues(models.Model):
created_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='issue_created_by')
title = models.CharField(max_length=225, default='', null=False)
details = models.CharField(max_length=1000, default='')
created_on = models.DateField(default=timezone.now)
tags = models.CharField(max_length=225, blank=True, null=True, default='')
Issue Serializer
class IssueSerializer(serializers.ModelSerializer):
created_by = UserSerializer()
class Meta:
model = Issues
fields = ['created_by', 'title', 'details', 'created_on', 'tags']
UserSerializer
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username', 'first_name', 'last_name', 'email', 'password']
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = super().create(validated_data)
user.set_password(validated_data['password'])
user.save()
return True
views.py
class IssueView(viewsets.ViewSet):
def create(self, request):
serialized_issues = IssueSerializer(data=request.data)
if serialized_issues.is_valid():
serialized_issues.save()
return Response({'message': 'Issue Created', 'status': status.HTTP_200_OK})
else:
return Response({'error': serialized_issues.errors, 'status': status.HTTP_400_BAD_REQUEST})
def list(self, request):
all_issues = Issues.objects.all()
serialized_issues = IssueSerializer(all_issues, many=True)
return Response(serialized_issues.data)
To work with a writeable nested serializer you need to overwrite the create method of the serializer. You can see the documentation [here]: https://www.django-rest-framework.org/api-guide/relations/#writable-nested-serializers
It seems you are creating a new user with every Issue created. It is quite unrealistic. You need to pass the request.user object to the serializer while creating a new issue. The updated code may be like
method in view
def create(self, request, *args, **kwargs):
created_by = request.user
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
serializer.save(created_by=created_by)
return Response(data=serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(data=serializer.errors, status=status.HTTP_400_BAD_REQUEST)```
and serializer file may be like
class IssueSerializer(serializers.ModelSerializer):
class Meta:
model = Issues
fields = ['title', 'details', 'created_on', 'tags']
I just started using the Django Rest Framework recently and was wondering how to use 2 models within a single serializer. I have a custom model named 'Profile' and am also using a default Django model 'User'. With these two tables, I planned to use nested representations. Finally, in order to test the RegisterAPI, I wanted to create data using the POSTMAN tool but currently, it's not working as expected.
This is what I had done so far:
models.py:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete = models.CASCADE)
company = models.CharField(max_length=100, blank=True, null=True)
address = models.TextField()
views.py:
class RegisterAPI(APIView):
permission_classes = [AllowAny]
def post(self, request, format=None):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
content = {
'result': 'Thanks for registering!'
}
return Response(content, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializers.py:
from rest_framework import serializers
from myapp.models import Profile
from django.contrib.auth.models import User
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ['company', 'address']
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer(many = True)
class Meta:
model = User
fields = ['username', 'email', 'password', 'profile']
def create(self, validated_data):
profile_data = validated_data.pop('profile')
password = validated_data.pop('password', None)
# instance = self.Meta.model(**validated_data)
user = User.objects.create(**validated_data)
if password is not None:
user.set_password(password)
user.save()
Profile.objects.create(user = user, **profile_data)
return user
Within POSTMAN, I am not able to set the fields 'company' and 'address' for the Profile serializer. How can I do this?
I want to create a Registration API, wherein, I aim to combine the User Model and Profile Model in order to create a new user by adding the username, email, password (User Model fields) and gender, salary, company, and address (Profile Model fields). I attempted to use the source from this link. However, I am not able to POST any data in. This is my code so far:
views.py:
class RegisterAPIView(APIView):
def post(self, request, format=None):
serializer = ProfileSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response("Thank you for registering", status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializers.py:
from rest_framework import serializers
from users.models import Profile
from django.contrib.auth.models import User
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ['gender', 'company', 'salary', 'address']
class RegisterBSerializer(serializers.ModelSerializer): #User Model serializer
profile = ProfileSerializer()
class Meta:
model = User
fields = ['username', 'email', 'password']
def create(self, validated_data):
profile_data = validated_data.pop('profile')
password = validated_data.pop('password', None)
user = User.objects.create(**validated_data)
if password is not None:
user.set_password(password)
user.save()
Profile.objects.create(user = user, **profile_data)
return user
Can anyone hint me on where I am going off-track?.
I am not able to add data via DRF:
image
Usually, I should be getting a Body which states:
{"username" : "this field is required",
"email" : "this field is required",
"password" : "this field is required", (will be hashed using set_password())
"gender" : "this field is required",} etc etc...
You can do this by writing custom to_representation method like this
class RegisterBSerializer(serializers.ModelSerializer): #User Model serializer
profile = ProfileSerializer()
class Meta:
model = User
fields = ['username', 'email', 'password']
def to_representation(self, instance):
data = super().to_representation(instance)
profile = data.pop('profile', {})
data.update(profile)
return data
Currently any password that I PUT or PATCH do not get encrypted.
I am using a ModelSerializer.
class UserSerializer (serializers.ModelSerializer):
class Meta:
model = User
fields = ('url', 'username', 'email', 'pk')
With a ModelViewSet
class UserViewSet (ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
Why are my passwords saved as plain text? And how do I fix this?
Should I overwrite the update () in serialiser or update () in ViewSet? Where's the problem?
You should overwrite create and update methods in your serializers to make encryption possible:
from django.contrib.auth.hashers import make_password
class UserSerializer(serializers.ModelSerializer):
def create(self, validated_data):
user = User.objects.create_user(
password=make_password(
validated_data['user'].pop('password')
),
**validated_data.pop('user')
)
def update(self, instance, validated_data):
if 'user' in validated_data:
instance.user.password = make_password(
validated_data.get('user').get('password', instance.user.password)
)
instance.user.save()
class Meta:
model = User
fields = (
'url', 'username', 'email', 'pk'
)
I'm trying to add a custom create method, and am using the Django REST FRAMEWORK. Once a user is created, I want to create an auth token and then return a JSON response with this user AND an auth token.
UPDATE: I was able to update the below to create the user, but now I am getting Cannot assign "<User: User object>": "Token.user" must be a "User" instance What am i doing wrong?
How can I modify the below so when POST to users/, I create a user, create an auth token, and return both?
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('first_name', 'last_name', 'email', 's3_link', 'phone_confirmed', 'agreed_to_tos', 'phone_number', 'facebook_id', 'stripe_id', 'phone_verification_code')
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
##ISSUE WITH CODE STARTS HERE
user = serializer.save()
token = Token.objects.create(user=user)
You could use a custom Response to add the token to the user data:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('first_name', 'last_name', 'email', 's3_link', 'phone_confirmed', 'agreed_to_tos', 'phone_number', 'facebook_id', 'stripe_id', 'phone_verification_code')
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
# Customized rest_framework.mixins.CreateModelMixin.create():
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
# Your code
user = serializer.save()
token = Token.objects.create(user=user)
# Create custom response
data = serializer.data
# You may need to serialize your token:
# token = token.your_token_string_field
data.update({'token': token})
headers = self.get_success_headers(serializer.data)
return Response(data, status=status.HTTP_201_CREATED, headers=headers)
If you want to create a user, I would recommend following this format:
serializers.py:
class AccountCreateSerializer(serializers.ModelSerializer):
class Meta:
model = User
# You will need to check accuracy of fields, but this is for demo purposes
fields = ['username', 'email', 'password']
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = User(
username=validated_data['username'],
email=validated_data['email']
)
user.set_password(validated_data['password'])
user.save()
return user
views.py:
from rest_framework import generics
from serializers.py import AccountCreateSerializer
class AccountCreateAPIView(generics.CreateAPIView):
serializer_class = AccountCreateSerializer