how to make email field required in the django user admin - django

I want to make the email field required in the user admin add and
change pages. I read this post: Django User model email field: how to make it mandatory and I did this:
class MyUserCreationForm(UserCreationForm):
def __init__(self, *args, **kwargs):
super(MyUserCreationForm, self).__init__(*args, **kwargs)
# make user email field required
self.fields['email'].required = True
class UserAdmin(BaseUserAdmin):
form = MyUserCreationForm
add_form = MyUserCreationForm
add_fieldsets = ((None, {'fields': ('username', 'email',
'password1', 'password2'), 'classes': ('wide',)}),)
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
This works fine in add user, but in change user I get the user's
encrypted password shown in the password field, instead of what you
normally see:
algorithm: pbkdf2_sha256 iterations: 24000 salt: ****** hash:
**************************************
Raw passwords are not stored, so there is no way to see this user's
password, but you can change the password using this form.
And when I try to save from the change screen it says "Please correct
the errors below." even though there are no errors shown.
How can I fix these issues in the change form?

Have a look at the source code of the UserAdmin.
The UserAdmin uses a UserChangeForm as it's formproperty and a UserCreationForm as it's add_form property. But you have overridden both of them with a class derived from UserCreationForm which is okay for the create view but doesn't work for the update view .
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from django.contrib.auth.models import User
class EmailRequiredMixin(object):
def __init__(self, *args, **kwargs):
super(EmailRequiredMixin, self).__init__(*args, **kwargs)
# make user email field required
self.fields['email'].required = True
class MyUserCreationForm(EmailRequiredMixin, UserCreationForm):
pass
class MyUserChangeForm(EmailRequiredMixin, UserChangeForm):
pass
class EmailRequiredUserAdmin(UserAdmin):
form = MyUserChangeForm
add_form = MyUserCreationForm
add_fieldsets = ((None, {
'fields': ('username', 'email', 'password1', 'password2'),
'classes': ('wide',)
}),)
admin.site.unregister(User)
admin.site.register(User, EmailRequiredUserAdmin)
This should do the trick.

Related

Any change to the customized Django User Model is completely ignored in the Admin

I customized the User model by extending AbstractUser, not AbstractBaseUser, because I don't need to remove the username, I just need to authenticate users by email and I still want to use the authentication system that comes with Django. Therefore I just defined the email address as the username and I extended AbstractUser before any migration.
But the Admin doesn't recognize this and completely ignores what I specify in admin.py, except the register instruction. Here's the content of my admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from .models import User as CustomUser
from .forms import UserChangeForm, UserCreationForm
# from django.utils.translation import ugettext_lazy as _
# I obviously tried by extending UserAdmin too, no result
class CustomUserAdmin(admin.ModelAdmin):
add_form = UserCreationForm
form = UserChangeForm
model = CustomUser
fields = ('email')
# commenting or uncommenting the following doesn't change anything
"""
list_display = ('email', 'is_staff', 'is_active',)
list_filter = ('email', 'is_staff', 'is_active',)
exclude = ('first_name',)
fieldsets = (
(None, {'fields': ('email', 'password')}),
('Permissions', {'fields': ('is_staff', 'is_active')}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'password1', 'password2', 'is_staff', 'is_active')}
),
)
search_fields = ('email',)
ordering = ('email',)
"""
admin.site.register(CustomUser, BaseUserAdmin)
I tried everything and nothing works. I can't add the email field in the Add User form, and I can't remove the first and last_name from http://127.0.0.1:8000/admin/users/user/
It seems that the Django built-in Admin ignores any change made in any class extending AbstractUser
Obviously everything is correct in settings.py:
INSTALLED_APPS = [
'users',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
# this is necessary for the custom user model and must to be taken into account before any migration will occur.
AUTH_USER_MODEL = 'users.User'
models.py:
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.contrib.auth.base_user import BaseUserManager
from django.utils.translation import ugettext_lazy as _
class CustomUserManager(BaseUserManager):
"""
Custom user model manager where email is the unique identifier for
authentication instead of username.
Between the username field (email) and password, you must put all
the required fields. extra_fields must contain all the optional
fields.
"""
def create_user(self, email, username, password, **extra_fields):
"""
Create and save a User with the given email and password.
"""
if not email:
raise ValueError(_('The Email must be set'))
if not username:
raise ValueError("Users must have an Username")
user = self.model(
email = self.normalize_email(email),
username=username,
)
user.set_password(password)
user.save()
return user
def create_superuser(self, email, username, password):
user = self.create_user(
email=email,
username=username,
password=password
)
user.is_admin = True
user.is_staff=True
user.is_superuser=True
user.save(using=self._db)
return user
"""
This model behaves identically to the default user model, but you’ll be able to customize it in the future if the need arises. This is the recommended behavior
"""
class User(AbstractUser):
email = models.EmailField(_('email address'), unique=True)
USERNAME_FIELD = 'email'
"""
The REQUIRED_FIELDS = ['username'] must be present, otherwise the following error will arise:
TypeError: create_superuser() missing 1 required positional argument: 'username'
"""
REQUIRED_FIELDS = ['username']
objects = CustomUserManager()
def getEmailField(self):
return self.email
def __str__(self):
return self.email
forms.py:
class UserChangeForm(auth_forms.UserChangeForm):
class Meta(auth_forms.UserChangeForm.Meta):
model = CustomUser
fields = '__all__'
class UserCreationForm(auth_forms.UserCreationForm):
class Meta(auth_forms.UserCreationForm.Meta):
model = CustomUser
fields = '__all__'
Obviously I tried with several things in fields = 'whatever' but any and every change I try, is happily ignored, the Admin will always display the data and the forms in the same way as the default.
My views.py...........
from django.shortcuts import render, redirect
from django.urls import reverse
from django.contrib.auth import login
from users.forms import CustomUserCreationForm
from django.template import RequestContext
def dashboard(request):
return render(request, "users/dashboard.html")
def register(request):
if request.method == "GET":
return render(
request, "users/register.html",
{"form": CustomUserCreationForm}
)
elif request.method == "POST":
form = CustomUserCreationForm(request.POST)
if form.is_valid():
# the form.cleaned_data must be used after calling
# the form.is_valid method.
# if(email_matches(form.cleaned_data["email"], form.cleaned_data["email2"])):
user = form.save()
login(request, user)
return redirect(reverse("dashboard"))
context = {
"form": form,
}
return render(request, "users/register.html", context)
def home_page(request):
return render(request, "home_page.html")
I read somewhere that it is not possible to override anything from the AbstractUser class and to do this, I must extend the AbstractBaseUser class directly.
Is this also valid with the Admin forms? After all, I'm not overriding anything, I just trying to display the email address in the Admin Add User form.
But supposing I can't really customize anything coming from the AbstractUser. Why are things like that? This is not a today problem. I have been trying to understand this behavior for over a week now and neither the documentation nor the internet is of any help.
It seems you are using the wrong admin:
admin.site.register(CustomUser, CustomUserAdmin)
Instead of extending admin.ModelAdmin for CustomUserAdmin, you should do something like this-
class CustomUserAdmin(BaseUserAdmin):
...
...
And then please correct your model registration line like this-
admin.site.register(CustomUser, CustomUserAdmin)
P.S. Do not forget to uncomment the lines you have commented.

Problem adding user's email in Django admin

I created a custom model extending AbstractUser in order to authenticate users by email instad of by username (but not wanting to drop the username, because it will also be used).
This was the first thing I made before running the first migration, everything worked correctly except in the Django admin, when I create a new user, I want these fields to be filled
username
email
password
And the admin only ask me for the username and password. How could I add the email too? Here's my codes
models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.contrib.auth.base_user import BaseUserManager
from django.utils.translation import ugettext_lazy as _
class CustomUserManager(BaseUserManager):
"""
Custom user model manager where email is the unique identifier for
authentication instead of username.
"""
def create_user(self, email, password, **extra_fields):
"""
Create and save a User with the given email and password.
"""
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()
return user
def create_superuser(self, email, password, **extra_fields):
"""
Create and save a SuperUser with the given email and password.
"""
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
extra_fields.setdefault('is_active', True)
if extra_fields.get('is_staff') is not True:
raise ValueError(_('Superuser must have is_staff=True.'))
if extra_fields.get('is_superuser') is not True:
raise ValueError(_('Superuser must have is_superuser=True.'))
return self.create_user(email, password, **extra_fields)
class User(AbstractUser):
email = models.EmailField(_('email address'), unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = CustomUserManager()
def __str__(self):
return self.email
admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User as CustomUser
class CustomUserAdmin(UserAdmin):
model = CustomUser
list_display = ('email', 'is_staff', 'is_active',)
list_filter = ('eamil', 'is_staff', 'is_active',)
fieldsets = (
(None, {'fields': ('email', 'password')}),
('Permissions', {'fields': ('is_staff', 'is_active')}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'password1', 'password2', 'is_staff', 'is_active')}
),
)
search_fields = ('email',)
ordering = ('email',)
admin.site.register(CustomUser, UserAdmin)
What I have to do? It seems that everything is correct, but the required email field is missing. Notice that this only happens when I use the Add User feature in the admin. When I create a superuser or when I login to the admin, everything is working as expected.
UPDATE:
Here's the content of my forms.py
from django.contrib.auth.forms import UserCreationForm
# the user model was customized it should be invoked
from django.contrib.auth import get_user_model
from django.contrib.auth import forms as auth_forms
from .models import User as CustomUser
class UserChangeForm(auth_forms.UserChangeForm):
class Meta(auth_forms.UserChangeForm.Meta):
model = CustomUser
class UserCreationForm(auth_forms.UserCreationForm):
class Meta(auth_forms.UserCreationForm.Meta):
model = CustomUser
fields = ("username", "email")
class CustomUserCreationForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
# the user model was customized it should be invoked
model = get_user_model()
fields = UserCreationForm.Meta.fields + ("email",)
And here the content (updated) of my admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User as CustomUser
from .forms import UserChangeForm, UserCreationForm
class CustomUserAdmin(UserAdmin):
model = CustomUser
list_display = ('email', 'username', 'is_staff', 'is_active',)
list_filter = ('eamil', 'username', 'is_staff', 'is_active',)
form = UserChangeForm
add_form = UserCreationForm
fieldsets = (
(None, {'fields': ('email', 'username', 'password')}),
('Permissions', {'fields': ('is_staff', 'is_active')}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'username', 'password1', 'password2', 'is_staff', 'is_active')}
),
)
search_fields = ('email',)
ordering = ('email',)
admin.site.register(CustomUser, UserAdmin)
But I still have the same problem, I still can't add an email field in /admin/users/user/add/
The UserAdmin class has the attributes form and add_form which point to form classes to be used to edit and create the user respectively. You need to override these form classes and also set these attributes yourself as these two forms are created for the default user model.
Firstly define these custom forms:
from django.contrib.auth import forms as auth_forms
from .models import User as CustomUser
class UserChangeForm(auth_forms.UserChangeForm):
class Meta(auth_forms.UserChangeForm.Meta):
model = CustomUser
class UserCreationForm(auth_forms.UserCreationForm):
class Meta(auth_forms.UserCreationForm.Meta):
model = CustomUser
fields = ("username", "email")
Next set these in your CustomUserAdmin:
from .forms import UserChangeForm, UserCreationForm
class CustomUserAdmin(UserAdmin):
# Your other attributes here
form = UserChangeForm
add_form = UserCreationForm
In your code, I see
admin.site.register(CustomUser, UserAdmin)
but you have to register through your CustomUserAdmin.
Change your admin.py code to
admin.site.register(CustomUser, CustomUserAdmin)

Custom Fields in Django not displayed in admin panel

I am using UserCreationForm for registration of the user. Added one address filed as mandatory in the code. however whenever the user is registering on the html page the address input is present.In the admin panel for users the address is not present.
form.py
class RegistrationForm(UserCreationForm):
email=forms.EmailField(required=True)
address=forms.CharField(max_length=250,required=True)
class Meta:
model=User
fields=(
'username',
'first_name',
'last_name',
'email',
'address',
'password1',
'password2'
)
def save(self,commit=True):
user=super(RegistrationForm,self).save(commit=False)
user.first_name=self.cleaned_data['first_name']
user.last_name=self.cleaned_data['last_name']
user.email=self.cleaned_data['email']
if commit:
user.save()
return user
views.py
def register(request):
print("inside views")
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
form.save()
return redirect('./login.html')
else:
form = RegistrationForm()
args = {'form': form}
return render(request,'./reg_form.html',args)
else:
form = RegistrationForm()
args = {'form': form}
return render(request,'./reg_form.html',args)
In the user model all the fields are seen in the admin page,just address is not present. However while registering the user, address input is accepted.
I think you need to add this code in your models.py this field you input is required, they are added by default.
Email & Password & last_name & first_name are required by default.
REQUIRED_FIELDS = ['Address', 'SomethingElse']
and then in your admin.py
admin.py
from django.contrib import admin
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from .forms import UserAdminCreationForm, UserAdminChangeForm
from .models import User
name of your class and your Object you want to add in admin page.
class UserAdmin(BaseUserAdmin):
# The forms to add and change user instances
form = UserAdminChangeForm
add_form = UserAdminCreationForm
# The fields to be used in displaying the User model.
# These override the definitions on the base UserAdmin
# that reference specific fields on auth.User.
list_display = ('email', 'admin', 'first_name', 'address')
list_filter = ('admin', 'staff', 'active')
fieldsets = (
(None, {'fields': ('email', 'password')}),
('Personal info', {'fields': ('first_name', 'address')}),
('Permissions', {'fields': ('admin', 'staff', 'active')}),
)
# 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', 'first_name', 'address', 'active', 'staff', 'admin')}
),
)
search_fields = ('email')
ordering = ('email',)
filter_horizontal = ()
admin.site.register(User, UserAdmin)
# Remove Group Model from admin. We're not using it.
admin.site.unregister(Group)
finally, you can check how I add address field into my project
by this link in Viva-ecommerce-models.py and Follow the Class Address

Force User to Provide Profile Before Saving in Django Admin

Am trying to force a user to provide a profile before they are saved from the django admin.
here is my profile model
class UserProfile(models.Model):
user=models.OneToOneField(AUTH_USER_MODEL,related_name='profile',primary_key=True)
#other fields
def get_user_info(user):
return UserProfile.objects.get(user=user)
#receiver(post_save, sender=AUTH_USER_MODEL)
def create_profile_for_new_user(sender, created, instance, **kwargs):
if created:
profile = UserProfile(user=instance)
profile.save()
Am using authtools so the user profile sender is AUTH_USER_MODEL.
If I add a new user from within admin,they are saved even if they have not provided or filled in the profile.
I want to prevent them from being saved until they fill in the profile fields.
Any insights on how to do this?
Here is my admin.py
from django import forms
from django.contrib.auth import get_user_model
from django.contrib.auth.forms import PasswordResetForm
from django.utils.crypto import get_random_string
from authtools.admin import NamedUserAdmin
from authtools.forms import UserCreationForm
User = get_user_model()
class UserCreationForm(UserCreationForm):
"""
A UserCreationForm with optional password inputs.
"""
def __init__(self, *args, **kwargs):
super(UserCreationForm, self).__init__(*args, **kwargs)
self.fields['password1'].required = False
self.fields['password2'].required = False
# If one field gets autocompleted but not the other, our 'neither
# password or both password' validation will be triggered.
self.fields['password1'].widget.attrs['autocomplete'] = 'off'
self.fields['password2'].widget.attrs['autocomplete'] = 'off'
def clean_password2(self):
password1 = self.cleaned_data.get("password1")
password2 = super(UserCreationForm, self).clean_password2()
if bool(password1) ^ bool(password2):
raise forms.ValidationError("Fill out both fields")
return password2
class UserAdmin(NamedUserAdmin):
"""
A UserAdmin that sends a password-reset email when creating a new user,
unless a password was entered.
"""
inlines = [ UserProfileInline, ]
add_form = UserCreationForm
add_fieldsets = (
(None, {
'description': (
"Enter the new user's name and email address and click save."
" The user will be emailed a link allowing them to login to"
" the site and set their password."
),
'fields': ('email', 'name',),
}),
('Password', {
'description': "Optionally, you may set the user's password here.",
'fields': ('password1', 'password2'),
'classes': ('collapse', 'collapse-closed'),
}),
)
def save_model(self, request, obj, form, change):
if not change and not obj.has_usable_password():
# Django's PasswordResetForm won't let us reset an unusable
# password. We set it above super() so we don't have to save twice.
obj.set_password(get_random_string())
reset_password = True
else:
reset_password = True
super(UserAdmin, self).save_model(request, obj, form, change)
if reset_password:
reset_form = PasswordResetForm({'email': obj.email})
assert reset_form.is_valid()
reset_form.save(
subject_template_name='registration/account_creation_subject.txt',
email_template_name='registration/account_creation_email.html',
)
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
The best way to do this is to create a custom middleware and check, if the logged in user has filled out the profile. If not, the middleware would redirect the user to a view, where he can fill out the profile.
Example:
class ProfileMiddleware(MiddlewareMixin):
def __init__(self, get_response=None):
self.get_response = get_response
def process_view(self, request, view_func, view_args, view_kwargs):
if not request.user.profile:
return HttpResponseRedirect(reverse('create-profile'))
return None
Haven't tested the code, but it should get you started. It's the cleanest way of doing this and it will also force the user to fill out the profile, otherwise he cannot open a page on the website.
Hope this helps.

How to edit only some fields in django admin from an extend user model?

After looking for the best way to extend user, I followed the way described here (https://docs.djangoproject.com/en/dev/topics/auth/customizing/#extending-the-existing-user-model). Now I am looking for a way to let the user edit some of their fields from User (eg. first_name, last_name and email) and from the new class extended. It would be nice if this could be done on the same screen, but I am not sure if this is possible (I don't want to let the permissions editable for non superuser).
So, I have first tried this code in admin, but it not works:
#admin.py
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
class MyUserAdmin(UserAdmin):
def get_fieldsets(self, request, obj=None):
if request.user.is_superuser:
fieldsets = super(MyUserAdmin, self).get_fieldsets(request, obj)
else:
fieldsets = (
(None, {
'fields': ('username', 'first_name', 'last_name', 'email', 'password', 'last_login', 'date_joined',)
}),)
return fieldsets
# If not superuser, do not show some fields
def get_readonly_fields(self, request, obj=None):
ro_fields = super(MyUserAdmin, self).get_readonly_fields(request, obj)
if not request.user.is_superuser:
ro_fields = list(ro_fields) + ['username', 'last_login', 'date_joined',]
return ro_fields
admin.site.unregister(User)
admin.site.register(User, MyUserAdmin)
Here is a piece of my model:
class Investigator(models.Model):
user = models.OneToOneField(User, verbose_name=_('User'))
force_password_change = models.BooleanField(_('Force password change'), default=True)
...
I got no error, but the changes are not saved. What I have to do to let the users edit some specific fields?
You need set up permission for the user to edit User model