I am using rest-auth registration api for user registration. I have some extra fields in the UserProfile model.
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
org_id = models.CharField(max_length=100, default='')
is_teacher = models.BooleanField(blank=True, default=False)
def __str__(self):
return self.user.username
def create_profile(sender, **kwargs):
if kwargs['created']:
user_profile = UserProfile.objects.create(user=kwargs['instance'])
post_save.connect(create_profile, sender=User)
The UserProfile model is shown above. How can I add these fields to rest-auth regestration api endpoint and save the data to database.
I found an answer for myself
The serializers can be written as
from rest_framework import serializers
from rest_auth.registration.serializers import RegisterSerializer
from .models import UserProfile
class RegistrationSerializer(RegisterSerializer):
first_name = serializers.CharField(required=False)
last_name = serializers.CharField(required=False)
personal_id = serializers.CharField(required=True)
def custom_signup(self, request, user):
user.first_name = self.validated_data.get('first_name', '')
user.last_name = self.validated_data.get('last_name', '')
user.userprofile.personal_id = self.validated_data.get(
'personal_id', '')
user.save(update_fields=['first_name', 'last_name'])
user.userprofile.save(update_fields=['org_id'])
I didnt add the is_teacher because its optional.
In views.py extend the RegisterView of the rest_auth.regeistration.views to pass this data and its done.
class RegistrationView(RegisterView):
serializer_class = RegistrationSerializer
And finally add a url and pass RegisterView.as_view().
Related
Im still learning Django and I am stuck at user registration / profile creation.
My goal
So, the purpose of this is to save the new user and at the same time save the profile of the new user with de data from the form. I use 2 forms u_form and p_form.
What I have done so far:
Created model Profile with OneToOneField to User
Created 2 forms for User (u_form) and Profile (p_form)
Created signals.py to create new Profile when new User is created
In the view I have create function with u_form.save()
Problem
This works, but the new Profile is completely empty.. When I put p_form.save() in my view it gives me this error:
NOT NULL constraint failed
The code
models.py
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
voorletter = models.CharField(max_length=10)
voorvoegsel = models.CharField(max_length=10)
achternaam = models.CharField(max_length=200)
depers = models.CharField(max_length=25)
depersoud = models.CharField(max_length=25)
telefoonnummer = models.CharField(max_length=25)
class Meta:
verbose_name = "Collega"
verbose_name_plural = "Collega's"
def __str__(self):
return self.depers
signals.py
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.dispatch import receiver
from .models import Profile
#receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_profile(sender, instance, **kwargs):
instance.profile.save()
views.py
from django.contrib.auth import login
from django.contrib.auth.decorators import login_required
from gebruikers.forms import UserRegisterForm, ProfileRegisterForm
def gebruiker_create(request):
if request.method == "POST":
u_form = UserRegisterForm(request.POST)
p_form = ProfileRegisterForm(request.POST)
if u_form.is_valid() and p_form.is_valid():
u_form.save()
messages.success(request, f'Account is aangemaakt.')
return redirect('login')
else:
u_form = UserRegisterForm()
p_form = ProfileRegisterForm()
context = {
'u_form': u_form,
'p_form': p_form
}
return render(request, 'users/register.html', context)
forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from .models import Profile
class UserRegisterForm(UserCreationForm):
email = forms.EmailField(label = "Email")
password1 = forms.CharField(widget=forms.TextInput(attrs={'type':'password'}), label="Wachtwoord", help_text=None)
password2 = forms.CharField(widget=forms.TextInput(attrs={'type':'password'}), label="Wachtwoord herhalen", help_text=None)
class Meta:
model = User
fields= ['username', 'email', 'password1', 'password2']
class ProfileRegisterForm(forms.ModelForm):
voorletters = forms.CharField(label="Voorletters", max_length=10)
voorvoegsel = forms.CharField(label="Voorvoegsel", max_length=50)
achternaam = forms.CharField(label='Achternaam', max_length=100)
depers = forms.CharField(label='Depers', max_length=8)
depersoud = forms.CharField(label='Oude Depers', max_length=50)
telnummer = forms.CharField(label="Telefoonnummer", max_length=20)
class Meta:
model = Profile
fields = ['voorletters', 'voorvoegsel', 'achternaam', 'depers', 'depersoud', 'telnummer']
If you want Profile fields to be empty initially just add blank=True in every model fields except for user .
voorletter = models.CharField(max_length=10, blank=True)
.....
.....
.....
telefoonnummer = models.CharField(max_length=25, blank=True)
Then run makemigrations and migrate command to successfully make the changes in the database.
it will solve NOT NULL constraint failed error
i wanted to add custom field with django-allauth SingupForm and adding new field like phone number. i already managed to add this field in Postgresql on my own(without migrations,but by my hands).
this is my postgresql screen
In my signup page i have these fields already but i can't managed to add "phone" to my database, i really want to make it! please someone help me.
forms.py
from allauth.account.forms import SignupForm
from django import forms
class CustomSignupForm(SignupForm):
first_name = forms.CharField(max_length=30, label='Voornaam')
last_name = forms.CharField(max_length=30, label='Achternaam')
phone = forms.CharField(max_length=30, label='phone')
def __init__(self, *args, **kwargs):
super(CustomSignupForm, self).__init__(*args, **kwargs)
self.fields['first_name'] = forms.CharField(required=True)
self.fields['last_name'] = forms.CharField(required=True)
self.fields['phone'] = forms.CharField(required=True)
def save(self, request):
user = super(CustomSignupForm, self).save(request)
user.phone = self.cleaned_data.get('phone')
user.save()
return user
def signup(self,request,user):
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
return user
settings.py
ACCOUNT_FORMS = {'signup': 'registration.forms.CustomSignupForm'}
What you need is a profile model which is attached to a user so that you can add extra fields for information you might want.
If you're starting a project from the very beginning you can also consider a custom user model so that all data is on one object.
When I do this, I create an accounts app which I put my overrides in for allauth and my model starts something like this (modified to add the receiver function which I don't have at the moment because I'm not using Profile objects);
from django.contrib.auth import get_user_model
from django.db import models
from django.utils.translation import ugettext_lazy as _
User = get_user_model()
class Profile(models.Model):
"""
Profile model
"""
class Meta:
"""
Metadata
"""
app_label = 'accounts'
verbose_name = _('User profile')
verbose_name_plural = _('User profiles')
user = models.OneToOneField(
verbose_name=_('User'),
to=User,
related_name='profile',
on_delete=models.CASCADE
)
# Add your fields here like `phone`
def __str__(self):
"""
String representation
"""
return f'User Profile for: {self.user}'
#receiver(post_save, sender=User)
def create_profile(sender, instance, **kwargs):
"""
Setup a profile as a user is created
"""
Profile.objects.create(user=instance) # Using `create` also saves the object
Your signup form then does something like
def save(self, request):
user = super(CustomSignupForm, self).save(request)
user.profile.phone = self.cleaned_data.get('phone')
user.profile.save()
return user
If you've already got users you'll also need a migration which creates profiles for them;
# Generated by Django 2.2.12 on 2020-05-01 22:03
from django.db import migrations
def create_profiles(apps, schema_editor):
User = apps.get_model('authentication', 'User') # this should match the User model you are using
Profile = apps.get_model('accounts', 'Profile')
for user in User.objects.all():
profile = Profile.objects.create(user=user)
profile.save()
class Migration(migrations.Migration):
dependencies = [
('accounts', '0001_initial'),
]
operations = [
migrations.RunPython(create_profiles, migrations.RunPython.noop)
]
I'm learning django and I made the tutorial on django site. I thought that I could link user to poll that he created but I'm struggling with it. When I'm logged in and creating a poll I can't see user name. In database column author_id has value null. I would appreciate every help.
Here is my code
from django.db import models
from django.utils import timezone
import datetime
from django.contrib import auth
# Create your models here.
User = auth.get_user_model()
class Question(models.Model):
question_text = models.CharField(max_length=200)
author = models.ForeignKey(User, on_delete=models.CASCADE, null=False)
def __str__(self):
return self.question_text
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
def __str__(self):
return self.choice_text
class User(auth.models.User, auth.models.PermissionsMixin):
def __str__(self):
return "#{}".format(self.username)
forms.py:
class UserCreateForm(UserCreationForm):
class Meta:
fields = ('username', 'email', 'password1', 'password2')
model = get_user_model()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['username'].label = 'Display Name'
self.fields['email'].label = 'Email Address'
class CreatePollForm(forms.ModelForm):
class Meta:
model = Question
fields = ('question_text',)
and views.py
class CreatePoll(LoginRequiredMixin, generic.CreateView):
form_class = forms.CreatePollForm
success_url = reverse_lazy('pollapp:index')
template_name = 'polls/createPoll.html'
Since your CreatePollForm only assigns the question_text field, you need to assign the author in code. A CreateView is a FormView which does the saving of the form in its form_valid() method. So in your CreateView, you want to override that method:
# in class CreatePoll
def form_valid(self, form):
question = form.save(commit=False) # fetch the new question, don't save
question.author = self.request.user # assign the user
question.save() # now save
return super().form_valid(form)
It isn't clear how you could end up with a null value for the author_id, that should have raised an IntegrityError. Are you sure you ran makemigrations and migrate in the current state?
I'm making a site in Django using django-allauth for authentication.
I've created a custom user class in accounts.models to add a custom field, FavouriteTeam.
The issue I have is that the form renders fine and submits formdata for fav_team fine (as inspected in Chrome dev tools) but the fav_team entry doesn't get stored to user.FavouriteTeam and I can't figure out why.
I can go into the Django shell, import my User class from accounts.models, query for a user, add a .FavouriteTeam, and save just fine. It's just that the form doesn't seem to save the data into the new User instance for some reason.
I'm guessing it's due to the way django-allauth interacts with custom user models but I can't figure it out for the life of me. I've seen some similar posts but none have a situation like this or have a solution that seems to work for me.
Any ideas?
accounts.models: -
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
FavouriteTeam = models.ForeignKey('predictor.Team', on_delete=models.CASCADE, blank=True, null=True)
def __str__(self):
return self.email
accounts.forms: -
from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import User
from allauth.account.forms import SignupForm
from predictor.models import Team
from django.contrib.auth import get_user_model
class CustomSignupForm(SignupForm):
first_name = forms.CharField(max_length=30, label='First Name')
last_name = forms.CharField(max_length=30, label='Last Name')
fav_team = forms.ModelChoiceField(queryset=Team.objects.all(), empty_label=None, label='Favourite Team')
class Meta:
model = get_user_model()
def signup(self, request, user):
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.FavouriteTeam = self.cleaned_data['fav_team']
user.save()
return user
predictor.models: -
class Team(models.Model):
ShortName = models.CharField(max_length=4, primary_key=True)
Town = models.CharField(max_length=20)
Nickname = models.CharField(max_length=20)
Conference = models.CharField(max_length=3, null=True, blank=True)
Division = models.CharField(max_length=5, null=True, blank=True)
ConfDiv = models.CharField(max_length=9, null=True, blank=True)
Logo = models.ImageField(default='football.png', upload_to='logos')
def __str__(self):
return('{} {}'.format(self.Town, self.Nickname))
def save(self, *args, **kwargs):
self.ConfDiv = str(self.Conference)+" "+str(self.Division)
super(Team, self).save(*args, **kwargs)
For anyone looking at this and suffering the same issue, I eventually found the solution to be as below.
Firstly, I had to create adpaters.py within my accounts app and fill in the below: -
from allauth.account.adapter import DefaultAccountAdapter
class AccountAdapter(DefaultAccountAdapter):
def save_user(self, request, user, form, commit=False):
data = form.cleaned_data
user.email = data['email']
user.first_name = data['first_name']
user.last_name = data['last_name']
user.FavouriteTeam = data['fav_team']
if 'password1' in data:
user.set_password(data['password1'])
else:
user.set_unusable_password()
self.populate_username(request, user)
user.save()
return user
Then I had to referenced the new account adapter in my project's settings.py file as below: -
ACCOUNT_ADAPTER = "accounts.adapters.AccountAdapter"
Hope that helps someone in the future.
I want to create a registration app for my project.
Here is my serializer:
from rest_framework import serializers
from rest_framework.validators import UniqueValidator
from django.contrib.auth.models import User
from rest_framework import serializers
from django.contrib.auth import get_user_model # If used custom user model
UserModel = get_user_model()
class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True)
def create(self, validated_data):
user = UserModel.objects.create(
username=validated_data['username'],
email=validated_data['email'],
first_name=validated_data['first_name'],
last_name=validated_data['last_name']
)
user.set_password(validated_data['password'])
return user
class Meta:
model = User
fields = ('id', 'username', 'password','email','first_name','last_name')
write_only_fields = ('password',)
read_only_fields = ('id',)
As you see, I use UserModel which is one of the default models of rest_framework. I want to make first_name field required for my registration serializer.
Waiting for your help.
You need to specify required=True attrubute for first_name field:
class UserSerializer(serializers.ModelSerializer):
first_name = serializers.CharField(required=True)
password = serializers.CharField(write_only=True)