How to add custom user model in admin panel? - django

I created a custom user model extending AbstractBaseUser class. code is below.
class UserModel(AbstractBaseUser):
sys_id = models.AutoField(primary_key=True, blank=True)
name = models.CharField(max_length=127, null=False, blank=False)
email = models.EmailField(max_length=127, unique=True, null=False, blank=False)
mobile = models.CharField(max_length=10, unique=True, null=False, blank=False)
user_type = models.PositiveSmallIntegerField(choices=user_type_choices, null=False, blank=True, help_text="Admin(1)/Institute(2)/Student(3)")
access_valid_start = models.DateTimeField(null=True, blank=True)
access_valid_end = models.DateTimeField(null=True, blank=True)
created_when = models.DateTimeField(null=True, blank=True )
created_by = models.BigIntegerField(null=True, blank=True)
last_updated_when = models.DateTimeField(null=True, blank=True)
last_updated_by = models.BigIntegerField(null=True, blank=True)
notes = models.CharField(max_length=2048, null=True, blank=True)
is_active = models.BooleanField(default=True)
# this field is required to login super user from admin panel
is_staff = models.BooleanField(default=True)
# this field is required to login super user from admin panel
is_superuser = models.BooleanField(default=False)
objects = MyUserManager()
USERNAME_FIELD = "email"
# REQUIRED_FIELDS must contain all required fields on your User model,
# but should not contain the USERNAME_FIELD or password as these fields will always be prompted for.
REQUIRED_FIELDS = ['name', 'mobile', 'user_type']
class Meta:
app_label = "accounts"
db_table = "users"
def __str__(self):
return self.email
def get_full_name(self):
return self.name
def get_short_name(self):
return self.name
# this methods are require to login super user from admin panel
def has_perm(self, perm, obj=None):
return self.is_superuser
# this methods are require to login super user from admin panel
def has_module_perms(self, app_label):
return self.is_superuser
Now in my admin panel only 'groups' is available. I want my custom user mode i.e. UserModel to appear in admin panel so that I can add, edit and delete user from admin user interface.
I tried multiple codes from SO, simplest of them is below
from django.contrib.auth.admin import UserAdmin
from accounts.models import UserModel
from django.contrib import admin
class MyUserAdmin(UserAdmin):
model = UserModel
fieldsets = UserAdmin.fieldsets + (
(None, {'fields': ('mobile',)}),
)
admin.site.register(UserModel, MyUserAdmin)
now I am getting this errors ..
ERRORS:
<class 'accounts.admin.MyUserAdmin'>: (admin.E019) The value of 'filter_horizontal[0]' refers to 'groups', which is not an attribute of 'accounts.UserModel'.
<class 'accounts.admin.MyUserAdmin'>: (admin.E019) The value of 'filter_horizontal[1]' refers to 'user_permissions', which is not an attribute of 'accounts.UserModel'.
<class 'accounts.admin.MyUserAdmin'>: (admin.E033) The value of 'ordering[0]' refers to 'username', which is not an attribute of 'accounts.UserModel'.
<class 'accounts.admin.MyUserAdmin'>: (admin.E108) The value of 'list_display[0]' refers to 'username', which is not a callable, an attribute of 'MyUserAdmin', or an attribute or method on 'accounts.UserModel'.
<class 'accounts.admin.MyUserAdmin'>: (admin.E108) The value of 'list_display[2]' refers to 'first_name', which is not a callable, an attribute of 'MyUserAdmin', or an attribute or method on 'accounts.UserModel'.
<class 'accounts.admin.MyUserAdmin'>: (admin.E108) The value of 'list_display[3]' refers to 'last_name', which is not a callable, an attribute of 'MyUserAdmin', or an attribute or method on 'accounts.UserModel'.
<class 'accounts.admin.MyUserAdmin'>: (admin.E116) The value of 'list_filter[3]' refers to 'groups', which does not refer to a Field.
Please let me know how can I add custom user model successfully to admin UI?

From the Django Documentation
If your custom user model extends django.contrib.auth.models.AbstractUser, you can use Django’s existing django.contrib.auth.admin.UserAdmin class. However, if your user model extends AbstractBaseUser, you’ll need to define a custom ModelAdmin class. It may be possible to subclass the default django.contrib.auth.admin.UserAdmin; however, you’ll need to override any of the definitions that refer to fields on django.contrib.auth.models.AbstractUser that aren’t on your custom user class.
So basically, if you create a new user model from the AbstractBaseUser instead of AbstractUser you need to create your own custom ModelAdmin class, alternatively if you inherit from UserAdmin (which is what you're currently doing) you need to override and handle any differences.

#Anurag Rana your custom UserAdminclass should look like this
from django.contrib.auth.admin import UserAdmin
from accounts.models import UserModel
from django.contrib import admin
class MyUserAdmin(UserAdmin):
model = UserModel
list_display = () # Contain only fields in your `custom-user-model`
list_filter = () # Contain only fields in your `custom-user-model` intended for filtering. Do not include `groups`since you do not have it
search_fields = () # Contain only fields in your `custom-user-model` intended for searching
ordering = () # Contain only fields in your `custom-user-model` intended to ordering
filter_horizontal = () # Leave it empty. You have neither `groups` or `user_permissions`
fieldsets = UserAdmin.fieldsets + (
(None, {'fields': ('mobile',)}),
)
admin.site.register(UserModel, MyUserAdmin)

You are trying to use UserAdmin but you don't have fields which it refers to
groups
permissions
first name
last name
username
You should probably need to check UserAdmin class in depth and see what variables you should override
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')}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('username', 'password1', 'password2'),
}),
)
form = UserChangeForm
add_form = UserCreationForm
change_password_form = AdminPasswordChangeForm
list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff')
list_filter = ('is_staff', 'is_superuser', 'is_active', 'groups')
search_fields = ('username', 'first_name', 'last_name', 'email')
ordering = ('username',)
filter_horizontal = ('groups', 'user_permissions',)
Also errors can give you a hint of where something went wrong

Related

Django3: ManyToMany field causes: Unknown field(s) (username) specified for User. Check fields/fieldsets/exclude attributes of class CustomUserAdmin

I have a custom user model without username, but using email instead.
class User(AbstractUser):
username = None
email = models.EmailField(_("email address"), unique=True)
uuid = models.CharField(max_length=36, default=uuid4, unique=True)
USERNAME_FIELD = "email"
REQUIRED_FIELDS = []
objects = CustomUserManager()
def __str__(self):
return self.email
And a project model that has multiple users:
from user.models import User
class Project(models.Model):
project_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=256, unique=True, blank=False)
created = models.DateTimeField(auto_now_add=True)
description = models.TextField(max_length=10000, default="")
slug = models.SlugField(max_length=256, unique=True)
created_by = CurrentUserField(related_name='creator')
users = models.ManyToManyField(User, related_name='users')
....
My UserAdmin model looks like this:
class CustomUserAdmin(UserAdmin):
list_display = ("email", "is_staff", "is_superuser")
readonly_fields = ("last_login", "date_joined", "uuid")
ordering = ("email",)
fieldsets = (
(
"Fields",
{
"fields": (
"email",
"uuid",
"date_joined",
"last_login",
"is_active",
"is_staff",
"is_superuser",
"groups",
"user_permissions",
"password",
)
},
),
)
admin.site.register(User, CustomUserAdmin)
However, when I go to admin view to a project instance and try to add user I get:
FieldError at /admin/user/user/add/
Unknown field(s) (username) specified for User. Check fields/fieldsets/exclude attributes of class CustomUserAdmin.
Request Method: GET
Request URL: http://localhost:8000/admin/user/user/add/?_to_field=id&_popup=1
Django Version: 3.2.11
Exception Type: FieldError
Exception Value:
Unknown field(s) (username) specified for User. Check fields/fieldsets/exclude attributes of class CustomUserAdmin.
I added my user model as a default model in settings, so I don't know where the username field here comes from.
The username field is still part of the search_fields and the add_fieldsets. You thus should remove the username field there:
class CustomUserAdmin(UserAdmin):
list_display = ('email', 'is_staff', 'is_superuser')
readonly_fields = ('last_login', 'date_joined', 'uuid')
ordering = ('email',)
search_fields = ('first_name', 'last_name', 'email') # 🖘 no username
fieldsets = (
(
'Fields',
{
'fields': (
'email',
'uuid',
'date_joined',
'last_login',
'is_active',
'is_staff',
'is_superuser',
'groups',
'user_permissions',
'password',
)
},
),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'password1', 'password2'),
# 🖞 without username
}),
)
admin.site.register(User, CustomUserAdmin)

how to post data to table having foreignkey relation with user model using django rest api

i would like to add data to table which having a foreignkey relatonship with user model through django rest api.
models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
# username = None
email = models.EmailField(verbose_name='email',max_length=50,unique=True)
phone = models.CharField(max_length=17,blank=True)
REQUIRED_FIELDS = [
'first_name',
'last_name',
'phone',
'username',
]
USERNAME_FIELD = 'email'
def get_username(self):
return self.email
class UserInfo(models.Model):
user = models.ForeignKey(User,on_delete=models.CASCADE)
address = models.CharField(max_length=50)
zipcode = models.CharField(max_length=20)
medication = models.CharField(max_length=25)
def __str__(self):
return str(self.user)
serializers.py
from djoser.serializers import UserCreateSerializer
class UserCreateSerializerCustom(UserCreateSerializer):
class Meta(UserCreateSerializer.Meta,):
model = User
fields = (
'id',
'email',
'username',
'password',
'first_name',
'last_name',
'phone',
)
## User Additional Info Serializers
class UserAdditionalSerializers(serializers.ModelSerializer):
user = UserCreateSerializerCustom()
class Meta:
model = UserInfo
fields = (
'user',
'address',
'zipcode',
'medication',
)
views.py
class UserAdditionalView(generics.ListCreateAPIView):
queryset = UserInfo.objects.all()
serializer_class = UserAdditionalSerializers
how will i send user instance with the data....??
i'm using token authentication for the api. Which is the best way to achieve this..??

Add foreign key field and choices charfield to Django user creation form

I would like to modify Django UserCreationForm so that it would support creating my custom user. There are required fields company and role which both should offer some kind of selection to pick the correct choice (there will be only 3 roles but there can be hundreds of companies).
I believe I need to extend UserCreationForm and modify UserAdmin. However, I have followed several different examples but so far in vain. Below is the model. How can I command Django to add the extra fields to the user creation form?
ROLE_CHOICES = [
('role1', 'Role 1'),
('role1', 'Role 2'),
('janitor', 'Janitor'),
]
class Company(models.Model):
created = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=100)
class Meta:
ordering = ('created',)
db_table = "company"
def __str__(self):
return self.name
class CustomUser(AbstractUser):
created = models.DateTimeField(auto_now_add=True)
username = models.CharField(max_length=100, unique=True)
email = models.EmailField(max_length=200, unique=True)
company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name='%(class)s_company')
role = models.CharField(
max_length=100,
choices=ROLE_CHOICES,
default='janitor',
)
phone_number = models.CharField(null=True, blank=True, max_length=20)
class Meta:
ordering = ('created',)
db_table = "custom_user"
def __str__(self):
return self.username
You don't have to extend UserCreationForm. just use this:
forms.py
from django import forms
from .models import CustomUser
class UserRegistrationForm(forms.ModelForm): # you can name it anything
class Meta:
model = CustomUser
fields = ('username', 'email', 'company',....) # add the fields here you want in form just not created. it's auto fill
Use this form.
If you want admin. Write this in admins.py
from .models import CustomUser
class CustomUserAdmin(admin.ModelAdmin):
list_display = ('username', 'email', 'company'...) # add fields as you want
admin.site.register(CustomUser, CustomUserAdmin)
Hope this help. If not, please comment.

How to Modify Emailfield in AbstractUser Model so that Email is Required

How to set emailfield to required when extending AbstractUser?
I was wondering how to override the AbstractUser's emailfield model to set it as required. Currently my form list the email field but does not set as required. By default shouldn't all models be required?
From the documentation it shows an example of how to set a field to required when creating a super user but I am not sure how to complete the task from the AbstractUser model without using AbstractBaseUser.
model.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
display_name = models.CharField(max_length=50, unique=True)
bio = models.CharField(max_length=500, blank=True, null=True)
authorization_code = models.CharField(max_length=20)
forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser
class CustomUserCreationForm(UserCreationForm):
class Meta (UserCreationForm):
model = CustomUser
fields = ('username', 'email', 'display_name', 'authorization_code')
class CustomUserChangeForm(UserChangeForm):
class Meta:
model = CustomUser
fields = ('username', 'email', 'bio')
admin.py
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
from .forms import CustomUserCreationForm, CustomUserChangeForm
from .models import CustomUser
class CustomUserAdmin(UserAdmin):
add_form = CustomUserCreationForm
form = CustomUserChangeForm
model = CustomUser
list_display = ['email', 'username', 'display_name', 'bio']
list_display_links = ('username', 'display_name')
fieldsets = (
(None, {'fields': ('username', 'display_name', 'email', 'bio', 'password')}),
('Permissions', {'fields': ('is_staff', 'is_active')}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('username', 'display_name', 'email', 'bio', 'password1', 'password2', 'is_staff', 'is_active')}
),
)
admin.site.register(CustomUser, CustomUserAdmin)
Solution:
# It was as simple as updating my CustomUser model as shown:
class CustomUser(AbstractUser):
display_name = models.CharField(max_length=50, unique=True)
bio = models.CharField(max_length=500, blank=True, null=True)
authorization_code = models.CharField(max_length=20)
email = models.EmailField(max_length=50, unique=True)
You can do it by overriding email in AbstractBaseUser.
from django.contrib.auth.models import AbstractBaseUser, UserManager
class User(AbstractBaseUser):
email = models.EmailField(_('email address'), blank=False)
objects = UserManager()

Django rest framework extended user profile

I recently discovered DRF and I'm lost with the quantity of views, viewsets and other possibilities.
I have a Python3/Django 1.8 application with an extended user profile:
from django.db import models
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
class Profile(models.Model):
GENDER = (
('male', _('MALE')),
('female', _('FEMALE')),
)
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.CharField(default='', max_length=500, null=True, blank=True)
gender = models.CharField(max_length=10, choices=GENDER, null=True, blank=True)
city = models.CharField(default='', max_length=30, null=True, blank=True)
country = models.CharField(default='', max_length=30, null=True, blank=True)
I would like to allow external mobile applications connected with oauth2/token Bearer to get the current connected user's profile through the api and editing it using those routes:
GET or PUT /api/profile
GET or PUT /api/user
My first intention was using only one route for manipulate both models (through /api/profile) but I failed and I'm not sure if it's a good practice to mix two models behind one route.
I tried lot of things. My last attempt was to get the profile like this:
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'username', 'password', 'email', 'groups')
password = serializers.CharField(write_only=True)
class UserViewSet(viewsets.ModelViewSet):
#list_route(methods=['get', 'post'], permission_classes=[permissions.IsAuthenticated])
def profile(self, request):
u = User.objects.filter(pk=request.user.pk)[0]
p = Profile.objects.filter(user=u)[0]
return Response({"id": u.id, "first_name": u.first_name, "last_name": u.last_name, "email": u.email,
"city": p.city, "country": p.country, "bio": p.bio})
permission_classes = [permissions.IsAuthenticated]
queryset = User.objects.all()
serializer_class = UserSerializer
router = routers.DefaultRouter()
router.register(r'users', UserViewSet)
Problem is: I'm failed trying to implement the same thing for PUT requests. Furthermore I would like to do the security and defensive coding part on the API side and in this situation I don't even using the serializers.
Could you guys help me to find the right thing to do? Do you have any tips, suggestions?
Cheers
I think this is what you want:
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ('bio', 'gender', 'city', 'country')
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer()
class Meta:
model = User
fields = ('url', 'username', 'password', 'email', 'groups', 'profile')
Or if you want it flat:
class UserSerializer(serializers.ModelSerializer):
bio = serializers.CharField(source='profile.bio')
gender = serializers.CharField(source='profile.gender')
#same for the others
class Meta:
model = User
fields = ('url', 'username', 'password', 'email', 'groups', 'bio', 'gender')
I didn't test it, but should be close to what you want, or close to it at least.