User Edit Profile Form won't edit all fields,Django - python

I have created a registration form using Django's usercreation form.
Then i created a EditProfileForm using the registration form.
My issue is that EditProfileForm uses User Model, and the Birthdate field is in Profile Model. Thus, I am able to edit all fields except Birthdate as it is not from the User Model.
How do I go about editing birthdate, or creating a form where I can edit all fields instead of only the user fields?
My Model for Profile:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.PROTECT)
def __str__(self):
return self.user.username;
USWEST = 'usw'
USEAST = 'use'
EUROPE = 'eu'
OCEANIA = 'oce'
ASIA = 'as'
SOUTHAMERICA = 'sam'
SOUTHAFRICA = 'saf'
MIDDLEEAST = 'me'
PREF_SERVER_CHOICES = (
(USWEST, 'US-West'),
(USEAST, 'US-East'),
(EUROPE, 'Europe'),
(OCEANIA, 'Oceania'),
(ASIA, 'Asia'),
(SOUTHAMERICA, 'South America'),
(SOUTHAFRICA, 'South Africa'),
(MIDDLEEAST, 'Middle-East'),
)
pref_server = models.CharField(
max_length=3,
choices=PREF_SERVER_CHOICES,
default=USWEST,
)
birth_date = models.DateField(null=True, blank=False,)
sessions_played = models.IntegerField(null=False, blank=False, default='0',)
teamwork_commends = models.IntegerField(null=False, blank=False, default='0',)
communication_commends = models.IntegerField(null=False, blank=False, default='0',)
skill_commends = models.IntegerField(null=False, blank=False, default='0',)
positivity_commends = models.IntegerField(null=False, blank=False, default='0',)
FORMS.PY
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from apps.api.models import Profile, Feedback
from django.forms import ModelForm
#form to create profile
#RegistrationForm VIEW must be created first as well as URl
class RegistrationForm(UserCreationForm):
first_name = forms.CharField(max_length=30, required = False, help_text='Optional.')
last_name = forms.CharField(max_length=30, required = False, help_text='Optional.')
email = forms.EmailField(max_length=254, required=True, help_text='Required. Enter a valid email address.')
birth_date = forms.DateField(help_text='Required. Format: YYYY-MM-DD')
#Class meta will dictate what the form uses for its fields
class Meta:
model = User
fields = (
'username',
'first_name',
'last_name',
'email',
'birth_date',
'password1',
'password2',
)
#Function to save form details
def save(self,commit=True):
if commit:
#Setting Commit to false,otherwise It will only save the fields existing in UserCreationForm
user = super(RegistrationForm, self).save(commit=False)
#Adding additional Fields that need to be saved
#Cleaned data prevents SQL Injections
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.email = self.cleaned_data['email']
Profile.birth_date = self.cleaned_data['birth_date']
user.save()
return user
# User editing profile details
class EditProfileForm(RegistrationForm):
class Meta:
model = User
fields = [
'email',
'first_name',
'last_name',
'birth_date',
]

Related

How to make sequential signup pages with Django allauth?

I currently have a single page signup form implemented with allauth
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
email = models.EmailField(_('Professional email address'), unique=True)
username = models.CharField(_("User Name"), blank=False, max_length=255, unique=True)
first_name = models.CharField(_("First Name"), null=True, max_length=255, default='')
last_name = models.CharField(_("Last Name"), null=True, max_length=255, default='')
country = CountryField(_("Country of Practice"), blank_label='(Country of Practice)', blank = False, default='GB')
terms = models.BooleanField(verbose_name=_('I have read and agree to the terms and conditions'), default=False)
def get_absolute_url(self):
return reverse(
"users:detail", kwargs={"username": self.username}
)
objects = UserManager()
And this is the forms.py
class UserCreationForm(forms.UserCreationForm):
error_message = forms.UserCreationForm.error_messages.update(
{"duplicate_username": _("This username has already been taken.")}
)
username = CharField(label='User Name',
widget=TextInput(attrs={'placeholder': 'User Name'}))
class Meta(forms.UserCreationForm.Meta):
model = User
fields = ['username', 'email', 'first_name', 'last_name', 'password1', 'password2', 'terms']
field_order = ['username', 'email', 'first_name', 'last_name', 'password1', 'password2', 'terms']
def clean_terms(self):
is_filled = self.cleaned_data['terms']
if not is_filled:
raise forms.ValidationError('This field is required')
return is_filled
def clean_username(self):
username = self.cleaned_data["username"]
if self.instance.username == username:
return username
try:
User._default_manager.get(username=username)
except User.DoesNotExist:
return username
raise ValidationError(
self.error_messages["duplicate_username"]
)
I would like however for the first sign up page to have a ‘next’ button at the bottom and then there would be a second page where the user input separate details (the data input here might vary based on the inputs in the first page). The Django ‘form tools’ form wizard seems well suite to this but I can’t work out how to integrate it with all auth
Any suggestions much appreciated

Django User model saves twice into database

I am trying to create multi user registration system with Django. However, anytime I call the save() method to save a User type, it saves into the User table twice. The funny thing about the second model that is saved is that many required fields are empty.
I am using a custom user model that I created from AbstractBaseUser. I also rewrote the forms for the CustomUser model. For the multiple user types, I am using a profile model (Student model has a OneToOne field to the user model)
models.py:
class User(AbstractBaseUser, PermissionsMixin):
# I've removed some unimportant code here
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
class Types(models.TextChoices):
STUDENT = 'STUDENT', 'Student'
DEPARTMENT_USER = 'DEPARTMENT_USER', 'Department user'
ADMIN = 'ADMIN', 'Admin'
user_type = models.CharField(_('Type'), max_length=50, choices=Types.choices, default=Types.STUDENT)
first_name = models.CharField(_('First name'), max_length=70, blank=False, default="")
middle_name = models.CharField(_('Middle name'), max_length=70, blank=True, default="")
last_name = models.CharField(_('Last name'), max_length=70, blank=False, default="")
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False) # a admin user; non super-user
is_superuser = models.BooleanField(default=False) # a superuser
last_login = models.DateTimeField(null=True, blank=True)
date_joined = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = 'email'
EMAIL_FIELD = 'email'
REQUIRED_FIELDS = ['user_type', 'first_name', 'last_name'] # Email & Password are required by default.
objects = UserManager()
class Meta:
verbose_name = ('user')
verbose_name_plural = ('users')
#db_table = 'auth_user'
abstract = False
class AccountConfirmed(models.Model):
# Model to determine which users have confirmed their email addresses.
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='accountconfirmed')
email_confirmed = models.BooleanField(default=False)
reset_password = models.BooleanField(default=False)
class Meta:
app_label = 'auth'
# When the user model is created, through signals an AccountConfirmed model is also created.
# The email_confirmed and reset_password field is set to false.
#receiver(models.signals.post_save, sender=User)
def update_user_profile(sender, instance, created, **kwargs):
if created:
AccountConfirmed.objects.create(user=instance)
instance.accountconfirmed.save()
######################################################
######################################################
class Student(User):
# This is the model class for students
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True, related_name='students')
matric_number = models.CharField(_('Matriculation number'), max_length=11, blank=False)
department = models.CharField(_('Department'), max_length=40, blank=False)
# has_graduated, level, etc. future possibilities
def __str__(self):
return f'{self.user.email}'
forms.py:
class StudentSignupForm(UserCreationForm):
# first_name = forms.CharField(max_length=70)
# middle_name = forms.CharField(max_length=70, required=False)
# last_name = forms.CharField(max_length=70)
matric_number = forms.CharField(min_length=10, max_length=11, help_text='Your Matric number must be 10 characters')
department = forms.CharField(max_length=40, help_text='e.g Computer Science')
class Meta(UserCreationForm.Meta):
model = User
fields = UserCreationForm.Meta.fields + ('matric_number', 'department')
#transaction.atomic
def save(self, commit=True):
# Save the User instance and get a reference to it
user = super().save(commit=False)
user.user_type = User.Types.STUDENT
user.is_active = False
#if commit:
user.save()
print(f' forms.py {user.email} {user.first_name}')
student = Student.objects.create(user=user, matric_number=self.cleaned_data.get('matric_number'), department=self.cleaned_data.get('department'))
# Add other details
# Return User instance, not Student instance
return user
views.py:
class StudentUserSignupView(CreateView):
model = User
template_name = 'account/signup.html'
form_class = StudentSignupForm
def get_context_data(self, **kwargs):
kwargs['user_type'] = 'STUDENT'
return super().get_context_data(**kwargs)
def form_valid(self, form):
user = form.save()
#login(self.request, user)
send_verification_mail(self, user)
return redirect('verification_sent')
Anytime a user signs up, this is what the students table looks like:
Also, this is what the users table look like after signup (with the multiple saves)
So how do I correct the multiple saves in the user table?
Also, How is it even possible to save a model with most of the required fields empty?
As pointed out by #RaghavKundra, the line below was what caused the problem of saving multiple times to the database
class Student(User):
Instead of that, it should be
class Student(models.Model):

Django create user profile

I created a user profile model for my system. I created all models and it works perfectly. I have a form, and the form works too. But when I look user create form from admin page, it doesn't look the same.
There are some missing parts like rank, comp_name. How can I fix it?
models.py
class UserProfile(models.Model):
ranks = (
('xxx', 'xxx'),
...
)
comp_name = models.CharField(max_length=200, default="Choose")
user_id = models.UUIDField(default=uuid.uuid4(), editable=False, unique=True)
username = models.CharField(max_length=500)
first_name = models.CharField(max_length=200, default=None)
last_name = models.CharField(max_length=200, default=None)
password = models.CharField(max_length=50)
email = models.EmailField(max_length=254)
rank = models.CharField(max_length=200, choices=ranks)
forms.py
class SignUpForm(UserCreationForm):
comp_name = forms.CharField(label='What is your company name?')
email = forms.CharField(max_length=254)
rank = forms.ChoiceField(label='What is your rank?', choices=UserProfile.ranks)
first_name = forms.CharField(max_length=250)
last_name = forms.CharField(max_length=250)
comp_name = forms.ModelChoiceField(queryset=CompanyProfile.objects.all())
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email', 'comp_name', 'password1', 'password2', 'rank'
admin
admin panel "Change User" screen
In forms.py
class SignUpForm(UserCreationForm):
comp_name = forms.CharField(label='What is your company name?')
email = forms.CharField(max_length=254)
rank = forms.ChoiceField(label='What is your rank?', choices=UserProfile.ranks)
first_name = forms.CharField(max_length=250)
last_name = forms.CharField(max_length=250)
comp_name = forms.ModelChoiceField(queryset=CompanyProfile.objects.all())
class Meta:
model = User --> change to UserProfile
fields = ('username', 'first_name', 'last_name', 'email', 'comp_name', 'password1', 'password2', 'rank'

Django REST Framework : Serializer field level validation of OneToOne fields

I am creating a view that handles a new Customer registration. My Customer model OneToOne links to Person which again OneToOne links to User where User is AUTH_USER_MODEL. When I send a POST request for customer registration through the register_customer view, person_serialized.is_valid() returns {"user":["This field is required."]} as the user object is not available and passed on the PersonSerializer. I think there should be a better and cleaner to do what I am doing here. Any solutions?
models.py
class User(AbstractBaseUser, PermissionsMixin):
USERNAME_FIELD = 'mobile_number'
# Form validation errors
mobile_number_errors = {'required': 'Mobile number is required',
'invalid': 'Enter a valid 10 digit mobile number' +
'without spaces, + or isd code.'}
_mobile_regex_validator = RegexValidator(regex=r"^\d{10}$",
message="Phone number must be 10 digits without + or spaces.")
mobile_number = models.CharField("Mobile Number", max_length=10,
validators=[_mobile_regex_validator],
blank=False, null=False, unique=True,
error_messages=mobile_number_errors)
is_active = models.BooleanField("Is Active?", default=True)
is_staff = models.BooleanField("Is Staff?", default=False)
created = models.DateTimeField("Account created on", auto_now_add=True, blank=True, null=True)
modified = models.DateTimeField("Last Modified on", auto_now=True, blank=True, null=True)
objects = UserManager()
def __unicode__(self):
return self.mobile_number
def get_full_name(self):
return self.mobile_number
def get_short_name(self):
return self.mobile_number
class Person(models.Model):
user = models.OneToOneField('users.User')
GENDER_CHOICES = (('M', 'Male'),
('F', 'Female'),
('N', 'Not Specified'))
first_name = models.CharField("First Name", max_length=32, blank=False, null=False)
last_name = models.CharField("Last Name", max_length=32, blank=False, null=False)
gender = models.CharField("Gender", max_length=1, choices=GENDER_CHOICES, blank=False, default='N')
class Customer(models.Model):
person = models.OneToOneField('users.Person')
email_errors = {'required': 'Email field is required.',
'invalid': 'Enter a valid email id.'}
email = models.EmailField("Email", blank=False, null=False, unique=True,
error_messages=email_error
serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'mobile_number',)
class PersonSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = Person
fields = ('id', 'user', 'first_name', 'last_name', 'gender',)
class CustomerSerializer(serializers.ModelSerializer):
person = PersonSerializer()
class Meta:
model = Customer
fields = ('id', 'person', 'email',)
views.py
from django.shortcuts import render
from .serializers import UserSerializer, PersonSerializer, CustomerSerializer
from .models import User, Person, Customer
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
# Create your views here.
#api_view(['POST'])
def register_customer(request):
user_serialized = UserSerializer(data=request.data)
person_serialized = PersonSerializer(data=request.data)
customer_serialized = CustomerSerializer(data=request.data)
if user_serialized.is_valid() and person_serialized.is_valid() and customer_serialized.is_valid():
user = User.objects.create_user(mobile_number=user_serialized.data['mobile_number'],
password=user_serialized.init_data['password'])
person = Person(user=user, first_name=person_serialized.data['first_name'],
last_name=person_serialized.data['last_name'],
gender=person_serialized.data['gender'])
person.save()
customer = Customer(person=person, email=customer_serialized.data['email'])
customer.save()
return Response(request.data, status=status.HTTP_201_CREATED)
else:
return Response(person_serialized._errors, status=status.HTTP_400_BAD_REQUEST)

how to change the order of the fields?

please help change the order of the form fields on the screen.
I created the following model:
class UserProfile(User):
status = models.CharField(
max_length=30,
blank=True,
)
address = models.EmailField(
max_length=50,
blank=True,
)
objects = UserManager()
then combined field of this model built with fields of the registration form:
class MyRegistrationForm(UserCreationForm):
username = forms.CharField(
label='username',
help_text='',
required=True,
)
password1 = forms.CharField(
label='pass1',
help_text='',
required=True,
)
password2 = forms.CharField(
label='pass2',
help_text='',
required=True,
)
class Meta:
model = UserProfile
fields = ('status', 'address')
as a result the user sees on the display field in this order:
status, address, username, pass1, pass2
but I need the user to see the screen fields in the following order:
username, address, pass1, pass2, status
According to the documentation:
The generated Form class will have a form field for every model field
specified, in the order specified in the fields attribute.
class MyRegistrationForm(UserCreationForm):
...
class Meta:
model = UserProfile
fields = ('username', 'address', 'password1', 'password2', 'status')
See also: How can I order fields in Django ModelForm?

Categories