Django Admin doesn't recognise blank=True in model - python

When I try to edit a user (using a custom UserChangeForm) in the Django admin panel, validation insists that fields I have set blank=True in the model are required.
I don't know where to begin solving this; I had the same issue with the CustomUserCreationForm but reverted to using the default which works as expected (asks for username, password1 & password2, creates the user with blank display_name, bio and profile_picture fields).
models.py:
from django.db import models
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
display_name = models.CharField(max_length=30, blank=True, null=True)
bio = models.TextField(blank=True, null=True)
profile_picture = models.ImageField(upload_to='images/', blank=True, null=True)
def save(self, *args, **kwargs):
if not self.display_name:
self.display_name = self.username
super().save(*args, **kwargs)
def __str__(self):
return self.username
forms.py:
from django import forms
from django.contrib.auth.forms import UserChangeForm
from .models import CustomUser
class CustomUserChangeForm(UserChangeForm):
display_name = forms.CharField(label="display_name")
bio = forms.CharField(widget=forms.Textarea)
profile_picture = forms.ImageField(label="profile_picture")
class Meta():
model = CustomUser
fields = ("username", "email", "display_name", "bio", "profile_picture")
admin.py:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .forms import CustomUserChangeForm
from .models import CustomUser
class CustomUserAdmin(UserAdmin):
form = CustomUserChangeForm
fieldsets = (
(None,
{'fields': ('username', 'password', 'email', 'display_name', 'bio', 'profile_picture')}
),
)
model = CustomUser
list_display = ["username", "email",]
admin.site.register(CustomUser, CustomUserAdmin)

From the Django documentation:
By default, each Field class assumes the value is required, so if you
pass an empty value – either None or the empty string ("") – then
clean() will raise a ValidationError exception:
So you have to add required=False in your forms.py. For example:
display_name = forms.CharField(required=False, label="display_name")

Related

Django UNIQUE constraint failed: accounts_user.username

I am implementing a custom Django user from scratch. Almost all things are working, but an issue arises when I create new user from the Django admin panel as follows.
django.db.utils.IntegrityError: UNIQUE constraint failed: accounts_user.username
Why does this error occur even if there is no username field in my User model?
I have included supporting files here.
models.py
import uuid
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils.translation import gettext_lazy as _
from .managers import CustomManager
from .utils import path_to_upload
# Create your models here.
class BaseModelMixin(models.Model):
uuid = models.UUIDField(default=uuid.uuid4, unique=True, editable=False)
creation_date = models.DateTimeField(_("Created At"), auto_now=True)
modification_date = models.DateTimeField(_("Modified At"),
auto_now_add=True)
class Meta:
abstract = True
class User(AbstractUser, BaseModelMixin):
display_name = models.CharField(_("Display Name"), max_length=200,
blank=True, null=True)
email = models.EmailField(_("Email Address"), unique=True,
blank=False, null=False)
profile_pic = models.ImageField(_("Profile Picture"),
upload_to=path_to_upload)
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["display_name"]
objects = CustomManager()
forms.py
from django import forms
from django.contrib.auth.password_validation import validate_password
from django.forms import TextInput, PasswordInput
from django.contrib.auth.forms import UserCreationForm
from django.utils.translation import gettext_lazy as _
from .models import User
class AdminUserRegistrationForm(UserCreationForm):
password1 = forms.CharField(validators=[validate_password,],
widget=forms.PasswordInput(attrs={"placeholder": "Password"}))
password2 = forms.CharField(validators=[validate_password, ],
widget=forms.PasswordInput(attrs={"placeholder": "Confirm Password"}))
class Meta(UserCreationForm.Meta):
model = User
fields = ["display_name", "email", "profile_pic"]
widgets = {}
for field in fields:
if field not in ("password", "profile_pic"):
widgets[field] = TextInput(attrs={
"placeholder": field.replace("_", " ").title()
})
widgets["password"] = PasswordInput(render_value=True,
attrs={'required': False})
widgets["profile_pic"] = forms.FileInput()
class AdminUserUpdateForm(forms.ModelForm):
password = forms.CharField(label=_("Password"), required=True,
validators=[validate_password],
widget=PasswordInput(render_value=True))
class Meta:
model = User
fields = ["display_name", "profile_pic", "password"]
widgets = {}
for field in fields:
if not field == "profile_pic":
widgets[field] = TextInput(attrs={"placeholder": field.replace("_", " ").title()})
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
managers.py
from django.contrib.auth.models import BaseUserManager
from django.utils.translation import gettext_lazy as _
class CustomManager(BaseUserManager):
def create_user(self, display_name, email, password, **extras):
if not email:
raise ValueError(_("Email is a mandatory field"))
email = self.normalize_email(email)
user = self.model(display_name=display_name, email=email, **extras)
user.set_password(password)
# user.save()
return user
def create_superuser(self, display_name, email, password, **extras):
extras.setdefault("is_staff", True)
extras.setdefault("is_active", True)
extras.setdefault("is_superuser", True)
if "is_superuser" not in extras:
raise ValueError(_("'is_superuser' is mandatory for superuser"))
if "is_staff" not in extras:
raise ValueError(_("'is_staff' is mandatory for superuser"))
return self.create_user(display_name, email, password, **extras)
admin.py
from django.contrib import admin
from . import models
from . import forms
# Register your models here.
class CustomUserAdmin(admin.ModelAdmin):
# add_form = forms.AdminUserRegistrationForm
form = forms.AdminUserRegistrationForm
model = models.User
list_display_links = None
list_display = ("display_name", "email",)
admin.site.register(models.User, CustomUserAdmin)
You are inheriting from AbstractBaseUser and it has username field, in order to completely remove it from your model you need to set it as None in the custom user class:
class User(AbstractUser, BaseModelMixin):
username = None
...
By default AbstractUser has username field.
by doing USERNAME_FIELD = "email" username field won't be removed.
It just tells Django to use email as username field instead of the actual username field.
since the username field is required and unique you need to remove it manually:
class User(AbstractUser, BaseModelMixin):
# ...
username = None
# ...

Adding extended Profile model into custom user models admin

How can i add extended Profile model fields (fields which are not available in custom user model fields) into custom users admin users.admin?
what i am trying to do is that i want too see Profile model fields like photo, date_of_birth, country, phone etc.. inside the Personal Info(see in image) & i can make changes in it from here.
profile model
from django.db import models
from django.dispatch import receiver
from django.urls import reverse
from django.db.models.signals import post_save
from django.contrib.auth import get_user_model # or from users.models import User
User = get_user_model()
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
photo = models.ImageField(null=True, blank=True)
date_of_birth = models.DateField(null=True, blank=True)
phone = models.IntegerField(null=True, blank=True)
country = models.CharField(max_length=150, null=True, blank=True)
city = models.CharField(max_length=150, null=True, blank=True)
bio = models.TextField(max_length=150, null=True, blank=True)
def __str__(self):
return str(self.user.email)
def get_absolute_url(self):
return reverse('profiles:profile-detail', kwargs={'pk':self.pk})
def post_save_user_model_receiver(sender, instance, created, *args, **kwargs ):
# when a user is created(custom user model)like signup or through admin it will create those user's profile too
if created:
try:
Profile.objects.create(user=instance) # it create those user's profile
except:
pass
post_save.connect(post_save_user_model_receiver, sender=User)
users.admin
from django.contrib import admin
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth import get_user_model # or from .models import User
from .forms import UserAdminCreationForm, UserAdminChangeForm
# Register your models here.
User = get_user_model() # or from .models import User
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', 'first_name', 'get_phone', 'last_login', 'date_joined', 'is_admin')
list_filter = ('admin', 'staff', 'active')
list_select_related = ('profile',)
def get_phone(self, instance): # to show the Phone in list display from the Profile Model
return instance.profile.phone
get_phone.short_description = 'Phone'
fieldsets = (
(None, {'fields': ('email', 'password')}),
('Personal Info', {'fields': ('first_name', 'last_name',)}),
('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', 'first_name', 'last_name', 'password1', 'password2', )
}
),
)
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)
form which is used to edit users in admin
from django import forms
from django.contrib.auth import get_user_model # or from .models import User
from django.contrib.auth.forms import ReadOnlyPasswordHashField
User = get_user_model() # this method will return the currently active user model
# or from .models import User
class UserAdminCreationForm(forms.ModelForm):
"""
A form for creating new users in admin panel. Includes all the required
fields, plus a repeated password.
"""
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Confirm Password', widget=forms.PasswordInput)
class Meta:
model = User
fields = ('first_name', 'last_name', 'email')
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get('password1')
password2 = self.cleaned_data.get('password2')
if password1 and password2 and password1 != password2:
raise forms.ValidationError("Password don't match")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super(UserAdminCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data['password1'])
if commit:
user.save()
return user
class UserAdminChangeForm(forms.ModelForm):
"""
A form for updating users in admin panel. Includes all the fields on
the user, but replaces the password field with admin's
password hash display field.
"""
password = ReadOnlyPasswordHashField()
class Meta:
model = User
fields = ('first_name', 'last_name', 'email', 'password', 'active', 'staff', 'admin')
def clean_password(self):
# Regardless of what the user provides, return the initial value.
# This is done here, rather than on the field, because the
# field does not have access to the initial value
return self.initial['password']
I would recommend that you override the User model.
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, \
PermissionsMixin
class UserManger(BaseUserManager):
"""
Add extra calling functionalities here
"""
pass
class User(AbstractBaseUser, PermissionsMixin):
"""Custom user model"""
pass
objects = UserManger()
This is the basic format. Add the extra profile fields in the model
in setting.py add
AUTH_USER_MODEL = '{{ app_name }}.{{ model_name }}'
# eg. 'core.User'

DRF: Username still required after customizing Abstract User

I'm trying to write a REST API using Django and DRF. I'm trying to create a user model and use it in my application. But the problem is that it returns a 400 error status code which says:
{"username":["This field is required."]}
This is my code for models:
import uuid
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.conf import settings
from django.dispatch import receiver
from django.utils.encoding import python_2_unicode_compatible
from django.db.models.signals import post_save
from rest_framework.authtoken.models import Token
from api.fileupload.models import File
#python_2_unicode_compatible
class User(AbstractUser):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
email = models.EmailField('Email address', unique=True)
name = models.CharField('Name', default='', max_length=255)
phone_no = models.CharField('Phone Number', max_length=255, unique=True)
address = models.CharField('Address', default='', max_length=255)
country = models.CharField('Country', default='', max_length=255)
pincode = models.CharField('Pincode', default='', max_length=255)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
def __str__(self):
return self.email
#receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
if created:
Token.objects.create(user=instance)
The Serializer:
class CreateUserSerializer(serializers.ModelSerializer):
username = None
def create(self, validated_data):
validated_data['username'] = uuid.uuid4()
user = User.objects.create_user(**validated_data)
return user
def update(self, instance, validated_data):
instance.name = validated_data.get('name', instance.name)
instance.address = validated_data.get('address', instance.address)
instance.country = validated_data.get('country', instance.country)
instance.pincode = validated_data.get('pincode', instance.pincode)
instance.phone_no = validated_data.get('phone_no', instance.phone_no)
instance.email = validated_data.get('email', instance.email)
instance.save()
return instance
class Meta:
unique_together = ('email',)
model = User
fields = (
'id', 'password', 'email', 'name', 'phone_no', 'address', 'country', 'pincode',
)
extra_kwargs = {'password': {'write_only': True}}
Admin.py file:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User
#admin.register(User)
class UserAdmin(UserAdmin):
pass
class User(AbstractUser):
As your User model inherits from AbstractUser, it will inherit
the username field.
Just remove the username field from your User model by setting username = None like this:
class User(AbstractUser):
# ...
username = None
# ...
class UserAdmin(UserAdmin):
As your UserAdmin model inherits from django.contrib.auth.admin.UserAdmin, you will need to update fieldsets, list_display, search_fields, and ordering fields in your UserAdmin model because they use username which you have removed from your User model.
Abstract User always has the username field. Removing it will cause problems. I will suggest you store the email address of the user in username field as well and use that. Please make sure its always updated in both fields which is not very hard.

Remove/hiding username field in django admin edit user form

The frontend user signup form doesn't have username field as I am using email to login.Now I have access to django admin.
When I edit an user I get that username field to edit but I do not want that field to be editable or view-able at all.
admin.py --
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from .models import UserProfile
class ProfileInline(admin.StackedInline):
model = UserProfile
can_delete = False
verbose_name_plural = 'Profile'
fk_name = 'user'
class CustomUserAdmin(UserAdmin):
inlines = (ProfileInline, )
list_display = ('email', 'first_name', 'last_name', 'is_staff')
list_select_related = ('profile', )
exclude = ('username',)
def get_inline_instances(self, request, obj=None):
if not obj:
return list()
return super(CustomUserAdmin, self).get_inline_instances(request, obj)
admin.site.unregister(User)
admin.site.register(User, CustomUserAdmin)
forms.py --
from django import forms
from django.contrib.auth.models import User
from django.utils.translation import ugettext as _
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Div, Field
from ajax_select.fields import AutoCompleteSelectField, AutoCompleteField
from phonenumber_field.formfields import PhoneNumberField
from . import models
from captcha.fields import ReCaptchaField
class SignUpForm(forms.Form):
first_name = forms.CharField(max_length=30)
last_name = forms.CharField(max_length=30)
phone_number = PhoneNumberField(label=_("Phone (Please state your country code eg. +44)"))
organisation = forms.CharField(max_length=50)
email = forms.EmailField()
password1 = forms.CharField(max_length=20)
password2 = forms.CharField(max_length=20)
captcha = ReCaptchaField(attrs={'theme' : 'clean'})
models.py--
from __future__ import unicode_literals
from django.db import models
from django.contrib.auth.models import User
from django.utils.translation import ugettext as _
from easy_thumbnails.fields import ThumbnailerImageField
from ciasroot.settings import THUMBNAILER_SIZES, UPLOAD_PATH
from ciasroot.constants import GENDERS, LANGUAGES
from ciasroot.util import HashedPk
from phonenumber_field.modelfields import PhoneNumberField
import math, decimal, datetime, os
class UserProfile(models.Model, HashedPk):
user = models.OneToOneField(User, unique=True, related_name ='profile')
job_title = models.CharField(max_length=128, blank=True, null=False, default="")
website = models.URLField(max_length=255, blank=True, null=True)
organisation = models.CharField(max_length=50, blank=True, null=True, default="")
phone_number = PhoneNumberField( blank=True, null=True)
The django version is 1.10 so I used exclude. But it throws an error--
KeyError: "Key 'username' not found in 'UserForm'. Choices are: date_joined, email, first_name, groups, is_active, is_staff, is_superuser, last_login, last_name, password, user_permissions."
Do I need to override the signup form because when adding a new user from admin I don't get the fields as frontend signup form(At some point I need to do that as well).
Any help is highly appreciated.
A discussion about this problem is here.
Briefly:
That's because the stock UserAdmin defines fieldsets in which 'username'
is a field, you need to modify the fieldsets, too.
For example:
class CustomUserAdmin(UserAdmin):
# ...
exclude = ('username',)
fieldsets = (
('Personal info', {'fields': ('full_name', 'email', 'password')}),
('Important dates', {'fields': ('last_login', 'date_joined')}),
('Permissions', {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions')}),
)

register django textarea widget in admin

I want to register standard django textarea widget to admin. Now I'm a little confused about this, because in documentation I have ModelAdmin.formfield_overrides example, I don't want to override it, I just want to register it.
Is there some simple way of doing this registration or should I override it and make a custom widget out of it?
models.py:
from django.db import models
from django_countries.fields import CountryField
import datetime
# Create your models here.
class ContactForm(models.Model):
first_name = models.CharField(max_length=150)
last_name = models.CharField(max_length=150)
email = models.EmailField(max_length=250)
mobile = models.CharField(max_length=32, blank=True)
timestamp = models.DateTimeField(default=datetime.datetime.now)
birthday = models.DateField(null=True)
move_in_date = models.DateField(null=True)
move_out_date = models.DateField(null=True)
country = CountryField()
def __unicode__(self):
"""TODO: Docstring for __unicode__.
:returns: TODO
"""
return self.email
class Meta:
ordering = ['-timestamp']
forms.py:
from django.forms import ModelForm, extras
from .models import ContactForm
from django import forms
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout
from crispy_forms.bootstrap import TabHolder, Tab
from django_countries.widgets import CountrySelectWidget
import datetime
def get_move_date_field():
"""TODO: Docstring for get_move_date_field.
:returns: return a DateField suitable for move_in_date and move_out_date
"""
return forms.DateField(widget=extras.SelectDateWidget(), initial=datetime.date.today())
class ContactView(ModelForm):
"""TODO: Docstring for ContactView.
:returns: ContactView is a class in which we are returning about user information.
"""
birthday = forms.DateField(widget=extras.SelectDateWidget(years=range(2025, 1939, -1)))
move_in_date = get_move_date_field()
move_out_date = get_move_date_field()
message = forms.CharField(widget=forms.Textarea)
helper = FormHelper()
helper.form_tag = False
helper.layout = Layout(
TabHolder(
Tab(
'Basic Information',
'first_name',
'last_name',
'email',
'birthday',
'country',
'mobile'
),
Tab(
'Moving and additional Information',
'move_in_date',
'move_out_date',
'message',
)
)
)
class Meta:
fields = ['first_name', 'last_name', 'email', 'mobile',
'birthday', 'move_in_date', 'move_out_date',
'country', 'message']
model = ContactForm
widgets = {
'country': CountrySelectWidget()
}
admin.py:
from django.contrib import admin
from .models import ContactForm
# Register your models here.
class ContactFormAdmin(admin.ModelAdmin):
"""Docstring for ContactFormAdmin. """
class Meta:
model = ContactForm
admin.site.register(ContactForm, ContactFormAdmin)
I just forgot to register message inside `models.py'.
message = models.CharField(max_length=1000)
after makemigration and migrate I had textarea inside django admin.

Categories