I just created a custom user Model and a custom user manager. I'm able to create superuser with manage.py createsuperuser, but when it comes to update, delete or even create a new user on the admin panel, I have this wierd error :
NOT NULL constraint failed: users_user.email
I have no idea how to fix it and I'm just stuck. Here is my model and my manager :
class UserManager(BaseUserManager):
#custom create_user method
def create_user(self, email, password=None):
if not email:
raise ValueError('Users must have an email address')
user = self.model(
email = self.normalize_email(email)
)
print(email)
user.set_password(password)
user.save(using=self._db)
print(user)
return user
#Custom create_super_user method
def create_superuser(self, email, password=None):
user = self.create_user(
email = email,
password = password
)
user.admin = True
user.is_superuser = True
user.is_staff = True
user.save(using=self._db)
return user
class User(AbstractBaseUser):
#setting up Choices for interest, Must add other fields ...
#interests = MultiSelectField(
# max_length = 2,
# choices = INTERESTS_CHOICES
#)
#Setting up a Ranking System
email = models.EmailField(
max_length=50,
unique=True,
blank=False,
null=False
)
username = models.CharField(
max_length=25,
unique=True,
null=True,
blank=True
)
date_joined = models.DateTimeField(auto_now_add=True)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=70)
birth_date = models.DateField(null=True, blank=True)
reputation = models.PositiveIntegerField(default=0)
active = models.BooleanField(default=True)
rank = models.CharField(choices=RANKING_CHOICES, max_length=5, default="basic")
is_staff = models.BooleanField(default=False)
admin = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
bio = models.TextField(
max_length=300,
default="default Bio",
blank=True
)
objects = UserManager()
#Setting email to be the main source of authentication
USERNAME_FIELD = 'email'
#Super User Only
REQUIRED_FIELDS = ['password']
#def get_absolute_url(self):
#use reverse + nom de l'url de view
def __str__(self):
return str(self.email)
def get_full_name(self):
return f"{self.first_name} {self.last_name}"
def get_short_name(self):
return self.first_name
def get_username(self):
return self.username
def set_user_league(self):
if 15 <= self.reputation < 40:
self.rank = "gold"
elif 40 <= self.reputation < 80:
self.rank = "platinium"
else:
self.rank = "diamond"
#property
def is_admin(self):
print(f" is {self.email} admin ? ")
return self.admin
def has_perm(self, obj=None):
return True
def has_module_perms(self, obj=None):
return True
What should I do ? I have been stuck for hours. Thank you !
make your model as a OneToOne relationship to User model. then rewrite the email field with null=True
Related
I am trying to use Django Authentication and I want to create a custom model for the user that has an autoincrementing integer as id. I know about uuid library, but I want the id to be an integer number, that is why I want to avoid it.
My code looks like:
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
class MyAccountManager(BaseUserManager):
def create_user(self, first_name, last_name, email, username, avatar, password=None):
if not username:
raise ValueError('User must have an username')
if not avatar:
raise ValueError('User must have an avatar')
user = self.model(
email=self.normalize_email(email),
username=username,
avatar=avatar,
first_name=first_name,
last_name=last_name
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, first_name, last_name, email, username, avatar, password):
user = self.create_user(
email=self.normalize_email(email),
username=username,
avatar=avatar,
password=password,
first_name=first_name,
last_name=last_name
)
user.is_admin = True
user.is_active = True
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
return user
class Account(AbstractBaseUser):
id = models.AutoField(primary_key=True)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
username = models.CharField(max_length=50, unique=True)
email = models.CharField(max_length=50, unique=True)
avatar = models.CharField(max_length=200)
# required
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_superadmin = models.BooleanField(default=False)
USERNAME_FIELD = 'id'
REQUIRED_FIELDS = ['username', 'first_name', 'last_name', 'email', 'avatar']
objects = MyAccountManager()
def __str__(self):
return self.username
def has_perm(self, perm, obj=None):
return self.is_admin
def has_module_perms(self, add_label):
return True
The problem is that I still want to log in with the username, not the id. Also, as it autoincrements, it would be nice that I won't have to manually introduce it when I create a superuser from the console.
Is this possible?
I tried to solve your requirement... using save method overriding
Model code:
class CustomUserModel(AbstractBaseUser, PermissionsMixin):
auto_id = models.PositiveBigIntegerField(unique=True)
username = models.CharField(max_length=255, unique=True)
email = models.EmailField(verbose_name="email address", max_length=255, unique=True)
mobile_no = models.PositiveIntegerField(verbose_name="mobile number",unique=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
date_joined = models.DateTimeField(default=timezone.now)
objects = CustomUserManager()
USERNAME_FIELD = "username"
REQUIRED_FIELDS = ['email','mobile_no']
def save(self, *args, **kwargs):
count_id = CustomUserModel.objects.all().count()
self.auto_id = count_id+1
super(CustomUserModel, self).save(*args, **kwargs)
def __str__(self):
return self.email
Output admin panel view:
I've created a custom user model using AbstractBaseUser. When I try to create a super user using terminal via createsuperuser command.
I get
in create_superuser
user.is_staff = True
AttributeError: can't set attribute
Below is my code for creating custom user model.
import uuid
from django.db import models
from django.contrib.auth.models import (
AbstractBaseUser, AbstractUser, BaseUserManager
)
class AdminUserManager(BaseUserManager):
def create_user(self, email_id, password=None, *args, **kwargs):
if not email_id:
raise ValueError('User must have an Email ID')
user = self.model(email_id=email_id)
user.set_password(password)
user.save(using = self._db)
return user
def create_staffuser(self, email_id, password, *args, **kwargs):
user = self.create_user(
email_id, password=password
)
user.is_staff = True
user.save(using = self._db)
return user
def create_superuser(self, email_id, password, *args, **kwargs):
user = self.create_user(
email_id, password=password
)
user.is_staff = True
user.is_superuser = True
user.save(using = self._db)
return user
USER_ROLE = (
('pilot', 'pilot'),
('admin', 'admin')
)
class AdminUser(AbstractBaseUser):
user_uid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
profile_picture = models.ImageField(upload_to='user_profile_picture/', null=True, blank=True)
user_role = models.CharField(max_length=10, choices=USER_ROLE)
email_id = models.EmailField(verbose_name='Email ID', unique=True)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
phone_number = models.CharField(max_length=20)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_verified = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
USERNAME_FIELD = 'email_id'
REQUIRED_FIELDS = ['first_name', 'last_name']
def get_full_name(self):
full_name = f'{self.first_name} {self.last_name}'
return full_name
def get_short_name(self):
return self.first_name
def __str__(self):
return self.email_id
#property
def is_staff(self):
return self.is_staff
#property
def is_superuser(self):
return self.is_superuser
objects = AdminUserManager()
I had everything running perfectly but I had to reinstall windows and when I tried to run the code again, it started giving me errors on user registration.
I followed Vitor Freitas guide to make implement multiple user types but only differed in views as he was using class based views and I had no idea what those were at that time.
I have narrowed down the problem where I am creating User table
(This line: user = User.objects.create(user=account) in forms.py).
Models.py
class MyAccountManager(BaseUserManager):
def create_user(self , email, password=None):
if not email:
raise ValueError('Users must have an email address')
user = self.model(
email=self.normalize_email(email)
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, first_name,last_name, password):
user = self.create_user(
email=self.normalize_email(email),
password=password,
first_name=first_name,
last_name=last_name,
)
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=50,unique=True)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
is_user = models.BooleanField(default=False)
is_Manager = models.BooleanField(default=False)
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)
USERNAME_FIELD = 'email'
objects = MyAccountManager()
def __str__(self):
return self.email
# For checking permissions. to keep it simple all admin have ALL permissons
def has_perm(self, perm, obj=None):
return self.is_admin
# Does this user have permission to view this app? (ALWAYS YES FOR SIMPLICITY)
def has_module_perms(self, app_label):
return True
def upload_location(instance, filename):
file_path = 'resume/{user_id}/{filename}'.format(
user_id=str(instance.user.id), filename=filename)
return file_pathenter code here
class User(models.Model):
user = models.OneToOneField(Account, on_delete=models.CASCADE, primary_key=True)
city = models.CharField(max_length=40)
CV = models.FileField(upload_to=upload_location,validators=[FileExtensionValidator(allowed_extensions=['pdf'])])
dream_Job_Title = models.CharField(max_length=200)
industries = models.CharField(max_length=150)
speciality = models.CharField(max_length=200)
current_Profession = models.CharField(max_length=50)
target_Job_Title = models.CharField(max_length=100)
feedback = models.CharField(max_length=200)
rating = models.IntegerField()
phone = PhoneNumberField(null=False, blank=False, unique=True)
university = models.CharField(max_length=70,default="")
GPA = models.CharField(max_length=20,default="")
Major = models.CharField(max_length=30,default="")
Minor = models.CharField(max_length=30,default="")
Certification = JSONField()
Accomplishment = JSONField()
WorkExperience = JSONField()
Skills = JSONField()
REQUIRED_FIELDS=['CV','dream_Job_Title','industries','speciality']
# Does this user have permission to view this app? (ALWAYS YES FOR SIMPLICITY)
def has_module_perms(self, app_label):
return True
forms.py
class UserRegister(UserCreationForm):
email = forms.EmailField(max_length=50,help_text='Enter a valid Email')
city = forms.CharField(max_length=40)
class Meta:
model = Account
fields = ['first_name','last_name', 'email', 'city', 'password1','password2']
#transaction.atomic
def save(self):
account = super().save(commit=False)
account.is_user = True
account.save()
user = User.objects.create(user=account)
#user.city.add(form.cleaned_data.get('city'))
user.city = self.cleaned_data.get('city')
user.save()
return account
def clean_email(self):
return self.cleaned_data['email'].lower()
views.py
def UserRegister_view(request):
context = {}
user = request.user
if user.is_authenticated and user.is_user:
return redirect("UserDashboard")
if request.POST:
form = UserRegister(request.POST)
if form.is_valid():
form.save()
return redirect('login')
else:
context['registration_form'] = form
else:
form = UserRegister()
context['registration_form'] = form
return render(request, 'UserRegister.html', context)
I have also tried upgrading/downgrading dependencies.
Good day SO.
I am new to Django and having troubles with something basic. What I am trying to do is when I click on register, I want to create an Account and at the same time, a company account.
When I click on sumbit, the template returns my Account(the OneToOneField) This field is required.
Though my methods might be not aligned with good practice, but I hope that you can help me with this. I have been trying to check with other resources for two days but I can't seem to find the solution to my concern.
Here is my forms.py:
from django import forms
from django.contrib.auth.forms import UserCreationForm
from .models import Account, CompanyAccount
class AccountCreationForm(UserCreationForm):
email = forms.EmailField(max_length=60, help_text="Required")
class Meta:
model = Account
fields = ("email", "username", "password1", "password2", "account_type")
class CompanyAccountForm(forms.ModelForm):
class Meta:
model = CompanyAccount
fields = "__all__"
my models.py:
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
# Create your models here.
class MyAccountManager(BaseUserManager):
def create_user(self, email, username, account_type, password):
if not email:
raise ValueError("Users must have an Email Address")
if not username:
raise ValueError("Users must have an Username")
if not account_type:
raise ValueError("Users must have an Account Type")
user = self.model(
email=self.normalize_email(email),
username=username,
password=password,
account_type=account_type,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, username, account_type, password):
user = self.create_user(
email=self.normalize_email(email),
username=username,
password=password,
account_type=account_type,
)
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, default='', null=False, unique=True)
username = models.CharField(verbose_name='Username', max_length=50, default='', null=False, unique=True)
date_joined = models.DateTimeField(verbose_name='date joined', auto_now_add=True)
last_joined = models.DateTimeField(verbose_name='last joined', auto_now_add=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)
ACCOUNT_TYPE_CHOICES = (
(1, 'Applicant'),
(2, 'Company'),
(3, 'Client'),
)
account_type = models.PositiveSmallIntegerField(default=0, choices=ACCOUNT_TYPE_CHOICES)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username', 'account_type', ]
objects = MyAccountManager()
def __str__(self):
return self.username
def has_perm(self, perm, obj=None):
return self.is_admin
def has_module_perms(self, app_label):
return True
class CompanyAccount(models.Model):
account = models.OneToOneField(Account, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
company_name = models.CharField(max_length=100, default='', null=False)
views.py
context = {}
if request.method == "POST":
rForm = AccountCreationForm(request.POST)
cForm = CompanyAccountForm(request.POST)
if rForm.is_valid() and cForm.is_valid():
rForm.save()
cForm.save()
else:
rForm = AccountCreationForm()
context['rForm'] = rForm
cForm = CompanyAccountForm()
context['cForm'] = cForm
return render(request, 'registration/company_registration_form.html', context)
If you want your model's field to be allowed to be empty when submitting forms, add blank=True:
account = models.OneToOneField(Account, on_delete=models.CASCADE, blank=True)
https://docs.djangoproject.com/en/3.1/ref/models/fields/
I would like to create a form with my CustomUserModel as shown below, which has a extending model called customer.
class UserManager(BaseUserManager):
def create_user(self, email, password=None,is_active=True, is_staff=False, is_admin=False):
if not email:
raise ValueError("Users must have email address")
user_obj = self.model(email = self.normalize_email(email))
if not password:
raise ValueError("Users must have a password")
user_obj.set_password(password)
user_obj.staff = is_staff
user_obj.admin = is_admin
user_obj.active = is_active
user_obj.save(using=self._db)
return user_obj
def create_staffuser(self,email,password=None):
user = self.create_user(email, password=password,is_staff=True)
return user
def create_superuser(self, email, password=None):
user = self.create_user(email, password=password, is_staff=True, is_admin=True)
return user
class User(AbstractBaseUser):
email = models.EmailField(max_length=255,unique=True)
active = models.BooleanField(default=True)
staff = models.BooleanField(default=False)
admin = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
# email and password are required by default
REQUIRED_FIELDS = []
objects = UserManager()
def __str__(self):
return self.email
def get_full_name(self):
return self.email
def get_short_name(self):
return self.email
def has_perm(self, perm, obj=None):
return True
def has_module_perms(self, app_label):
return True
#property
def is_staff(self):
return self.staff
#property
def is_admin(self):
return self.admin
#property
def is_active(self):
return self.active
class Customer(models.Model):
GENDER = (
('Male', 'Male'),
('Female', 'Female'),
)
TITLE = (
('Mr', 'Mr'),
('Mrs', 'Mrs'),
('Miss', 'Miss'),
('Ms', 'Ms'),
('Dr', 'Dr'),
('Sir', 'Sir'),
('Madam', 'Madam'),
)
user = models.OneToOneField(User,on_delete=models.CASCADE)
title = models.CharField(max_length=200, null=True, choices=TITLE)
first_name = models.CharField(max_length=200, null=True)
middle_name = models.CharField(max_length=200, blank=True,default='')
last_name = models.CharField(max_length=200, null=True)
phone = models.CharField(max_length=200, null=True)
country = CountryField()
birth_year = models.CharField(max_length=4, null=True)
gender = models.CharField(max_length=200, null=True, choices=GENDER)
date_created = models.DateTimeField(auto_now=True, null=True)
profile_pic = models.ImageField(null=True, blank=True)
last_purchase = models.DateTimeField(blank=True, null=True)
def __str__(self):
return self.user.email
Below shows my views.py to create a new customer/user form, that I would like to create at the same time.
#login_required(login_url='login')
def NewCustomerProfile(request):
user_form = RegisterForm()
customer_form = CustomerProfileForm()
if request.method == 'POST':
user_form = RegisterForm(request.POST)
customer_form = CustomerProfileForm(request.POST)
if user_form.is_valid() and customer_form.is_valid():
user_form.save()
customer_form.save()
return redirect('/')
return render(request, 'accounts/new_customer_profile.html', {'user_form': user_form,'customer_form':customer_form})
And see below showing my forms for both of the models
class CustomerProfileForm(ModelForm):
class Meta:
model = Customer
fields = ['title','first_name','middle_name','last_name','phone','country','birth_year','gender']
class RegisterForm(forms.ModelForm):
"""
A form for creating new users. Includes all the required
fields, plus a repeated password.
"""
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
class Meta:
model = User
fields = ('email','admin', 'active')
I have been getting an error as shown below
IntegrityError at /new_customer_profile/
NOT NULL constraint failed: accounts_customer.user_id
Problem is new customer can be created once a user has been established and created. I would like to create a new user and customer at the same time with no errors accordingly to the models, forms and views shown above.
How does one come to the solution?
A ModelForm when saved returns the instance. In addition it has parameter that allows you not to save the model to the database. Combine these two and there's your solution:
if user_form.is_valid() and customer_form.is_valid():
user = user_form.save()
customer = customer_form.save(commit=False)
customer.user = user
customer.save()
return redirect('/')
I think you answer your own question, in a sense:
"Problem is new customer can be created once a user has been established and created."
Re-write your view to logic to do this in the order that it actually works. It can still happen "at the same time" relative to to the user, even when your view is doing it sequentially.