Extend User model in Django with jwt authorization - django

I am trying to extend User model in Django. I already have authorization using jwt Token and I am currently trying to add another field to User model, such as phone number and address.
My views.py looks like this:
class MyObtainTokenPairView(TokenObtainPairView):
permission_classes = (AllowAny,)
serializer_class = MyTokenObtainPairSerializer
class RegisterView(generics.CreateAPIView):
queryset = User.objects.all()
permission_classes = (AllowAny,)
serializer_class = RegisterSerializer
models.py like this:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
phone_number = models.IntegerField(blank=False)
address = models.CharField(max_length=500, blank=True)
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
In here I'm trying to create another class that will store phone number and address. How can I incorporate my Profile class into User so that it will create new user model and what is more use earlier implemented Token authorization.

Related

Overwriting save method to create entry in related table automatically django

After registration email with email confirmation is sent to a new user. I created model
UserWithConfirmation with new field is_email_confirmed. I was following this https://docs.djangoproject.com/en/4.1/topics/auth/customizing/#extending-the-existing-user-model.
I want to have UserWithConfirmation created for each new user when user is saved. For now I have sth like this.
from django.db import models
from django.contrib.auth.models import User
class UserWithConfirmation(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="user_with_confirmation")
is_email_confirmed = models.BooleanField(default=False)
def __str__(self):
return self.user.username
class User:
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
create_user_with_confirmation(User)
def create_user_with_confirmation(user):
UserWithConfirmation(user=user)
UserWithConfirmation.save()
How to make it works?
Just have UserWithConfirmation extend User
class UserWithConfirmation(User):
is_email_confirmed = models.BooleanField(default=False)
and change the entry when the email is confirmed
I solved my problem using signals
I changed UserWithConfirmation to Profile
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="profile")
is_email_confirmed = models.BooleanField(default=False)
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()

Updating Profile image of a UserProfile linked to a user with OneToOne Relations

I was able to automaticcaly create a userProfile everytime a user is created but I want to be able to modify somefields in the userprofile.
So my Model.py
def upload_path(instance,filename):
return 'users/avatars/{0}/{1}'.format(instance.user.username, filename)
class UserProfile(models.Model):
user= models.OneToOneField(User,on_delete=models.CASCADE, related_name='userprofile')
Profileimage= models.ImageField(upload_to=upload_path, blank=True, null=True, default='user/avatar.jpeg')
def __str__(self):
return self.user.username
# receiver(post_save,sender=User)
def create_user_profile(sender,instance,created,**kwargs):
if created:
UserProfile.objects.create(user=instance)
My Serializer.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields =['id', 'username','password','email']
extra_kwargs={'password':{'write_only':True, 'required':True}}
def create(self,validated_data):
user =User.objects.create_user(**validated_data)
Token.objects.create(user=user)
return user
class UserProfileSerializer(serializers.ModelSerializer):
user=serializers.SlugRelatedField(queryset=models.User.objects.all(), slug_field='username')
class Meta:
model =models.UserProfile
#lookup_field = 'username'
fields= '__all__'
My view.py
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
lookup_field = 'username'
class UserProfileViewSet(viewsets.ModelViewSet):
queryset = UserProfile.objects.all()
serializer_class = UserProfileSerializer
Using postman i get the error : django.db.utils.IntegrityError: UNIQUE constraint failed: api_userprofile.user_id
So after adding
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
I was able to change the fields that I want to.

(1048, "Column 'user_id' cannot be null") with Django DRF

I wrote code to create new users via POST and DRF (Django Rest Framework) successfully and I can obtain token correctly, however when I try to POST (via DRF) to fill Profile linked to that user I get
(1048, "Column 'user_id' cannot be null")
This is snippets of my code:
for serializers:
class UserSerializer(ModelSerializer):
class Meta:
model = User
fields = ('username','email','first_name','last_name','password')
def create(self, *args, **kwargs):
user = super().create(*args, **kwargs)
p = user.password
user.set_password(p)
user.save()
return user
def update(self, *args, **kwargs):
user = super().update(*args, **kwargs)
p = user.password
user.set_password(p)
user.save()
return user
class ProfileSerializer(ModelSerializer):
class Meta:
model = Profile
fields = ('bio','birth_date','location','country')
def create(self, *args, **kwargs):
profile = super().create(*args, **kwargs)
profile.save()
return profile
def update(self, *args, **kwargs):
profile = super().update(*args, **kwargs)
profile.save()
return profile
and for views:
class ProfileViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
permission_classes = IsAuthenticated,
serializer_class = ProfileSerializer
queryset = Profile.objects.all()
class UserViewSet(viewsets.GenericViewSet,mixins.CreateModelMixin,):
"""
API endpoint that allows users to be viewed or edited.
"""
#permission_classes= (IsAdminUser,)
serializer_class = UserSerializer
queryset = User.objects.all().order_by('-date_joined')
and for models:
#receiver(post_save, sender = settings.AUTH_USER_MODEL)
def create_auth_token(sender,instance=None,created=False, **kwargs):
if created:
Token.objects.create(user=instance)
class Country(models.Model):
iso = models.CharField(max_length = 2,unique = True)
name = models.CharField(max_length = 250)
iso3 = models.CharField(max_length = 6)
phonecode = models.CharField(max_length=6)
def __unicode__(self):
return '%s' % self.name
def __str__(self):
return '%s' % self.name
class Profile(models.Model):
#user = models.OneToOneField(User, on_delete=models.CASCADE,default = "")
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE,default = "")
bio = models.TextField(max_length=100, blank=True)
birth_date = models.DateField(null=True, blank=True)
location = models.CharField(max_length=254, blank=True)
country = models.ForeignKey(Country,on_delete=models.CASCADE,blank=True,null=True)
def __unicode__(self):
return '%s' % self.user
def __str__(self):
return '%s' % self.user
#receiver(post_save, sender=User)
def update_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
When I try with Postman and I pass a token for token authentication, it recognize the token correctly but I get this error:
(1048, "Column 'user_id' cannot be null") with Django DRF
I appreciate your help to solve this problem
Create() save to the database, so you need to set profile.user_id before calling the function.

Adding field to Profile when creating User

I have a signup form that looks like this:
class SignUpForm(UserForm):
username = forms.CharField(max_length=32, min_length=1)
initials = forms.CharField(max_length=3)
password = forms.CharField(widget=forms.PasswordInput())
confirm_password = forms.CharField(widget=forms.PasswordInput())'
...
I'm creating the user like this:
class SignUpView(FormView, LoginErrorView):
form_class = SignUpForm
template_name = "website/sign_up.html"
def form_valid(self, form):
User.objects.create_user(username=form.cleaned_data['username'],
email=form.cleaned_data['email'],
password=form.cleaned_data['password'])
....
The Profile model looks like this:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
initials = models.CharField(max_length=3)
....
I'm creating the profile like this:
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
What I would like to do is to save initials in post_save/create_user_profile (in the Profile-model) from the SignUpForm when I'm creating the Profile object, but I can't figure out any simple way of doing this. Any ideas?
You can save profile just after saving user. Try this:
class SignUpView(FormView, LoginErrorView):
form_class = SignUpForm
template_name = "website/sign_up.html"
def form_valid(self, form):
user = User.objects.create(username=form.cleaned_data['username'],
email=form.cleaned_data['email'],
password=form.cleaned_data['password'])
Profile.objects.create(user=user, initials=form.cleaned_data['initials'])
Remove post_save. I hope this will work. If not, please comment.

'RelatedManager' object has no attribute 'save'

This is the models.py code
class Profile(models.Model):
user = models.ForeignKey(User, unique=True, on_delete=models.CASCADE, related_name='profile')
#receiver(post_save, sender=User)
def update_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
# Profile.objects.save()
instance.profile.save()
and This the views.py code:
def signup(request):
....
if request.method == "POST":
user_form = UserRegistrationForm(request.POST)
if user_form.is_valid():
user = user_form.save(commit=False)
user.save(using='default')
login(request, user)
return HttpResponse("User Loged In")
Then after fill the registration form and click the submit button, Django get me the following Error:
You defined a ForeignKey from a Profile to a User, but that means every Profile is attached to a User, it is however perfectly possible that multiple Profiles are attached to the same User.
Hence if you use a_user.profile, it will not return a single Profile, but a RelatedManager: a manager that manages the collection (that can contain zero, one or multiple elements) of Profile.
I think however that you actually want to associated Profiles with distinct Users, in which case you better use a OneToOneField:
class Profile(models.Model):
user = models.OneToOneField(User, unique=True, on_delete=models.CASCADE)
If you then query someuser.profile you will either get a Profile instance, or it will raise a Profile.DoesNotExists error in case no profile is associated with that user.
By default the related_name of a OneToOneField is the name of the class in lowercase, so you no longer need to specify this.
Note however that in your code instance.profile.save() is not necessary, since the Profile, is basically already saved by creating it:
#receiver(post_save, sender=User)
def update_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
# unnecessary
# instance.profile.save()