Django UNIQUE constraint failed: accounts_user.username - python

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
# ...

Related

customize models in Django RestFramework using AbstractBaseUser, BaseUserManager

i wanna customize models. so i used AbstractBaseUser, BaseUserManager
i use **extra_field to add 'point' attribute.
i enterd http://127.0.0.1:8000/account/signup/
and write my data
but the errors occured
enter image description here
what's wrong with my code?
i'm a beginner, plz help me
# serializers.py
from .models import User
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
def create(self, validated_data):
user = User.objects.create_user(
email = validated_data['email'],
nickname = validated_data['nickname'],
name = validated_data['name'],
password = validated_data['password'],
point = validated_data['point']
)
return user
class Meta:
model = User
fields = ['name', 'nickname', 'email', 'password']
# models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
class UserManager(BaseUserManager):
def create_user(self, email, nickname, name, password=None, **point):
if not email:
raise ValueError('must have user email')
if not nickname:
raise ValueError('must have user nickname')
if not name:
raise ValueError('must have user name')
user = self.model(
email = self.normalize_email(email),
nickname = nickname,
name = name,
point = point
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, nickname, name, password=None):
user = self.create_user(
email,
password = password,
nickname = nickname,
name = name
)
user.is_admin = True
user.save(using=self._db)
return user
class User(AbstractBaseUser):
id = models.AutoField(primary_key=True)
email = models.EmailField(default='', max_length=100, null=False, blank=False, unique=True)
nickname = models.CharField(default='', max_length=100, null=False, blank=False, unique=False)
name = models.CharField(default='', max_length=100, null=False, blank=False)
point = models.IntegerField(default=500, max_length=10000000)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['email', 'name', 'nickname', 'password']
def __str__(self):
return self.name
# views.py
from django.shortcuts import render
from .serializers import UserSerializer
from .models import User
from rest_framework import generics
# 회원가입
class UserCreate(generics.CreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
# urls.py
from django.urls import path, include
from . import views
from rest_framework import urls
urlpatterns =[
path('signup/', views.UserCreate.as_view()),
path('api-auth/', include('rest_framework.urls')),
]
i wanna customize models. so i used AbstractBaseUser, BaseUserManager
i use **extra_field to add 'point' attribute.
i enterd http://127.0.0.1:8000/account/signup/
and write my data
but the errors occured
what's wrong with my code?
i'm a beginner, plz help me
When you unpack values using ** then you should access the values like this:
def create_user(self, email, nickname, name, password=None, **kwargs):
if not email:
raise ValueError('must have user email')
if not nickname:
raise ValueError('must have user nickname')
if not name:
raise ValueError('must have user name')
user = self.model(
email = self.normalize_email(email),
nickname = nickname,
name = name,
point = kwargs['point']
)
user.set_password(password)
user.save(using=self._db)
return user
Check how I get the value of point. The thing is that your definition of point is { point: 500 }. So is a dict. You need to get the value of point from that dict.

Django Admin doesn't recognise blank=True in model

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")

Custom user model - 'Group' object has no attribute 'user_set'

I am fairly new to Django and set up a custom user model with token authentication. Now I am trying to add the possibility of adding users to user groups from the backend in order to specify permissions. However, I dont seem to be able to get past this error. I am using Python 3.7 and Django 2.2.6. Appreciate your help!
The error:
File "/Users/.../Dev/.../account/models.py", line 114, in save_m2m
self.instance.user_set.set(self.cleaned_data['users'])
AttributeError: 'Group' object has no attribute 'user_set'
my models.py:
from django.db import models
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token
from django import forms
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.admin.widgets import FilteredSelectMultiple
from django.contrib.auth.models import Group
class MyAccountManager(BaseUserManager):
def create_user(self, email, username, password=None):
if not email:
raise ValueError("Users must have an email address")
if not username:
raise ValueError("Users must have a username")
user = self.model(
email=self.normalize_email(email),
username=username,
)
group = Group.objects.all()
user.groups.add(group)
# group.user_set.add(user)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, username, password):
user = self.create_user(
email=self.normalize_email(email),
password=password,
username=username,
)
user.is_admin = True
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
return user
class Account(AbstractBaseUser):
email = models.EmailField(verbose_name='email', max_length=60, unique=True)
username = models.CharField(max_length=30, unique=True)
date_joined = models.DateTimeField(
verbose_name='date joined', auto_now_add=True)
last_login = models.DateTimeField(verbose_name='last login', auto_now=True)
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
plan_choices = [("P", "Pro"), ("B", "Basic"),
("F", "Free"), ("S", "Staff"), ("A", "Admin")]
plan = models.CharField(choices=plan_choices, default="F", max_length=1)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
objects = MyAccountManager()
def __str__(self):
return self.email
def has_perm(self, perm, obj=None):
return self.is_admin
def has_module_perms(self, app_label):
return True
#receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
if created:
# if new user is registered to the server, a new token is generated
Token.objects.create(user=instance)
User = get_user_model()
# Create ModelForm based on the Group model.
# class GroupAdminForm(forms.ModelForm):
# class Meta:
# model = Group
# exclude = []
# Add the users field.
# users = forms.ModelMultipleChoiceField(
# queryset=User.objects.all(),
# required=False,
# widget=FilteredSelectMultiple('users', False)
# )
# def __init__(self, *args, **kwargs):
# super(GroupAdminForm, self).__init__(*args, **kwargs)
# if self.instance.pk:
# self.fields['users'].initial = self.instance.user_set.all()
# def save_m2m(self):
# self.instance.user_set.set(self.cleaned_data['users'])
# def save(self, *args, **kwargs):
# instance = super(GroupAdminForm, self).save()
# self.save_m2m()
# return instance
my admin.py:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from account.models import Account
from django.contrib.auth.models import Group
# from .models import GroupAdminForm
class AccountAdmin(UserAdmin):
list_display = ('email', 'username', 'date_joined',
'last_login', 'is_admin', 'is_staff', 'plan')
search_fields = ('email', 'username')
readonly_fields = ('date_joined', 'last_login')
filter_horizontal = ()
list_filter = ()
fieldsets = ()
admin.site.register(Account, AccountAdmin)
# Unregister the original Group admin.
# admin.site.unregister(Group)
# class GroupAdmin(admin.ModelAdmin):
# form = GroupAdminForm
# filter_horizontal = ['permissions']
# admin.site.register(Group, GroupAdmin)
UPDATE
I adjusted the code to no longer overwrite the Group class, and instead extend PermissionsMixin in my Account class. This allowed me to see permission groups and specific groups on a per user basis, however when these are applied, there seems to be no effect at all. I still have the same access as before. What am I missing?
group = Group.objects.all() is a queryset. user_set can be accessed from a Group instance.
Also, add the possibility of adding users to user groups from the backend - you don't need to do that. Once the user is created, you can always add the user to a particular group by:
groups = Group.objects.filter(...) # get groups you want to add the user to
user.groups.add(*groups)

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')}),
)

Categories