I'm trying to create a profile for my user using django rest auth model, but when I'm sending the data to the user's creation, the user's image is not being filled, that is, it's getting null, I tried to send the node as much as image, as much as profile.image, but without success below my code follows:
models\profile.py
from django.conf import settings
from django.db import models
class Profile(models.Model):
image = models.ImageField(blank=True)
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
def __str__(self):
return self.user.email
serializers\user.py
from rest_framework import serializers
from rest_framework.validators import UniqueValidator
from django.contrib.auth.models import User
from src.v1.user.models.profile import Profile
from .profile import ProfileSerializer
class UserSerializer(serializers.ModelSerializer):
profile = serializers.SerializerMethodField()
email = serializers.EmailField(
required=True,
validators=[UniqueValidator(queryset=User.objects.all())]
)
username = serializers.CharField(
max_length=32,
validators=[UniqueValidator(queryset=User.objects.all())]
)
password = serializers.CharField(min_length=6, write_only=True)
#staticmethod
def get_profile(user):
"""
Get or create profile
"""
profile, created = Profile.objects.get_or_create(user=user)
return ProfileSerializer(profile, read_only=True).data
def create(self, validated_data):
user = User(email=validated_data['email'],
username=validated_data['username'])
user.set_password(validated_data['password'])
user.save()
return user
class Meta:
model = User
fields = ('id', 'username', 'email', 'password', 'profile')
serializers\profile.py
from rest_framework import serializers
from src.v1.user.models.profile import Profile
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = '__all__'
class ProfileSerializerUpdate(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ('image',)
views\user.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from src.v1.user.serializers.user import UserSerializer
from rest_framework.authtoken.models import Token
class UserView(APIView):
"""
Creates the user.
"""
def post(self, request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
user = serializer.save()
if user:
token = Token.objects.create(user=user)
json = serializer.data
json['token'] = token.key
return Response(json, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
SerializerMethodField is read only by design. So it is not just the image, nothing is getting posted on the profile.
Also, creating the profile if it doesn't exist during a GET request is a bad design because generally, GET requests should be safe and not alter the state of the application.
You should instead, create the profile during user creation. Since there is just one field to be posted in the profile, it may be simpler and more effective to use a flat representation for the post data.
This is what you should do.
serializers/user.py
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer(read_only=True)
image = serializers.ImageField(write_only=True)
email = serializers.EmailField(
required=True,
validators=[UniqueValidator(queryset=User.objects.all())]
)
username = serializers.CharField(
max_length=32,
validators=[UniqueValidator(queryset=User.objects.all())]
)
password = serializers.CharField(min_length=6, write_only=True)
def create(self, validated_data):
user = User(email=validated_data['email'], username=validated_data['username'])
user.set_password(validated_data['password'])
user.save()
Profile.objects.create(user=user, image=validated_data['image')
return user
Now you can post your image field with the rest of the user fields
Related
I am trying to build my first Django backend project, so i'm trying to creatre a REST API end-point that gets a user registration data in json file from the front-end and save it on the database if it's valid.
I am trying to save the additional user information in a new model called Player and link it to the default User model using one-to-one-field.
When i recive the json file with the data from the front-end the a new user with the data is created in the User model, also a new row is created in the Player model that connected to the user we just created in the User model. But the problem is the fields "height" and "handicap" remain empty.
I don't know how to save the "height" and "handicap" parameters into the new Player instance.
This is my models.py file:
from django.db import models
from django.core.validators import MaxValueValidator, MinValueValidator
from django.contrib.auth.models import User
from django.dispatch import receiver
from django.db.models.signals import post_save
from datetime import *
# This model extend the basic built-in User model, by adding additional information on the uesr like
# handicap score anf height.
class Player(models.Model):
def __str__(self):
return self.user.username
user = models.OneToOneField(User, on_delete=models.CASCADE) # connecting this model to the User model
# (cascade means when deleting a user row in the user table
# the match row in this table will automatically will be deleted)
handicap = models.IntegerField(default=0, validators=[MinValueValidator(0), MaxValueValidator(28)])
height = models.IntegerField(default=0, validators=[MinValueValidator(0), MaxValueValidator(250)])
registration_date = models.DateTimeField(default=datetime.now())
#a listener that listen to the User model, if a new user as been save, it creates a new row in the player model with the new user in the user field
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created: # if a new user created in the User model
Player.objects.create(user=instance) # creating a new row in player, inserting the new user instance to the user field
# if a User is saved we update the user instance to the player user field
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
This is my serializers.py file:
from rest_framework import serializers
from django.contrib.auth.models import User
from rest_framework.validators import UniqueValidator
from django.contrib.auth.password_validation import validate_password
from django.core.validators import MaxValueValidator, MinValueValidator
from .models import Player
# class serializer that handel the data from user registration
class RegisterSerializer(serializers.ModelSerializer):
first_name = serializers.CharField(required=True)
last_name = serializers.CharField(required=True)
email = serializers.EmailField(required=True, validators=[UniqueValidator(queryset=User.objects.all())]) # making sure that the email that the user entered have not being used by another user
password = serializers.CharField(write_only=True, required=True, validators=[validate_password]) # checking that the password is valid
password2 = serializers.CharField(write_only=True, required=True)
height = serializers.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(250)])
handicap = serializers.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(28)])
class Meta: # nested class that gives the serializer details
model = User
fields = ('first_name', 'last_name', 'email', 'password', 'password2', 'height', 'handicap')
# overriding the built-in validation method of the model serializer
def validate(self, attrs):
if attrs['password'] != attrs['password2']: # if the 2 passwords on the form don't match
raise serializers.ValidationError({'password': "passwords don't match!"}) # raising an error
return attrs
# overriding the built-in create method
def create(self, validated_data):
# creating a user instance with the data came from the registration
user = User.objects.create(username=validated_data['email'], first_name=validated_data['first_name'], last_name=validated_data['last_name'], email=validated_data['email'], password=validated_data['password'])
user.save() # saving the user registration data to the database
player = Player.objects.get(user=user)
player.height = validated_data['height']
player.handicap = validated_data['handicap']
player.save()
return user
This is my views.py file:
from rest_framework import generics
from .serializers import *
# Create your views here.
class RegistrationView(generics.CreateAPIView):
queryset = User.objects.all()
serializer_class = RegisterSerializer
This is my urls.py file:
from django.urls import path
from .views import RegistrationView
urlpatterns = [
path('registration/', RegistrationView.as_view(), name='registration')
]
Does someone know what to do in order to also save the "height" and "handicap" to the Player model?
The problem you are facing can be solved with a serializer relation, more specifically with a nested relationship.
I have tried to preserve your code as much as I could. Although, some changes are necessary or just have been made to make the code cleaner.
models.py
from django.db import models
from django.core.validators import MaxValueValidator, MinValueValidator
from django.contrib.auth.models import User
from django.dispatch import receiver
from django.db.models.signals import post_save
class Player(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
handicap = models.IntegerField(default=0, validators=[MinValueValidator(0), MaxValueValidator(28)])
height = models.IntegerField(default=0, validators=[MinValueValidator(0), MaxValueValidator(250)])
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Player.objects.create(user=instance)
def __str__(self):
return self.user.username
Removed 'registration_date' field. The Abstract user model already has a field called 'date_joined', no need to store the same information. Also, the second function 'save_user_profile ' was not necessary.
serializers.py
from rest_framework import serializers
from django.contrib.auth.models import User
from rest_framework.validators import UniqueValidator
from django.contrib.auth.password_validation import validate_password
from django.core.validators import MaxValueValidator, MinValueValidator
from core.models import Player
class PlayerSerializer(serializers.ModelSerializer):
height = serializers.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(250)])
handicap = serializers.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(28)])
class Meta:
model = Player
exclude = ['user']
class RegisterSerializer(serializers.ModelSerializer):
first_name = serializers.CharField(required=True)
last_name = serializers.CharField(required=True)
email = serializers.EmailField(required=True, validators=[UniqueValidator(queryset=User.objects.all())])
password = serializers.CharField(write_only=True, required=True, validators=[validate_password])
password2 = serializers.CharField(write_only=True, required=True)
player = PlayerSerializer()
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email', 'password', 'password2', 'player')
def validate(self, attrs):
if attrs['password'] != attrs['password2']:
raise serializers.ValidationError({'password': "passwords don't match!"})
return attrs
def create(self, validated_data):
pw2 = validated_data.pop('password2')
player = validated_data.pop('player')
user = User.objects.create(**validated_data)
user.player.height = player['height']
user.player.handicap = player['handicap']
user.player.save()
return user
Created a PlayerSerializer to nest within the RegistrationSerializer. To represent the fields of 'handicap' and 'height' by excluding the 'user' field.
On the create method, poping keys out of the dictionary to use the **kwargs for a cleaner format. Later, using the keys to update relationship values.
views.py and urls.py remains untouched.
You created the user object in the serializer. In the next line you try to get the user instance from Player model. Actually that user's instance is not created in the Player model yet. So first you have to create that user instance in the Player model.
def create(self, validated_data):
# first, get the data for player model from validated data
height = validated_data.pop.get('height')
handicap = validated_data.pop.get('handicap')
# now create the user
user = User.objects.create(**validated_data)
# now using the user instance, create the player object for that user.
Player.objects.create(user=user, height=height, handicap=handicap)
return user
Perhaps the question is wrongly worded. I created user profile using Django through the following blocks of code:
models.py
class = Profile (models.Models):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
first name = models.CharField(max_length=50)
•••
serializer.py
class profile_serializer(serializers.ModerlSerializer)
class Meta:
model = Profile
fields = '__all__'
views.py
class profile_view(generics.ListCreateAPIView)
queryset = Profile.objects.all().filter(user=instance)
urls.py
urlspatterns = [path('profile', profile_view.as_view(), name='user_profile)
I definitely do not know how to implement the filter method to ensure that only the logged in user is retrieved. Or is there a better approach to obtain a specific user? If I use Project.objects.all() without the filter I get all the registered user as expected. But I don't know how to retrieve a particular user.
Hmm, I would do something like this:
from rest_framework.response import Response
from rest_framework import status, generics, permissions
class UserView(generics.GenericAPIView):
permission_classes = (permissions.IsAuthenticated,)
serializer_class = profile_serializer
def get(self, request):
user = request.user
return Response(profile_serializer(user).data,status=status.HTTP_200_OK)
Basically when a user is authenticated, their user is present in the request.
Here's the UserSerializer, comments was hard to format. (Ironically for a tech forum?)
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
def get(self, instance):
return instance
def patch(self, instance, validated_data):
instance.model_method()
return super().update(instance, validated_data)
def create(self, validated_data):
user = self.context['request'].user
return super().update(user, validated_data)
And here's the profile_serializer:
class profile_serializer(serializers.ModerlSerializer):
user = UserSerializer(read_only=True)
class Meta:
model = Profile
fields = ('user', 'first_name', )
I'm new in Django rest framework, I tried my whole day but can't do it,I want to do full crud operation in my UserProfile Model which have a OneToOne field user, User can only update their own profile and in UserProfile create or update user shouldn't update User[username], How can i achieve it Please Help me
*** serializers.py ***
from rest_framework import serializers
from product.models import UserProfile
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
read_only_fields = ['username','password', ]
class UserProfileSerializer(serializers.ModelSerializer):
user = UserSerializer(many=False)
class Meta:
model = UserProfile
fields = "__all__"
def create(self, validated_data):
user_data = validated_data.pop('user')
user_instance = User.objects.get(
username=user_data['username'])
user_instance.save()
user_profile_instance = UserProfile.objects.create(
**validated_data, user=user_instance)
user_profile.save()
return user_profile
*** views.py ***
from django.shortcuts import render
from .serializers import UserProfileSerializer
from rest_framework.views import APIView
from rest_framework import generics, permissions
from rest_framework.response import Response
from rest_framework import status
from django.contrib.auth.models import User
from product.models import UserProfile
# Create your views here.
class CreateUserView(generics.ListCreateAPIView):
serializer_class = UserProfileSerializer
permission_classes = [permissions.IsAuthenticated,]
def get_queryset(self):
user = self.request.user
return UserProfile.objects.filter(user = user)
*** models.py ***
from django.db import models
from django.contrib.auth.models import User
from django.core.validators import MaxValueValidator, MinValueValidator
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='user_profile', on_delete=models.CASCADE)
country = models.CharField(max_length=50, default='India')
city = models.CharField(max_length=100, default='')
phone = models.CharField(max_length=15,default='')
image = models.ImageField(upload_to='profile_image', blank=True)
created_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.user.username
For Authentication you can use token based authentication(like jwt)
and for username you can use read_only=True
no need to send the password for get request
to update profile you need to handle put/post methods
CLEANED Serializers:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
read_only_fields = ['username','password']
class UserProfileSerializer(serializers.ModelSerializer):
# REMOVED all unecessary overrides
user = UserSerializer(read_only=True)
class Meta:
model = UserProfile
fields = "__all__"
Views:
class UserProfileViewSet(viewsets.GenericViewSet,
mixins.UpdateModelMixin):
# Changed inherited class and class NAME !
# I assume that your endpoint is something like /users/me/profile
# I think you want only to update user profile
# Listing or creating profile here is bad - user should have only ONE profile
# and you should do this on user model post_save signal
serializer_class = UserProfileSerializer
permission_classes = [permissions.IsAuthenticated,]
def get_object(self):
return self.request.user.user_profile
This setup will allow you to update profile and only profile data
In your models file you can make signal listener for automatically creating UserProfile object on User object create.
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
When I try to make a POST request to my API endpoint to create a user in my Users table, I get mysterious Serializer errors. The error says that the email, password, codename (3 strings needed to create a user) are required. But I am sending all three strings. It is because the serializer is NOT valid (go to serializers.py, the if serializer.is_valid() check)... but I can't figure out why it's not valid.
Entire said error message:
{'codename': [ErrorDetail(string=u'This field is required.', code=u'required')], 'password': [ErrorDetail(string=u'This field is required.', code=u'required')], 'email': [ErrorDetail(string=u'This field is required.', code=u'required')]}
All these files are in my users folder within my Django directory.
serializers.py:
from rest_framework import serializers
from .models import User
class UserPostSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('email', 'password', 'codename')
views.py:
from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework import status
from .serializers import *
from .models import User
from argon2 import PasswordHasher
import json
#api_view(['POST'])
def create_user(request):
print("inside create_user")
""" POST = Create user. """
data = {}
# print("request.data:\n", request.data)
serializer = UserPostSerializer(data=request.data)
print("got serializer")
if serializer.is_valid():
print("serializer is valid!")
email = serializer.data['email']
codename = serializer.data['codename']
password = serializer.data['password']
user = User.objects.filter(email=email)
if not user:
# Apply Argon2
ph = PasswordHasher()
hash = ph.hash(password)
# Create user
new_user = User(email=email, password=hash, codename=codename)
new_user.save()
request.session['email'] = email
request.session['id'] = new_user.pk
print(request.session['email'], "has logged in!")
print(request.session['id'], "user's id")
return Response(serializer.data, status=status.HTTP_200_OK)
# User with this email found... Please login...
else:
return Response(status=status.HTTP_409_CONFLICT)
else:
print(serializer.errors)
return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)
models.py:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.
class User(models.Model):
email = models.CharField(max_length=255, blank=False)
codename = models.CharField(max_length=255, blank=False)
password = models.CharField(max_length=255, blank=False)
profilePic = models.ImageField(upload_to='images/', blank=True)
I ended up finding a Stack Overflow post where they suggested to add extra_kwargs which specify that they are not required. This made my if serializers.is_valid() pass!
class UserPostSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('email', 'password', 'codename')
extra_kwargs = {'email': {'required': False}, 'password': {'required': False}, 'codename': {'required': False}}
A newbie here. This is a Django related question.
How can I save a newly registered user to the User Model (auth.model)? Currently, the only account which is seen inside the admin panel -- under Users (Authentication and Authorization section) is the superuser (aka me).
I am using DRF (Rest framework) in order to register a user and not an HTML form.
models.py:
class Register(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
username = models.CharField(max_length = 100)
email = models.EmailField(max_length = 100)
password = models.CharField(max_length = 100)
def __str__(self):
return self.name
views.py:
class RegisterView(APIView):
def post(self, request, format=None):
serializer = RegisterSerializer(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 .models import Register
class RegisterSerializer(serializers.ModelSerializer):
class Meta:
model = Register
fields = ['username', 'email', 'password']
When registering a new user via POSTMAN, the data is saved within the Register model (which is fine) but my issue is that it's not seen within the Users model.
Any feedback is highly appreciated. Thank you.