I have created the following code to build a custom Django authentication model and I am attempting to create a superuser with the custom user manager but when I attempt to use the python manage.py createsuperuser command, I receive the following error: TypeError: create_superuser() got an unexpected keyword argument 'password'
UserHandler/models.py
# Custom Auth Manager
class AuthManager(BaseUserManager):
def create_superuser(self, supplied_email, supplied_password, supplied_first_name, supplied_last_name, supplied_language):
# Make this password great again (w/ salting and hashing)
hashed_salted_password = AbstractBaseUser.set_password(supplied_password)
superuser = AuthLookup()
superuser.email_address = supplied_email
superuser.password = hashed_salted_password
superuser.first_name = supplied_first_name
superuser.last_name = supplied_last_name
superuser.language = supplied_language
superuser.save()
# Abstract Auth Model
class AuthLookup(AbstractBaseUser, PermissionsMixin):
email_address = models.EmailField(unique=True)
password = models.CharField(max_length=100)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
organization_id = models.UUIDField(null=False, max_length=36, default=uuid.uuid4)
# Firebase tokens(Max length represents 5 device tokens at maximum length of 256 characters plus 4 commas.)
associated_devices = models.CharField(max_length=1284)
is_requester = models.BooleanField(null=False, default=0)
is_responder = models.BooleanField(null=False, default=0)
user_identifier = models.UUIDField(null=False, max_length=36, default=uuid.uuid4)
language = models.CharField(max_length=6, default='US_EN')
user_added = models.DateTimeField(default='1970-01-01 00:00:00')
object = AuthManager()
USERNAME_FIELD = 'email_address'
REQUIRED_FIELDS = ['first_name', 'last_name', 'language']
Settings.py
AUTH_USER_MODEL = 'UserHandler.AuthLookup'
Turns out that the arguments passed to the create_superuser functions must be named the same as the fields in the model.
The error is not present now. (Ive moved on to different errors unfortunately)
class AuthManager(BaseUserManager):
def create_superuser(self, email_address, password, first_name, last_name, language):
# Keep everyone honest here, make sure the email is valid
email_address = self.normalize_email(email_address)
# Make this password great again (w/ salting and hashing)
password = AbstractBaseUser.set_password(password)
superuser = AuthLookup()
superuser.email_address = email
superuser.password = password
superuser.first_name = first_name
superuser.last_name = last_name
superuser.language = language
superuser.save(using=self._db)
Related
I'm relatively new(er) to django but very excited to learn such a versatile framework. I'm working on a project where I will have 2 user types, account 1 and account 2. Account 2 will have the ability to "add a user" to their account. Think of account 2 as a company who can add users to their company.
So far I've extended the generic User model and have created a class for each of the account types but I'm not sure if I'm doing it correctly. Ultimately I will want to create a login/register form for each of the account types - similar to how ziprecruiter functions so some advice on how to approach that would be awesome too if possible.
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('email address'), unique=True)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(default=timezone.now)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = UserManager()
def __str__(self):
return self.email
class account1(User):
profile = models.ForeignKey(User, on_delete=models.CASCADE, related_name='+', null=True)
# account 2 fields here
first_name = models.TextField(max_length=30, blank=True)
last_name = models.TextField(max_length=30, blank=True)
location = models.TextField(max_length=30, blank=True)
class Meta:
db_table = 'account1_user'
class account2(User):
profile = models.ForeignKey(User, on_delete=models.CASCADE, related_name='+')
# account2 user fields here
class Meta:
db_table = 'account2_user'
Am I doing this correctly? What's the best approach to creating a login/registration form for each type of account? What's the best approach to creating a model that will allow for a user of a user type (if that makes sense)?
Rule of thumb is no matter what your business logic, always use one User model for your application. if you want multiple types of user then you could use attributes to determine user types. For example, in my application i want three types of user, Admin,Broker and Client. Here is how we can do this.
class UserManager(BaseUserManager):
def create_client(self, username, first_name, last_name, email, phone, password=None):
user = self.model(
username=username,
first_name=first_name,
last_name=last_name,
phone=phone,
email=self.normalize_email(email)
)
user.set_password(password)
user.is_client = True
user.save(using=self.db)
return user
def create_reseller(self, username, first_name, last_name, email, phone, password=None):
user = self.create_client(username, first_name, last_name, email, phone, password)
user.is_reseller = True
user.save()
return user
def create_admin(self, username, first_name, last_name, email, phone, password=None):
user = self.create_reseller(username, first_name, last_name, email, phone, password)
user.is_admin = True
user.save()
return user
class User(AbstractBaseUser, PermissionsMixin):
is_superuser = None
REQUIRED_FIELDS = ["username", "first_name", "last_name", "phone"]
EMAIL_FIELD = "email"
USERNAME_FIELD = 'email'
objects = UserManager()
email = models.EmailField(unique=True)
username = models.CharField(max_length=DefaultModel.MAX_LENGTH)
first_name = models.CharField(max_length=DefaultModel.MAX_LENGTH)
last_name = models.CharField(max_length=DefaultModel.MAX_LENGTH)
phone = PhoneNumberField(unique=True)
is_admin = models.BooleanField(default=False)
is_reseller = models.BooleanField(default=False)
is_client = models.BooleanField(default=False)
# storing timestamps for users.
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
added_by = models.ForeignKey("self", models.CASCADE, default=None, null=True)
So what i did, i'm using custom UserManager Class which is responsible for generating User Model objects and i've implemented methods for generating for all types of users Client, Broker and Admin.
DefaultModel is actually class which i'm using as constants for setting default values for models. Here is what it looks like:
class DefaultModel:
MAX_LENGTH = 1000
MYSQL_UNIQUE_LENGTH = 255
If you don't know about objects in models you may learn about it from Django docs.
Custom User Models
Django Managers
I am building an app for a charity club that has many different users each belonging to a single club. I want to automatically increment the 'total_members' field of the class 'Club_Chapter' each time a user registers their account for a particular school (hence the User foreign key 'chapter').
models.py
class Club_Chapter(models.Model):
club_id = models.IntegerField(primary_key=True)
school_name = models.CharField(max_length=30)
state = models.CharField(max_length=4)
total_members = models.IntegerField(null = False, default = 0)
def __str__(self):
return self.school_name
# User Model
class User(AbstractUser):
username = None
email = models.EmailField(_('email address'), unique=True)
USERNAME_FIELD = 'email'
chapter = models.ForeignKey('Club_Chapter',on_delete=models.PROTECT)
ranking = models.CharField(default='member', max_length=20)
REQUIRED_FIELDS = []
objects = UserManager()
forms.py
class SignUpForm(UserCreationForm):
first_name = forms.CharField(max_length=30, required=True)
last_name = forms.CharField(max_length=30, required=True)
email = forms.EmailField(max_length=254, required=True)
chapter = models.ForeignKey('Club_Chapter',on_delete=models.PROTECT)
password2 = None
# below is my failed attempt to solve this problem
Club_Chapter.objects.update(total_members=F('total_members') + 1)
class Meta:
model = User
fields = ('first_name', 'last_name',
'chapter','email', 'password1',)
I know this seams like a trivial problem but I have been searching everywhere with no luck. Any input will be greatly appreciated for the solution to this problem will help greatly with other aspects of this project.
I'm not sure what it's important to have an auto-incrementing total_members fields. Django has super powerful querying capabilities. For instance, this would work perfectly:
total_members = User.objects.filter(chapter__school_name="My Cool School").count()
Notice I used the double underscore after chapter to reference a particular field in the foreign key.
Hope this helps.
I am building a custom User class in django to use in creating a signup application and I keep on getting the error above every time I try to makemigrations. As far as I can see, my code is per django documentation here..
I also have AUTH_USER_MODEL correctly placed in my settings configurations.
Here's my models.py
`class MyUserManager(BaseUserManager):
def create_user(self, email,
first_name,last_name,profile_picture,phone_no,password=None):
"""
Creates and saves a User with the given email and password.
"""
if not email:
raise ValueError('Users must have an email address')
user = self.model(
email=self.normalize_email(email),
first_name=first_name,
last_name=last_name,
profile_picture=profile_picture,
phone_no=phone_no,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password):
"""
Creates and saves a superuser with the given email and password.
"""
SuperUser = self.create_user(
email,
password=password,
)
SuperUser.staff = True
SuperUser.admin = True
SuperUser.save(using=self._db)
return SuperUser
class MyUser(AbstractBaseUser):
email = models.EmailField(
verbose_name = 'email_address',
max_length=255,
unique=True,
# validators=email_validator,
)
first_name = models.CharField(max_length=20,blank=False,null=False)
last_name = models.CharField(max_length=20,blank=False,null=False)
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$', message="Phone number
must be entered in the format: '+254 ...'")
phone_no = models.CharField(validators=[phone_regex], max_length=17,
blank=False)
profile_picture = models.ImageField(upload_to='media/',blank=False)
# email_validator = EmailValidator(message='Invalid email
# address',code=None,whitelist=None)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
objects = MyUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name','last_name','phone_no','profile_picture']
# Email & Password are required by default
def get_full_name(self):
return self.email
def get_short_name():
return self.email
def __str__(self):
return self.email
def has_perm(self,perm,obj=None):
#does user have a specific permission
return True
def has_module_pers(self,app_label):
#does user have permissions to view the app 'app_label'
return True
#property
def is_admin(self):
return self.is_admin
#property
def is_active(self):
return self.is_active
# hook in the New Manager to our Model
class MyUser(AbstractBaseUser):
...
objects = MyUserManager()
`
TO create custom User Model
class User(AbstractUser):
"""User model."""
username = None
email = models.EmailField(_('email address'), unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
Official Documentation for CustomUser
You are:
- Extending the base class that Django has for User models.
Removing the username field.
Making the email field required and unique.
List itemTelling Django that you are going to use the email field as the
USERNAME_FIELD
Removing the email field from the REQUIRED_FIELDS
settings (it is automatically included as USERNAME_FIELD)
Source Link
USERNAME_FIELD must be UNIQUE when you create custom User model
So you can just make unique Email field like Roshan says, but some cases Email can not be unique for some services.
So I prefer just make one uuid field and make it USERNAME_FIELD like this
class User(AbstractBaseUser):
uid = models.UUIDField(
default=None,
blank=True,
null=True,
unique=True,
)
USERNAME_FIELD = "uid"
Your code has a multiline comment that is ill-formatted
#email_validator = EmailValidator(message='Invalid email
address',code=None,whitelist=None)
the second line of that comment is not actually commented so the single quote after address is opening a string that appears to contain the rest of the class and is closed at the stray single quote at the very end.
Suddenly I had the need to use one of my existing models for authentication. I know that there are several approaches how I can do that, but here I'm wondering is it possible to redefine password field with a way I choosed?
To be more specific: I want django to know that my access_key field is a User password field.
But current code presupposes that I gonna add another field password to Company model.
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
from django.db import models
from django_extensions.db.fields.encrypted import EncryptedCharField
class CompanyManager(BaseUserManager):
def create_user(self, name, email, access_key=None):
if not email:
raise ValueError('Company must have an email address')
company = self.model(
email=CompanyManager.normalize_email(email),
)
if access_key is not None:
company.access_key = access_key
company.save(using=self._db)
return company
def create_superuser(self, name, email, access_key):
company = self.create_user(name, email, access_key)
company.is_admin = True
company.save()
return company
class Company(AbstractBaseUser):
name = models.CharField(max_length=64, blank=False, null=False,
unique=True)
email = models.CharField(max_length=254, blank=False, null=False,
unique=True)
access_key = EncryptedCharField(max_length=18)
verification_key = EncryptedCharField(max_length=20, blank=True,
null=True)
is_confirmed = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
callback_url = models.URLField(blank=False, null=True)
objects = CompanyManager()
USERNAME_FIELD = 'name'
REQUIRED_FIELDS = ['email']
In the "create super user" step of python manage.py syncdb, I'd like to be able to specify
E-mail
Password
First name
Middle name (blank is acceptable)
Last name
Phone
My "user model" is:
class Account(auth_models.AbstractBaseUser):
email = models.EmailField(unique = True, db_index = True)
created_on = models.DateField(auto_now_add = True)
person = models.ForeignKey(Person)
def get_full_name(self):
...
def get_short_name(self):
...
objects = AccountManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
and the Person model is:
class Person(models.Model):
name = models.CharField(max_length = 256)
is_staff = models.BooleanField(default = False)
phone = models.CharField(max_length = 16, blank = True)
My custom account "manager" naturally needs to have all these informations, so what I did was:
def create_user(self, email, password, name, phone):
normalized_email = self.normalize_email(email)
person = Person(
name = name,
phone = phone
)
person.save()
account = Account(email = normalized_email, person = person)
account.set_password(password)
account.save()
return account
At creation of course it only asks me about the email and password while completely ignoring the Person model that also needs to be populated.
How can I make it so that all 6 fields are required in the "create super user" step?
To those who are going to question the idea of having Account and Person separated: a single Person can have multiple Accounts associated with it; it's called a "one-to-many relationship" between Person and Account; It's perfectly legal, check it with your lawyer.
I tried to go with adding person to the REQUIRED_FIELDS but that, as specified in the documentation:
Since there is no way to pass model instances during the createsuperuser prompt, expect the user to enter the value of to_field value (the primary_key by default) of an existing instance.
requires a Person ID which I don't have (I need to create the person first).
Specifying:
REQUIRED_FIELDS = ['person.name', ...]
is not supported (the error says person.name is not a field of Account).
You need a method called create_superuser() that should look like this:
def create_superuser(self, email, password, name, phone):
user = self.create_user(
email=email,
password=password,
name = name,
phone = phone
)
user.save(using=self._db)
return user
This way REQUIRED_FIELDS = ['person'] works for me.