Django 3: Setting user email to be unique, minimal viable solution - django

I'm writing a simple app, and I'm totally happy with Django User model, except that I want a user email to be unique and obligatory field.
I have developed a solution, but I'm wondering if I'm missing something.
a) If you think that something is missing in the solution below please let me know
b) If you think that you have a better solution please share
This is what I did
Created a custom user model
# customers/models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
email = models.EmailField(verbose_name='Email', unique=True, blank=False, null=False)
EMAIL_FIELD = 'email'
REQUIRED_FIELDS = ['email']
Added AUTH_USER_MODEL = 'customers.User' to settings.py
Changed a reference to User model:
User = get_user_model()
Modified admin.py in order to be able to add users using Django admin.
# customers/admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth import get_user_model
User = get_user_model()
class CustomUserAdmin(UserAdmin):
list_display = ('username', 'email', 'is_staff')
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('username', 'email', 'password1', 'password2'),
}),
)
admin.site.register(User, CustomUserAdmin)

I hope this one you are looking for
Use the below code snippets in any of your models.py
models.py
from django.contrib.auth.models import User
User._meta.get_field('email')._unique = True
django version : 3.0.2
Reference : Django auth.user with unique email
https://stackoverflow.com/a/64075027/6651010

Related

User password is not hashed when creating through Django admin panel

I have inherited the user class to make custom authentication. I do not get password hashing this way. It just stores as plain text in MySQL database. I have created staff through admin panel and unable to login as staff. Furthermore I have also created auth API endpoints using DRF and Djoser and am unable to login with the user profiles created through Django admin panel.
Here is my code.
models.py
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
pass
admin.py
from .models import User
class UserAdmin(admin.ModelAdmin):
pass
admin.site.register(User, UserAdmin)
I have seen old replies in Stack Overflow suggesting changing the parent class to django.contrib.auth.admin.UserAdmin . When I try this the add user template form only has 3 fields. Username, Password and Password Confirmation.
admin.py
from django.contrib.auth.admin import UserAdmin as DefaultUserAdmin
from .models import User
class UserAdmin(DefaultUserAdmin):
pass
admin.site.register(User, UserAdmin)
How do I solve this problem.
I wrote a custom UserAdmin as well so i guess i can help you a little bit with that
try this one:
#admin.register(User)
class UserAdmin(UserAdmin):
"""Define admin model for custom User model with no email field."""
fieldsets = (
(None, {'fields': ('email', 'password')}),
(_('Personal info'), {'fields': ('first_name', 'last_name')}),
(_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser')}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'password1', 'password2'),
}),
)
list_display = ('email', 'is_staff')
search_fields = ('email',)
ordering = ('email',)
And i also think that there`s problems in your models.py bcs you dont have userManager
UserManager uses when you`re creating user so i guess problem in that

Can the User model be overridden to make the email field required?

For my application, the email field in the Django User model should be required, but by default it's optional.
I searched this question but most answers I found were talking about making the email field required in the customised UserCreationForm for example:
class CustomUserForm(UserCreationForm):
"""Provide a view for creating users with only the requisite fields."""
class Meta:
model = User
# Note that password is taken care of for us by auth's UserCreationForm.
fields = ('username', 'email')
def __init__(self, *args, **kwargs):
super(CustomUserForm, self).__init__(*args, **kwargs)
self.fields['email'].required = True
but I want to make the email mandatory in the User model as well because I was thinking maybe ModelForm are a frontend thing and the required condition maybe be turned off by the user by using the chrome dev tools, am I wrong? Will making the email required in ModelForm is same as making it required in the user model as well, or can I override the user model to make the email required?
Maybe ModelForm are a frontend thing and the required condition maybe be turned off by the user by using the chrome dev tools, am I wrong?
You are correct that a user can use tools to make an input item not required, in fact you can often alter the DOM and thus alter the HTML form, or even more simply make use of a tool like curl to make a POST request where you can post arbitrary data.
But a Form (and by extent an ModelForm) does not only render the form in HTML, it is also used to validate the data. This thus means that if you use a view with:
def my_view(request):
if request.method == 'POST':
form = CustomUserForm(request.POST, request.FILES)
if form.is_valid():
# …
# …
# …
then the form.is_valid() will return False if the user did not fill in an email address. A form is thus used to validate, clean data, store data in a model, and render a HTML form.
from django.contrib.auth.models import User
from django.db import models
User.add_to_class('email', models.EmailField(blank=False))
in your settings file
AUTH_USER_MODEL = 'yourapp.User'
in your models
from django.db import models
from django.contrib.auth.models import AbstractUser, Permission, Group
from django.utils.translation import gettext_lazy as _
class User(AbstractUser):
groups = models.ManyToManyField(
Group,
verbose_name=_('groups'),
blank=True,
help_text=_(
'The groups this user belongs to. A user will get all permissions '
'granted to each of their groups.'
),
related_name="user_groups",
related_query_name="user",
)
user_permissions = models.ManyToManyField(
Permission,
verbose_name=_('user permissions'),
blank=True,
help_text=_('Specific permissions for this user.'),
related_name="user_permissions",
related_query_name="user",
)
EMAIL_FIELD = 'email'
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['email']
# here you can add new fields and attributes
class Meta:
permissions = (
# here you can add pressiion
)
in your admin
from custodia.models import User
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.utils.translation import gettext_lazy as _
#admin.register(User)
class UserAdmin(UserAdmin):
list_display = ('email', 'first_name', 'last_name', 'customer', 'is_staff', 'is_active')
fieldsets = (
(None, {'fields': ('username', 'password')}),
(_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
(_('Aditional info'), {'fields': ('your_new_fields',)}),
(_('Permissions'), {
'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions'),
}),
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
)
list_filter = ('is_staff', 'is_superuser', 'is_active', 'groups', 'customer')

Adding extra field in admin for custom user in Django 1.11

I have a custom user model which is subclassed by AbstractUser with an added custom field.
# model.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
ADM = 'admin'
MEMBER = 'member'
ROLE_CH = ((ADM, 'Administrator'), (MEMBER, 'Member'))
role = models.CharField(max_length=20, choices=ROLE_CH, blank=True)
This model is also registered as the default auth model in settings.py
# settings.py
AUTH_USER_MODEL = "main.CustomUser"
Then in admin.py as per the documentation, I create a custom form which extends UserCreationForm and then register it to the custom user.
# admin.py
from django.contrib import admin
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.admin import UserAdmin
from .models import CustomUser
class CustomUserCreationForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = CustomUser
fields = UserCreationForm.Meta.fields + ('role',)
class CustomUserAdmin(UserAdmin):
add_form = CustomUserCreationForm
admin.site.register(CustomUser, CustomUserAdmin)
However, it does not work as expected. The Add User form remains the default one i.e. only username, password and password confirmation fields are present. The role field does not appear.
Use form attribute instead of add_form:
class CustomUserAdmin(ModelAdmin):
form = CustomUserCreationForm
admin.site.register(CustomUser, CustomUserAdmin)
UPD
First answer is correct only for ModelAdmin base class, since it doesn't have add_form attribute.
For UserAdmin you should update add_fieldsets attribute:
class CustomUserAdmin(UserAdmin):
add_fieldsets = UserAdmin.add_fieldsets + (
(None, {
'fields': ('role',),
}),
)
admin.site.register(CustomUser, CustomUserAdmin)
In this case you dont even required add_form.

Incorrect work redefined form in django admins

I've created custom model Profile and linked it to the User model which works fine. But, now I want to create custom UserCreateForm in Django admin. I redefined it and added necessary fields, but after that still shows, all fields from profile model, ex: phone, home_address. I need fields displayed as : 'first_name', 'last_name', 'username', 'password1', 'password2' in the UserCreateForm. What have I done wrong?
from django.contrib import admin
from django.contrib.auth.models import User
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserCreationForm
from .models import Profile
class NewUserCreateForm(UserCreationForm):
class Meta:
fields = ('username', 'first_name', 'last_name',)
class ProfileInline(admin.TabularInline):
model = Profile
class UserAdmin(UserAdmin):
add_form = NewUserCreateForm
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('first_name', 'last_name', 'username','password1', 'password2', ),
}),
)
inlines = [ProfileInline]
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
Firstly your models.py file will be :
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Profile(models.Model):
user_id = models.OneToOneField(User, on_delete=models.CASCADE)
phone = models.CharField(max_length=100)
address = models.CharField(max_length=256)
...
Then your admin.py file.
from.models import Profile
# Register your models here.
class ProfileAdmin(admin.ModelAdmin):
model = Profile
exclude = ['phone',] # Or whatever you don't want to display.
admin.site.register(Profile, ProfileAdmin)
For extended implementation please add or remove based on your needs.
Hope it helps.

Django 1.5 custom User model error. "Manager isn't available; User has been swapped"

I extend the django user model as described in the dev doc. I wan't to keep most of the original User model features so I extend the AbstractUser class. I've defined in settings.py:
AUTH_USER_MODEL = 'myapp.CustomUser'
My user class:
class CustomUser(AbstractUser):
custom_field = models.ForeignKey('OtherModel')
objects = UserManager()
Everything seems to work fine but when I try to make it managed by the admin site:
admin.site.register(CustomUser, UserAdmin)
I get this error on the admin CustomUser creation page (after validation of the password confirmation form):
AttributeError: Manager isn't available; User has been swapped for 'myapp.CustomUser'
The point is that I need this model managed by the admin site in order to have the same creation process as with the original User model (two step process with password validation).
You need only change form for adding user(overwrite clean_username and change User on get_user_model()
Full working example(If you inherited from AbstractUser)
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
class MyUserChangeForm(UserChangeForm):
class Meta:
model = get_user_model()
class MyUserCreationForm(UserCreationForm):
class Meta:
model = get_user_model()
def clean_username(self):
username = self.cleaned_data["username"]
try:
get_user_model().objects.get(username=username)
except get_user_model().DoesNotExist:
return username
raise forms.ValidationError(self.error_messages['duplicate_username'])
class MyUserAdmin(UserAdmin):
form = MyUserChangeForm
add_form = MyUserCreationForm
fieldsets = (
(None, {'fields': [('username', 'password'),]}),
(_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
(_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser',
'groups', 'user_permissions')}),
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
)
admin.site.register(MyUser, MyUserAdmin)
I struggled with this error for hours. For me I needed to remove all references to
from django.contrib.auth.models import User
and then replace it with:
from myapp.models import MyUser as User
This is assuming your custom User model is in an app called myapp and you called the model MyUser.
I'm using as User so that I don't have to change where my existing code that makes reference to the the User object from django.contrib.auth.models.
Good luck!
Alan
#aviars
You probably should look at full example in official documentation:
https://docs.djangoproject.com/en/dev/topics/auth/customizing/#a-full-example
There are some uncovered questions (permissions handling, for example), but, at least, it's working.
From Django docs:
You should also define a custom manager for your User model.
Subclass AbstractUser already handle objects = UserManager() for you (this code on github at line 327). You don't need to define it in your model again.
I'm not sure why it come with that error. But below config seem work for me with latest Dev version.
Model.py:
class CustomUser(AbstractUser):
custom_field = models.ForeignKey('OtherModel')
# remove : objects = UserManager()
I've found a solution:
I don't use UserAdmin to register the CustomUser in the admin site, I use a custom ModelAdmin.
class CustomUserAdmin(admin.ModelAdmin):
add_form = CustomUserCreationAdminForm
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('username', 'password1', 'password2')}
),
)
def get_fieldsets(self, request, obj=None):
if not obj:
return self.add_fieldsets
return super(CustomUserAdmin, self).get_fieldsets(request, obj)
def get_form(self, request, obj=None, **kwargs):
defaults = {}
if obj is None:
defaults.update({
'form': self.add_form,
'fields': admin.util.flatten_fieldsets(self.add_fieldsets),
})
defaults.update(kwargs)
return super(CustomUserAdmin, self).get_form(request, obj, **defaults)
Because I want to have a creation form different from the update form, I override the get_form function of the Model Admin as done in the UserAdmin django code. I also have created a custom Creation form for my custom user: CustomUserCreationForm
I received a response in the django-users mailing list that may be better than mine:
Unregister the original User class from the admin site and then register the CustomUser with UserAdmin:
admin.site.unregister(User)
admin.site.register(CustomUser, UserAdmin)
Not tested yet.
EDIT: this last solution doesn't work
Regards
UserAdmin is already registered to manage User, at the end of contrib/auth/admin.py
admin.site.register(User, UserAdmin)
Given you want to register UserAdmin with CustomUser, I think you would first have to unregister the User/UserAdmin pair:
admin.site.unregister(User)
then register your new model. Given the lack of control in the order of module loading, you could create a class derived from UserAdmin and manage your user model with it. This would work everytime, if I'm thinking properly.