I am having difficulty creating custom User fields using the Cookiecutter Django framework. I have changed the cookiecutter template significantly - removing django-allauth but a lot of the structure remains the same.
If I wanted to add another field to the User model (for example, "department" - the users are employees), where would I add it?
I figured I could add a department variable to users/models.py but it doesn't seem to work. When I login to the admin site, I don't see a department field when I add a user. Similarly, I don't see a name field in the admin site - I only see First Name, Last Name, and Email Address.
# users/models.py
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, absolute_import
from django.contrib.auth.models import AbstractUser
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
#python_2_unicode_compatible
class User(AbstractUser):
# First Name and Last Name do not cover name patterns
# around the globe.
name = models.CharField(blank=True, max_length=255)
department = models.CharField(blank=True, max_length=5)
def __str__(self):
return self.username
def get_absolute_url(self):
return reverse('users:detail', kwargs={'username': self.username})
The admin file:
# users/admin.py
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from django import forms
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as AuthUserAdmin
from django.contrib.auth.forms import UserChangeForm, UserCreationForm
from .models import User
class MyUserChangeForm(UserChangeForm):
class Meta(UserChangeForm.Meta):
model = User
class MyUserCreationForm(UserCreationForm):
error_message = UserCreationForm.error_messages.update({
'duplicate_username': 'This username has already been taken.'
})
class Meta(UserCreationForm.Meta):
model = User
def clean_username(self):
username = self.cleaned_data["username"]
try:
User.objects.get(username=username)
except User.DoesNotExist:
return username
raise forms.ValidationError(self.error_messages['duplicate_username'])
#admin.register(User)
class UserAdmin(AuthUserAdmin):
form = MyUserChangeForm
add_form = MyUserCreationForm
You are missing the fieldsets attribute:
#admin.register(User)
class UserAdmin(AuthUserAdmin):
form = MyUserChangeForm
add_form = MyUserCreationForm
fieldsets = (
('', {'fields': ('department',)}),
) + AuthUserAdmin.fieldsets
list_display = ('username', 'department', 'is_superuser')
search_fields = ['username', 'department']
You don't need to set the attributes list_display and search_fields to display your department field. But I left them in the sample since they are very handy when it comes to Django admin customization.
ModelAdmin.list_display:
Set list_display to control which fields are displayed on the change
list page of the admin.
ModelAdmin.search_fields:
Set search_fields to enable a search box on the admin change list
page. This should be set to a list of field names that will be
searched whenever somebody submits a search query in that text box.
Related
I've added a plan field to my custom Account class, but cannot get it to show on the individual account page in the django admin. It shows correctly in table of all accounts in the list view (as denoted by list_display), but does not show on each individual account page.
Here's my Account model:
class Account(AbstractUser):
PLANS = (
("free", "free"),
("pro", "pro")
)
plan = models.CharField(max_length=10, choices=PLANS, default="free")
def __str__(self) -> str:
return self.first_name
And my admin file:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from accounts.models import Account
from subscribers.models import Subscriber
class SubscriberInline(admin.TabularInline):
model = Subscriber
extra = 0
class AccountAdmin(UserAdmin):
inlines = [SubscriberInline, ]
list_display = ("first_name", "plan", "email")
# fields = ("first_name", "plan", "email")
admin.site.register(Account, AccountAdmin)
Why does this happen?
Is the problem related to the custom Account class which inherits from the AbstractUser model? I thought to add fields to the AccountAdmin class, but that just returns the below error:
ERRORS:
<class 'accounts.admin.AccountAdmin'>: (admin.E005) Both 'fieldsets' and 'fields' are specified.
The plan field also doesn't show in the admin panel when trying to create a new account (mind you, neither do most of the other fields as it only asks for the username, password1 and password2 fields, and the option to add new subscribers to the table, but other fields like first_name etc can be edited in the admin after creation).
Thanks
UPDATE:
Adding user #bdbd's suggested changes seems to not make a difference to the admin area - am I adding this incorrectly? Here's my admin.py after adding #bdbd's changes:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from accounts.models import Account
from subscribers.models import Subscriber
from django.contrib.auth.forms import UserChangeForm
from django import forms
PLANS = (
("free", "free"),
("pro", "pro")
)
class MyAccountChangeForm(UserChangeForm):
plan = forms.ChoiceField(choices=PLANS)
class SubscriberInline(admin.TabularInline):
model = Subscriber
extra = 0
class AccountAdmin(UserAdmin):
form = MyAccountChangeForm
inlines = [SubscriberInline, ]
list_display = ("first_name", "plan", "email")
# #todo: bug! plan field not showing in django admin on individual account pages
admin.site.register(Account, AccountAdmin)
UPDATE 2:
Screenshots of admin area:
You should not extend from UserAdmin. Instead, you should create your own model admin class which extends from admin.ModelAdmin.
Then you should register your model separately.
class AccountAdmin(admin.ModelAdmin):
...
admin.site.register(Account, AccountAdmin)
As necessary, you can customize AccountAdmin to get the effect you want. You can peek at the UserAdmin source code to see how it is customized, if you want your admin view to behave similarly.
You need to override the default form that is being used by UserAdmin and add your field like so:
from django.contrib.auth.forms import UserChangeForm
from django import forms
class MyAccountChangeForm(UserChangeForm):
plan = forms.ChoiceField(choices=PLANS)
Then assign the form to your admin:
class AccountAdmin(UserAdmin):
form = MyAccountChangeForm
This is the image of the default Django User fields.
models.py:
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Visitor(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
phone = models.IntegerField()
admin.py:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User
from accounts.models import Visitor
class VisitorInline(admin.StackedInline):
model = Visitor
can_delete = False
verbose_name_plural = "visitor"
class UserAdmin(BaseUserAdmin):
inlines = (VisitorInline,)
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
This image has the default Django user fields such as username, email address, first name and last name. I want to add a custom field such as a phone number to the existing fields. How can I achieve this? Thank you!
When I enter the details in this form and press the submit button , I don't see the values of phoneno and otp getting saved in the database .The fields phone number and otp are not shown at all .
SEE image only username is saved and the otp and phone number fields are not displayed nor saved
This is my signup/forms.py
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
# Create your models here.
class allusers1(UserCreationForm):
phoneno1=forms.CharField(label = "phonewa",max_length=10)
otp1=forms.IntegerField(label="OTPP",required=False)
class Meta:
model=User
fields=(
'username',
'password',
'phoneno1',
'otp1',
)
def save(self,commit=True):
user=super(allusers1,self).save(commit=False)
user.username1=self.cleaned_data['username']
user.password1=self.cleaned_data['password']
user.phoneno1=self.cleaned_data['phoneno1']
user.otp1=self.cleaned_data['otp1']
if commit:
user.save()
return user
This is mysignup/forms.py
from django.shortcuts import render
from .forms import allusers1
def signup(request):
form1=allusers1(request.POST or None)
if form1.is_valid():
form1.save()
context = {
"form1": form1,
}
return render(request, "signup.html",context)
Your User model is the default django.contrib.auth.models.User that you import in the second line. This model has predefined fields. otp1and phoneno1 are not amongst them as you can see from the docs. So when you save a Userinstance, these attributes are simply ignored.
So you have to extend the User model like described in the docs (Django 2.0).
# models.py
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
otp1 = models.IntegerField(null=True)
phoneno1 = models.CharField(max_length=10)
# settings.py
settings.AUTH_USER_MODEL = 'myapp.User'
# admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User
admin.site.register(User, UserAdmin)
You won't need a special form then for the Django admin. Your own model will inherit everything Django's User model brings with it, plus your own fields / methods.
I am having an issue passing a field from an extended user model to a template in Django.
I defined the extended user fields in a model in a new app called user_management:
#user_management/models.py
from __future__ import unicode_literals
from django.db import models
from django.contrib.auth.models import User
class lab_user(models.Model):
user = models.OneToOneField(User)
personal_order_list_url = models.URLField("Personal order list URL", max_length=255, blank=False, unique=True)
abbreviation_code = models.CharField("Abbreviation code", max_length=3, blank=False, unique=True)
def save(self, force_insert=False, force_update=False):
self.personal_order_list_url = self.personal_order_list_url.lower()
self.abbreviation_code = self.abbreviation_code.upper()
super(lab_user, self).save(force_insert, force_update)
I then registered the new fields in admin.py:
#user_management/admin.py
from __future__ import unicode_literals
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User
from .models import lab_user
class lab_user_inline(admin.StackedInline):
model = lab_user
can_delete = False
verbose_name_plural = 'Additional Fields'
class UserAdmin(BaseUserAdmin):
inlines = (lab_user_inline, )
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
I can see the new fields in "Authentication and Authorization", which I guess means I've done everything "right".
When I try to call fields in a template using {{ request.user.x }} where x could be first_name, personal_order_list_url or abbreviation_code, I can retrieve the desired value for first_name and personal_order_list_url, but not for abbreviation_code.
If request.user.personal_order_list_url works, request.user.abbreviation_code should work too, shouldn't it?
request.user.lab_user.personal_order_list_url
I've use this tutorial and do exactly what they do:
https://docs.djangoproject.com/ja/1.9/topics/auth/customizing/#extending-the-existing-user-model
my model.py:
from django.db import models
from django.contrib.auth.models import User
from datetime import datetime
class Chess(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
last_activity = models.DateTimeField(default=datetime(1970,1,1,))
is_avaliable = models.BooleanField(default=False,)
in_progress = models.BooleanField(default=False,)
my admin.py:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User
from chess_tournament.models import Chess
class ChessInline(admin.StackedInline):
model = Chess
can_delete = False
verbose_name_plural = 'Chess'
# Define a new User admin
class UserAdmin(BaseUserAdmin):
inlines = (ChessInline, )
# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
in managaer.py shell:
from django.contrib.auth.models import User
u = User.objects.get(pk=1)
u.username # return 'admin' it's work
u.chess.last_activity # return ERROR (described below)
AttrinbuteError: 'User' object has no attribute 'chess'
but in django admin panel this field are available and works
Please help to figure it out coz I already spent 4 hours for it...
You need to update your Chess model and add related_name to the user property.
class Chess(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="chess_board")
# Other Stuff
Now you can easily access last_activity or any other property:
u.chess_board.last_activity
Hope it helps.
Could it possible that you didn't register Chess model?
Try add admin.site.register(Chess, ChessAdmin) at the bottom of admin.py. Of course you might have to create a simple ChessAdmin for display first.