Skip to content

Instantly share code, notes, and snippets.

@squio
Last active July 15, 2019 10:08
Show Gist options
  • Save squio/1e5fe410d42622d4a54712daba4dfb77 to your computer and use it in GitHub Desktop.
Save squio/1e5fe410d42622d4a54712daba4dfb77 to your computer and use it in GitHub Desktop.
Add hooks to update Allauth email for a user from the Django contrib.auth panel without altering the contrib.auth.models.User model in any way
""" App administration """
from django import forms
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from django.contrib.auth.decorators import login_required
from django.utils.translation import gettext_lazy as _
from allauth.account.models import EmailAddress
from myapp.models.user import Profile
from myapp.forms import ProfileForm
# Require Allauth login:
# https://django-allauth.readthedocs.io/en/latest/advanced.html#admin
admin.site.login = login_required(admin.site.login)
class AllEmailField(forms.EmailField):
""" Extend email validation to check Allauth tables as well """
def validate(self, value):
super().validate(value)
exists = EmailAddress.objects.filter(email=value).count()
if exists > 0:
raise forms.ValidationError(_("E-mail address is already in use"), 'invalid')
class CustomUserCreationForm(UserCreationForm):
""" User Creation with additional model fields """
# add fields which are not part of Django default admin
# Custom email validator validates email uniqueness, also with allauth table
email = AllEmailField(widget=forms.EmailInput(), required=False)
first_name = forms.CharField(required=False)
last_name = forms.CharField(required=False)
class Meta(UserCreationForm):
model = User
fields = '__all__'
class CustomUserChangeForm(UserChangeForm):
""" User Edit with additional model fields """
# Custom email validator validates email uniqueness, also with allauth table
email = AllEmailField(widget=forms.EmailInput(), required=False)
class Meta(UserCreationForm):
model = User
fields = '__all__'
# Define an inline admin descriptor for Profile model
# which acts a bit like a singleton
class ProfileInline(admin.StackedInline):
""" User Profile form """
model = Profile
form = ProfileAdminForm
can_delete = False
class UserAdmin(BaseUserAdmin):
""" Extend User Admin """
# Override user creation form
# add fields which are not part of Django default admin
add_form = CustomUserCreationForm
add_fieldsets = BaseUserAdmin.add_fieldsets + (
(None, {'fields': ('email', 'first_name', 'last_name')}),
)
# override Edit / change form
form = CustomUserChangeForm
# Add profile inline
inlines = (ProfileInline, )
list_per_page = 20
def save_model(self, request, obj, form, change):
# Keep email address updated with Allauth
# only if email field has changed:
if not 'email' in form.changed_data:
# go ahead with original save action
super().save_model(request, obj, form, change)
else:
# first: check if email already exists in Django Allauth
exists = EmailAddress.objects.filter(email=obj.email).count() > 0
# Is it one of our own addresses?
is_own = obj.pk and EmailAddress.objects.filter(user=obj, email=obj.email).count() > 0
if exists:
if is_own:
# go ahead and make this our primary email address
super().save_model(request, obj, form, change)
email = EmailAddress.objects.filter(user=obj, email=obj.email).get()
email.primary = True
email.save()
return
else:
# this should already have been handled by validation in CustomUserChangeForm
raise forms.ValidationError(_("E-mail address is already in use by another account"), 'invalid')
# If we have an existing user object
if obj.pk:
# obj has the changed email address
# so get original value from DB, if it exists
orig_email = User.objects.get(pk=obj.pk).email
try:
# only if email was set previously (orig_email)
exists = EmailAddress.objects.filter(user=obj, email=orig_email).get()
# update existing email address from user
exists.primary = True
exists.change(request, obj.email, confirm=False)
except EmailAddress.DoesNotExist:
# we should create a new Email Address after the User has been saved
exists = False
else:
exists = False
super().save_model(request, obj, form, change)
# email was not set before in Allauth
# add it as primary email to allauth table
if not exists:
email = EmailAddress.objects.create(user=obj, email=obj.email, primary=True)
# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment