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
Related
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'
For a Django project, I have a customized User model:
class User(AbstractUser):
username = None
email = models.EmailField(_('e-mail address'),
unique=True)
first_name = models.CharField(_('first name'),
max_length=150,
blank=False)
last_name = models.CharField(_('last name'),
max_length=150,
blank=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name', 'last_name']
objects = UserManager()
def __str__(self):
return self.email
I'm creating a new user registration form:
class UserRegistrationForm(forms.ModelForm):
auto_password = forms.BooleanField(label=_('Generate password and send by mail'),
required=False,
initial=True)
password = forms.CharField(label=_('Password'),
widget=forms.PasswordInput)
password2 = forms.CharField(label=_('Repeat password'),
widget=forms.PasswordInput)
class Meta:
model = User
fields = ('email', 'first_name', 'last_name', 'is_staff',
'is_superuser')
def clean_password2(self):
cd = self.cleaned_data
if cd['password'] != cd['password2']:
raise forms.ValidationError(_("Passwords don't match."))
return cd['password2']
My form has a auto_password boolean field. When this checkbox is set, the password and password2 fields must not be checked, as their content (or the absence of content) has no importance. On the opposite, when the auto_password checkbox is unset, the password and password2 must be checked.
Is there a way to optionnally disable the Django form checks whenever needed?
Thanks for the help.
You add this to the the condition in the clean method:
class UserRegistrationForm(forms.ModelForm):
auto_password = forms.BooleanField(
label=_('Generate password and send by mail'),
required=False,
initial=True
)
password = forms.CharField(
label=_('Password'),
widget=forms.PasswordInput
)
password2 = forms.CharField(
label=_('Repeat password'),
widget=forms.PasswordInput
)
class Meta:
model = User
fields = ('email', 'first_name', 'last_name', 'is_staff',
'is_superuser')
def clean(self):
data = super().clean()
if not data['auto_password'] and data['password'] != data['password2']:
raise forms.ValidationError(_('Passwords don't match.'))
return data
The not data['auto_password'] will thus return False in case the checkbox is checked, and in that case the the check of data['password'] != data['password2'] will not run, nor will it raise a ValidationError.
You can also remove the required=True properties, and check if the password contains at least one character by checking it truthiness:
class UserRegistrationForm(forms.ModelForm):
auto_password = forms.BooleanField(
label=_('Generate password and send by mail'),
# no required=True
initial=True
)
password = forms.CharField(
label=_('Password'),
widget=forms.PasswordInput
)
password2 = forms.CharField(
label=_('Repeat password'),
widget=forms.PasswordInput
)
class Meta:
model = User
fields = ('email', 'first_name', 'last_name', 'is_staff',
'is_superuser')
def clean(self):
data = super().clean()
manual = not data['auto_password']
if manual and not data['password']:
raise forms.ValidationError(_('Password is empty.'))
if manual and data['password'] != data['password2']:
raise forms.ValidationError(_('Passwords don't match.'))
return data
Can't you just include it in your logic?
if not cd['auto_password'] and (cd['password'] != cd['password2']):
raise forms.ValidationError(_("Passwords don't match."))
I want create User, which hold Foreign Key to his Country.
Obviously I want to make it required field.
But when I send POST request without 'country' field, DRF Serializer doesn't throw an error.
Nowhere in the code have I allowed the field to be empty, it's the same as the rest.
I took a step further, and in the create() method of my ModelViewSet I decided to print serializer.validated_data
class UserViewSet(ModelViewSet):
serializer_class = UserSerializer
queryset = User.objects.all()
permission_classes = []
def create(self, request, format=None):
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
print(serializer.data)
I send following POST request:
And my serializer.validated_data value was:
OrderedDict([('password', 'test1231112'), ('first_name', 'Jarosław'), ('last_name', 'Psikuta'), ('phone', '2999111331'), ('email', 'rweww#gmail.css'), ('description', 'fajny uzytkownik'), ('address', 'Mieszka 3/4'), ('date_of_birth', datetime.date(1997, 10, 13))])
I realised that serializer just don't see my country field.
I already write some code to check if my country field exist:
country_code = request.data.get('country', None)
if not country_code:
return Response({'country': 'This field is required'}, status=status.HTTP_400_BAD_REQUEST)
but I know that's wrong approach. It's additional code, and actually serializer should do that work for me.
Here You have rest of my code:
serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
read_only_fields = ('id', 'last_login', 'is_superuser', 'is_staff', 'is_active','date_joined', 'groups', 'user_permissions')
extra_kwargs = {
'password': {'write_only': True}
}
depth = 1
def create(self, validated_data):
password = validated_data.pop('password')
user = User(**validated_data)
user.set_password(password)
user.save()
return user
models.py
class User(AbstractUser):
EMPLOYEE_TYPES = [
('R', 'Regular'),
('S', 'Specialist')
]
username_validator = None
username = None
first_name = models.CharField(_('first name'), max_length=30)
last_name = models.CharField(_('last name'), max_length=150)
phone = models.CharField(max_length=15, unique=True)
employee_type = models.CharField(max_length=1, choices=EMPLOYEE_TYPES, default='R')
email = models.EmailField(_('email address'), unique=True, max_length=255)
description = models.CharField(blank=True, max_length=500, default='')
address = models.CharField(max_length=100)
date_of_birth = models.DateField(null=True)
country = models.ForeignKey(Country, on_delete=models.CASCADE, related_name='users')
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name', 'last_name', 'date_of_birth', 'address', 'phone', 'country']
I deleted depth = 1 from serializer Metaclass and it works.
I'm working on a project using Python(3.7) and Django(2.2) in which I have implemented my models for multiple user types with custom user model as the base model. Everything working fine except the admin side, I have register these modles to admin but when I try to add an object from admin interface it's giving an error.
Here's what I have tried so far:
From models.py:
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=254, unique=True)
title = models.CharField(max_length=255, blank=False)
user_type = models.CharField(max_length=255, choices=USER_TYPE, blank=False)
gender = models.CharField(max_length=255, choices=CHOICES, blank=False)
contenst = models.CharField(max_length=255, blank=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
last_login = models.DateTimeField(null=True, blank=True)
date_joined = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = 'email'
EMAIL_FIELD = 'email'
REQUIRED_FIELDS = ['password']
objects = UserManager()
def get_absolute_url(self):
return "/users/%i/" % (self.pk)
class PersonalBelow18(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
dob = models.DateField(blank=False)
customer_id = models.BigIntegerField(blank=False)
collection_use_personal_data = models.BooleanField(blank=False)
reference_identities = models.ForeignKey(Identities, blank=False, on_delete=models.CASCADE, related_name='refs')
def __str__(self):
return self.user.email+'\'s account with ' + str(self.customer_id)
class PersonalAbove18(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
dob = models.DateField(blank=False)
customer_id = models.BigIntegerField(blank=False)
contact_email = models.EmailField(blank=False)
reference_identities = models.ForeignKey(Identities, blank=False, on_delete=models.CASCADE)
contact_no = PhoneNumberField(blank=True, help_text='Phone number must be entered in the'
'format: \'+999999999\'. Up to 15 digits allowed.')
collection_use_personal_data = models.BooleanField(blank=False)
def __str__(self):
return self.user.email+'\'s account with ' + str(self.customer_id)
class Parent(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
contact_email = models.EmailField(blank=False)
customer_id = models.BigIntegerField(blank=True)
contact_no = PhoneNumberField(blank=True, help_text='Phone number must be entered in the'
'format: \'+999999999\'. Up to 15 digits allowed.')
collection_use_personal_data = models.BooleanField(blank=False)
class GroupContactPerson(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
contact_email = models.EmailField(blank=False)
customer_id = models.BigIntegerField(blank=False)
contact_no = PhoneNumberField(blank=True, help_text='Phone number must be entered in the'
'format: \'+999999999\'. Up to 15 digits allowed.')
department = models.CharField(max_length=255, blank=False)
address = models.TextField(max_length=255, blank=False)
and here's how I register these models to admin:
From admin.py:
class UserAdmin(BaseUserAdmin):
fieldsets = (
(None, {'fields': ('email', 'password', 'title', 'user_type',
'gender', 'contenst', 'last_login')}),
('Permissions', {'fields': (
'is_active',
'is_staff',
'is_superuser',
'groups',
'user_permissions',
)}),
)
add_fieldsets = (
(
None,
{
'classes': ('wide',),
'fields': ('email', 'password1', 'password2')
}
),
)
list_display = ('email', 'title', 'is_staff', 'last_login')
list_filter = ('is_staff', 'is_superuser', 'is_active', 'groups')
search_fields = ('email',)
ordering = ('email',)
filter_horizontal = ('groups', 'user_permissions',)
admin.site.register(User, UserAdmin)
admin.site.register(PersonalBelow18)
admin.site.register(PersonalAbove18)
admin.site.register(Parent)
admin.site.register(GroupContactPerson)
The Parent and GroupContactPerson models are working well on admin side but the when I try to add an object for PersonalBelow18 & PersonalAbove18 models, it gives the following error as:
TypeError: str returned non-string (type int)
Here's how I debug this problem in these models:
I start removing all fields one-by-one
Remove a field from model & form and perform migrations
Then test the admin
Then I found that when I removed the reference_identities field it start working, so I get that this model was returning an integer, so I fixed that model and it fix the issue.
In short, it's a good approach to find a path to the actual place of problem by removing fields one-by-one and test the admin.
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',
]