Excluding password when extending UserCreationForm - django

I want to make a view where the user can change the attributes for his User object: first_name, last_name and email address. Django has a built in UserCreationForm for creating users. I already have an extended version that also includes email, first_name and last_name. Now I am trying to extend that one to not include the password and username.
from django.contrib.auth.forms import UserCreationForm
class ExtendedUserCreationForm(UserCreationForm):
class Meta:
model = User
fields = ('username', 'password1', 'password2',
'email', "first_name", 'last_name' )
class UserEditForm(ExtendedUserCreationForm):
class Meta:
model = User
exclude = ('username', 'password1', 'password2')
The UserEditForm I am looking for will of course not be able to create new instances, but it will only be used to edit existing instances:
form = UserEditForm(instance=request.user)
However, the username field and password fields are still shown. How can I exclude these?

You can just remove the password2 field in the init of the form like so:
class MyUserCreationForm(UserCreationForm):
def __init__(self, *args, **kargs):
super(MyUserCreationForm, self).__init__(*args, **kargs)
del self.fields['password2']

It's not a bad idea to have a look at a class you subclass. password1 and password2 fields are defined in form directly, not in the model. So exclude and fields will have no effect on them. Just make your own ModelForm as #MatthewSchinckel suggests.

think that would give the idea...
class RegistrationForm(UserCreationForm):
email = forms.EmailField(required=True)
#first_name = forms.CharField(required=True)
#last_name = forms.CharField(required=True)
username = forms.CharField (required=True)
class Meta:
model = User
fields = ('first_name','last_name','username','email', 'password1', 'password2')
def __init__ (self, *args, **kwargs):
super(RegistrationForm,self).__init__(*args, **kwargs)
#remove what you like...
self.fields.pop ('first_name')
self.fields.pop ('last_name')
self.fields.pop ('password1')
self.fields.pop ('password2')

Why not just use a ModelForm, and exclude the fields you don't want? That seems like it would be a simpler solution.

Related

Removing username from UserCreationForm and auto generating username for user based on email, first name and last name

I am trying to auto generate username for users as they sign up, however, I do not know where to set the username. I do not want to user signals, so therefore I want to set the username for users when the user signs up.
In my UserCreationForm I have remove the username by:
users = None
and inside the SingUpForm(UserCreationForm) method I have:
class SignUpForm(UserCreationForm):
class Meta:
fields = (
"first_name",
"last_name",
"email",
)
However, I do not know how to set the username for the user, should I set the username for user inside the signup view?
user = form.save(commit=False)
user.username = generate_username(user.first_name, user.last_name, user.email)
user.save()
Or should I have the generate_username() method inside my custom creation form?
Or should I have the generate_username() method inside my custom creation form?
That is probably more appropriate, since the form normally should be responsible to create a valid Userobject, you thus an override the def save method:
class SignUpForm(UserCreationForm):
class Meta:
fields = (
'first_name',
'last_name',
'email',
)
def save(self, *args, **kwargs):
user = self.cleaned_data
self.instance.username = generate_username(
user['first_name'],
user['last_name'],
user['email']
)
return super().save(*args, **kwargs)
then in the view, it is simply:
user = form.save()

Overwrite maxlength/minlength of username by Django User model in the ModelForm

Try to overwrite User models by the following code, but somehow I cannot overwrite the max_length and min_length of username.
More specifically, when I check by python manage.py shell, I do overwrite them. But it seems has no effect on the html which was rendered(username maxlength is still 150).
Don't know which parts get wrong, please help.
from django import forms
from django.contrib.auth.models import User
class RegistrationForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(RegistrationForm, self).__init__(*args, **kwargs)
email = self.fields['email']
username = self.fields['username']
email.required = True
email.label_suffix = ' '
username.label_suffix = ' '
######### this is not work!!!###############
username.min_length = 6
username.max_length = 30
############################################
class Meta:
model = User
fields = ('username', 'email')
labels = {
'username': '帳號',
}
help_texts = {
'username': '',
}
Instead of modifying the form, you should modify/override the model.
I recommend using django-auth-tools for building your own custom user model. It supplies basic models, views and forms which can be easily extended.
If you are trying to override just the model form field, you could do something like this
class RegistrationForm(forms.ModelForm):
username = forms.CharField(required=True, min_length=6, max_length=30)
class Meta:
model = User
fields = ('username', 'email')
or
class RegistrationForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(RegistrationForm, self).__init__(*args, **kwargs)
self.fields['username'] = forms.CharField(required=True, min_length=6, max_length=30)
class Meta:
model = User
fields = ('username', 'email')
But I would recommend creating a Custom User Model inherited from AbstractBaseUser to override the username or email field as documented in https://docs.djangoproject.com/en/1.10/topics/auth/customizing/

How to make UserCreationForm email field required

I am a newbie in Django.
I would like the email field in the subclassed UserCreationForm to be required.
I have tried the commented methods but none has worked so far. I have tried the solution from this but to no avail. Any help would be appreciated.
class MyRegistrationForm(UserCreationForm):
captcha = NoReCaptchaField()
#email = forms.EmailField(required=True, widget=forms.TextInput(attrs={'class': 'mdl-textfield__input'}))
class Meta:
model = User
fields = ('first_name', 'last_name', 'username', 'email', 'password')
#email = {
# 'required': True
#}
widgets = {
'first_name': forms.TextInput(attrs={'class': 'mdl-textfield__input'}),
'last_name': forms.TextInput(attrs={'class': 'mdl-textfield__input'}),
'username': forms.TextInput(attrs={'class': 'mdl-textfield__input'}),
#'email': forms.TextInput(attrs={'class': 'mdl-textfield__input'})
}
def save(self, commit=True):
user = super(MyRegistrationForm, self).save(commit=False)
user.first_name = self.cleaned_data["first_name"]
user.last_name = self.cleaned_data["last_name"]
user.username = self.cleaned_data["username"]
user.email = self.cleaned_data["email"]
#user.user_level = self.cleaned_data["user_level"]
if commit:
user.save()
return user
def __init__(self, *args, **kwargs):
super(MyRegistrationForm, self).__init__(*args, **kwargs)
self.fields['password1'].widget.attrs['class'] = 'mdl-textfield__input'
self.fields['password2'].widget.attrs['class'] = 'mdl-textfield__input'
#self.fields['email'].required=True
This solved the problem: email = forms.CharField(required=True, widget=forms.EmailInput(attrs={'class': 'validate',}))
I checked Django's User model and it has required=False. So, I think you cannot achieve what you are looking for with the default User model based on note section of "Overriding the default fields" in the django documentation. I have inluded the snippet
ModelForm is a regular Form which can automatically generate certain
fields. The fields that are automatically generated depend on the
content of the Meta class and on which fields have already been
defined declaratively. Basically, ModelForm will only generate fields
that are missing from the form, or in other words, fields that weren’t
defined declaratively.
Fields defined declaratively are left as-is, therefore any
customizations made to Meta attributes such as widgets, labels,
help_texts, or error_messages are ignored; these only apply to fields
that are generated automatically.
Similarly, fields defined declaratively do not draw their attributes
like max_length or required from the corresponding model. If you want
to maintain the behavior specified in the model, you must set the
relevant arguments explicitly when declaring the form field.
For example, if the Article model looks like this:
class Article(models.Model):
headline = models.CharField(
max_length=200,
null=True,
blank=True,
help_text='Use puns liberally',
)
content = models.TextField() and you want to do some custom validation for headline, while keeping the blank and help_text values
as specified, you might define ArticleForm like this:
class ArticleForm(ModelForm):
headline = MyFormField(
max_length=200,
required=False,
help_text='Use puns liberally',
)
class Meta:
model = Article
fields = ['headline', 'content'] You must ensure that the type of the form field can be used to set the contents of the corresponding
model field. When they are not compatible, you will get a ValueError
as no implicit conversion takes place.
So try this,
from django.forms import EmailField
from django.core.validators import EMPTY_VALUES
# I used django [emailfield code][2] as reference for the code of MyEmailField
# Also, following comment in django [custom form fields document][2]:
# If the built-in Field classes don’t meet your needs, you can easily create custom Field classes. To do this, just create a subclass of django.forms.Field. Its only requirements are that it implement a clean() method and that its __init__() method accept the core arguments mentioned above (required, label, initial, widget, help_text).
# You can also customize how a field will be accessed by overriding get_bound_field().
class MyEmailField(forms.EmailField):
def __init__(self, *args, **kwargs):
super(MyEmailField, self).__init__(*args, strip=True, **kwargs)
# Clean would be called when checking is_clean
def clean(self,value):
if value in EMPTY_VALUES:
raise Exception('Email required')
value = self.value.strip()
return super(MyEmailField, self).clean(value)
class MyRegistrationForm(UserCreationForm):
captcha = NoReCaptchaField()
# All available arguments listed in django [core fields argument document][2]. Currently they are required, label, label_suffix, initial, widget, help_text, error_messages, validators, localize, disabled
email = MyEmailField(required=True)
class Meta:
model = User
fields = ('first_name', 'last_name', 'username', 'email', 'password')
# other part of your code
PS: I have not tested this code but based on the documentation I think this should take you in a good direction.
Few more references:
Django auth.user with unique email
How to make email field unique in model User from contrib.auth in Django
https://simpleisbetterthancomplex.com/tutorial/2016/07/22/how-to-extend-django-user-model.html
Add this to your forms.py file:
class Userform(UserCreationForm):
email = forms.EmailField(required=True)
class meta:
model = User
fields = ('name','email')

Django related models and UpdateView fields

I created a model (UserSettings) to extend django's User model through a OneToOneField (as recommended by the documentation):
class UserSettings(models.Model):
user = models.OneToOneField(User, primary_key=True)
subscribeToMails = models.BooleanField(default=True)
[...]
I wish to offer my users a way to edit some of their profile data, some of which is stored in the User model (the email address), and the rest in the UserSettings model. How may I do that?
I thought of two ways: adding another OneToOneField in the UserSettings model for the email address field; or overriding the UpdateView get_queryset() method (but I'm not sure how). Is there a best or recommended way to do it? So far here's how my view look:
class EditUser(UpdateView):
model = UserSettings
fields = ('emailVisible', 'subscribeToMails', 'mpPopupNotif',
'mpEmailNotif', 'avatar', 'quote', 'website')
template_name = 'user/edit.html'
def get_object(self):
return UserSettings.objects.get(user_id=self.request.user)
def get_success_url(self):
return reverse_lazy('user:edit')
Thanks for the replies! However, since I couldn't figure out how to make this work and thought using two tables eventually resulted in too much clutter to my taste, I finally went with the easier route and subclassed AbstractUser:
# models.py
class ForumUser(AbstractUser):
subscribeToMails = models.BooleanField(default=True)
[...]
# views.py
class EditUser(LoginRequiredMixin, UpdateView):
model = ForumUser
fields = ('email', 'emailVisible', 'subscribeToMails', 'mpPopupNotif',
'mpEmailNotif', 'avatar', 'quote', 'website')
template_name = 'user/edit.html'
success_url = reverse_lazy('forum:welcome')
def get_object(self):
return ForumUser.objects.get(username=self.request.user)
I only had to change my registration form:
# forms.py
class RegisterForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = ForumUser
fields = ('username', 'email', 'password1', 'password2')
def clean_email(self):
"Ensure registered emails are unique."
email = self.cleaned_data.get('email')
username = self.cleaned_data.get('username')
if email and ForumUser.objects.filter(email=email).exclude(
username=username).count():
raise forms.ValidationError('Email address already in use.')
return email
def clean_username(self):
"""
UserCreationForm method where mentions of the User model are replaced
by the custom AbstractUser model (here, ForumUser).
https://code.djangoproject.com/ticket/19353#no1
and https://docs.djangoproject.com/en/1.7/_modules/django/contrib/
auth/forms/#UserCreationForm
"""
username = self.cleaned_data["username"]
try:
ForumUser.objects.get(username=username)
except ForumUser.DoesNotExist:
return username
raise forms.ValidationError(
self.error_messages['duplicate_username'],
code='duplicate_username',
)
Use this solution:
mix both User and UserSettings in a form like this:
class EmployeeEditForm(forms.ModelForm):
#fields from User model that you want to edit
first_name = forms.CharField(required=False, label=_('First Name'))
last_name = forms.CharField(required=False, label=_('Last Name'))
class Meta:
model = UserSettings
fields = ('first_name', 'last_name', 'subscribeToMails')
You can access to User and UserSettings object in views.py like this:
user = request.user
usersettings = user.usersettings
Now you can edit User object like this:
user.first_name = request.POST['first_name']
user.last_name = request.POST['last_name']
user.save()
And edit UserSettings like this:
usersettings.subscribeToMails = request.POST['subscribeToMails']
usersettings.save()
Formsets is the best way to go about it.
https://docs.djangoproject.com/en/dev/topics/forms/formsets/

"Please correct the error below" in the Django admin when using custom user models

I am working on a Django app and I followed exactly these instructions to build a custom User.
Now when I try to create a new user from the admin panel, I get this error message
so not very useful. Also I have the same problem whether I use the "change" form or the "create" form.
However if I try to create a new user through the shell, like
MyUser.objects.create_user(email="test#gmail.com", password=None)
it works.
Troubleshooting
Here is the model of the custom user:
class MyUser(AbstractBaseUser):
"""
A base user on the platform. Users are uniquely identified by
email addresses.
"""
email = models.EmailField(
verbose_name = "Email address",
max_length = 100,
unique = True
)
is_active = models.BooleanField(default=True, blank=True)
is_admin = models.BooleanField(default=False, blank=True)
#property
def is_staff(self):
return self.is_admin
objects = MyUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ()
def get_full_name(self):
return self.email
def get_short_name(self):
return self.email
def __unicode__(self):
return self.email
def has_perm(self, perm, obj=None):
'''Does the user have a specific permission?'''
return True
def has_module_perms(self, app_label):
'''Does the user have permissions to view the app `app_label`?'''
return True
One explanation is that it has something to do with a field of MyUser that has blank=False but that is not displayed by my ModelForm. I double checked, and it's fine.
Another explanation would be that the validation of the admin creation form has somehow inherited from the default User model of django.contrib.auth and it is trying to find a field from User that does not exist in MyUser. How can I check that?
Any idea?
I had a similar problem. But it wasn't in the Admin form, it was at the admin list. I was using list_editable fields. When I would save changes, I would get the "Please correct the error below" message with nothing highlighted.
My error was that I had included the first field in the list_display as list_editable. To correct it, I add 'id' to the front of the list_display fields.
Problem
I had a similar problem.
But that's pretty easy to solve.
How to solve it step by step?
First of all we need to talk about overriding, as you said before.
Django by default uses username field in models. And you need to change it like this in your models.py:
USERNAME_FIELD = 'email'
If you still really want to override a lot of code, next: create managers. Something about this: django managers
In your custom manages class you need to override user creation (create_user method) and create_superuser method With this samples:
def create_user(self, email, password, **extra_fields):
"""
Create and save a User with the given email and password.
"""
log.debug(f'Creating user: {email}')
if not email:
raise ValueError(_('The email must be set'))
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save()
log.info('Created user %s', repr(user))
Only after all this steps you can take care about overriding existing forms.
Django doc about this: https://docs.djangoproject.com/en/3.0/topics/auth/customizing/#custom-users-admin-full-example
implementation:
class CustomUserCreationForm(UserCreationForm):
class Meta(UserCreationForm):
model = CustomUser
fields = ('email', )
class CustomUserChangeForm(UserChangeForm):
class Meta:
model = CustomUser
fields = ('email', )
This forms are used for user Creation in your admin and User changing.
And only now you can add code in your admin.py and change your UserAdmin like that:
#admin.register(CustomUser)
class CustomUserAdmin(UserAdmin):
add_form = CustomUserCreationForm
form = CustomUserChangeForm
fieldsets = (
(None, {'fields': ('email', 'password')}),
('Personal info', {'fields': ('first_name', 'last_name',)}),
('Permissions', {
'fields': ('is_admin',),
}),
('Important dates', {'fields': ('last_login',)}),
)
# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
# overrides get_fieldsets to use this attribute when creating a user.
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'password1', 'password2', 'is_admin', 'is_root', )}
),
)
list_display = ('email', 'first_name', 'last_name', 'is_admin', 'created_at', 'updated_at',)
list_filter = ('is_admin',)
search_fields = ('email', 'first_name', 'last_name',)
ordering = ('email',)
filter_horizontal = ()
Be sure to add add_fieldsets and to override ordering by email.
In add_fieldsets you need to 2 type of passwords. If there be only 1 - error occures. From your screen by the way.
I hope this helps everyone who ever encounters this problem.
Ok guys, thanks for your answers but my problem actually came from my UserAdmin override.
More specifically, UserAdmin uses add_form and form to refer to the creation and change forms respectively. As I named my variables creation_form and change_form, it did not override the django.contrib.auth.models.User forms and that's why some fields did not validate because my ModelForms were not displaying those User fields.
Now I have renamed creation_form to add_form and change_form to form inside my custom UserAdmin and it works like a charm :-)
I think that this can help, because password CharField in AbstractBaseUser has blank property set default to False:
class MyUser(AbstractBaseUser):
def __init__(self, *args, **kwargs):
super(MyUser, self).__init__(*args, **kwargs)
passwd = [field for field in self._meta.fields if field.attname is 'password']
if passwd:
passwd[0].blank = True