In a project requires the user name is autogenerated code. For that, I made a custom user model as described requirements. All is working fine but the problem is when I am trying to create superuser using
python manage.py createsuperuser
As I described the username is an auto-generated field. But the console asking me for the user name.
After creating a superuser account the user code will need to print out as I need code for login along with superuser is created successfully.
How can I gain this functionality? I search on google but I can't find any suitable solution.
Here is my model:
class User(AbstractBaseUser, PermissionsMixin):
"""Custom user model"""
code = models.CharField(max_length=20, unique=True, blank=False)
email = models.EmailField(max_length=255, unique=True, null=True)
name = models.CharField(max_length=255)
address = models.CharField(max_length=255, blank=True)
nid = models.CharField(max_length=30, blank=True)
profile_pic = models.CharField(max_length=255, blank=True)
gender = models.CharField(max_length=10, choices=[('Male', 'Male'), ('Female', 'Female'), ('Other', 'Other')])
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'code'
REQUIRED_FIELDS = ['name']
Here is my Custom User manager:
class UserManager(BaseUserManager):
use_in_migrations = True
def create_user(self, password, **extra_fields):
"""Create new user with auto-generated code and password"""
if 'name' not in extra_fields.keys():
raise ValueError('Name must be needed')
code = code_generator.generate_employee_code(extra_fields['name'][:2])
user = self.model(code=code, **extra_fields)
user.set_password(password)
if 'email' in extra_fields.keys():
email = self.normalize_email(extra_fields['email'])
user.email = email
try:
user.save(using=self._db)
except IntegrityError as e:
raise ValueError("Email has already been used")
return user
def create_superuser(self, name, password):
"""Create and save super user"""
user = self.create_user(password=password, name=name)
user.is_staff = True
user.is_superuser = True
user.is_admin = True
user.save(using=self._db)
return user
This can be achieved by changing the source code, but generally not a good idea.
If you still want to try it out, here you go.
Find this file createsuperuser.py (site-packages->django->contrib->auth->management>commands) and add the code in the right place.
class Command(BaseCommand):
...
...
def handle(self, *args, **options):
...
...
if username:
...
if options['verbosity'] >= 1:
...
self.stdout.write("User name:" + username) # add this code
Again, am not saying that this a good idea, but if am desperate, I would go for this. Also since site-packages is not part of the repository, you will have to do this on each machine you deploy the application.
Related
I am a a newbie in django and currently I am exploring with creating Custom User model subclassing the AbstractBaseUser in django. I wrote the following code for the userModel and BaseUserManager implementation.
from django.db import models
from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import PermissionsMixin
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.base_user import BaseUserManager
from django.utils import timezone
class AccountManager(BaseUserManager):
use_in_migrations = True
def _create_user(self, email, password, **extra_fields):
values = [email]
field_value_map = dict(zip(self.model.REQUIRED_FIELDS, values))
for field_name, value in field_value_map.items():
if not value:
raise ValueError("The {} value must be set".format(field_name))
email = self.normalize_email(email)
extra_fields.setdefault("username", email) #this line of code here
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_user(self, email, password=None, **extra_fields):
extra_fields.setdefault("is_staff", False)
extra_fields.setdefault("is_superuser", False)
extra_fields.setdefault("username", email)
return self._create_user(email, password, **extra_fields)
def create_superuser(self, email, password=None, **extra_fields):
extra_fields.setdefault("is_staff", True)
extra_fields.setdefault("is_superuser", True)
if extra_fields.get("is_staff") is not True:
raise ValueError("Superuser must have is_staff=True.")
if extra_fields.get("is_superuser") is not True:
raise ValueError("Superuser must have is_superuser=True.")
return self._create_user(email, password, **extra_fields)
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_("email address"), unique=True)
username = models.CharField(unique=True, max_length=200)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(default=timezone.now)
is_superuser = models.BooleanField(default=False)
first_name = models.CharField(max_length=200)
last_name = models.CharField(max_length=200)
address = models.CharField(
max_length=200,
)
latitude = models.FloatField(default=0.00, blank=True)
longitude = models.FloatField(default=0.00, blank=True)
dob = models.DateTimeField(null=True, blank=True, default=timezone.now)
phone = models.CharField(max_length=200, blank=True)
phone_activated = models.BooleanField(default=False)
phone_activation_code = models.CharField(max_length=200)
genderChoices = [("Male", "Male"), ("Female", "Female"), ("Other", "Other")]
gender = models.CharField(max_length=200, choices=genderChoices, default="Male")
profile_image = models.ImageField(blank=True)
cover_photo = models.ImageField(blank=True)
facebook_link = models.CharField(max_length=200, blank=True)
twitter_link = models.CharField(max_length=200, blank=True)
instagram_link = models.CharField(max_length=200, blank=True)
youtube_link = models.CharField(max_length=200, blank=True)
linkedin_link = models.CharField(max_length=200, blank=True)
sendEmail = models.BooleanField(default=True)
sendSMS = models.BooleanField(default=True)
activation = models.CharField(max_length=200)
objects = AccountManager()
USERNAME_FIELD = "email"
REQUIRED_FIELDS = []
def __str__(self):
return self.email
I want to have a username field that could be later edited to a custom one, but at the time of creation equals to the email registered with.
Now when I create a superuser from cli all things go fine and I get what I want. But when I create a user with the django admin, the AccountManager class seem like non existent and none of the code here is implemented. i.e username field is stored as empty the first time and gives a non-unique integrity error the other time(of course). Why is the create_user method not called while creating user from django admin??
How can I make it right??
I have of course updated the AUTH_USER_MODEL and have registered the class in admin.py properly. (I'm using django 3.0.10)
As per KenWhitesell in django forums:
The reason that the createsuperuser cli command works is because it specifically calls the create_superuser method on the UserModel.
This is the line from createsuperuser.py:
self.UserModel._default_manager.db_manager(database).create_superuser(**user_data)
However, the Admin facility is designed to be generic, and work with every model. It’s not going to perform actions outside what it can expect to have implemented for every model.
When you look at the docs for managers in the section for adding extra manager methods, it reads in part:
Adding extra Manager methods is the preferred way to add “table-level” functionality to your models. (For “row-level” functionality – i.e., functions that act on a single instance of a model object – use Model methods, not custom Manager methods.)
Since what you’re doing here is a row-level action, a manager method doesn’t apply. To do what you’re trying to do, you may want to override the save method on the model.
Ken
So basically overriding the save method in the CustomModel itself solves the problem.
def save(self, *args, **kwargs):
self.username = self.email
super(CustomUser, self).save(*args, **kwargs)
I'm working on a project using Python(3.7) and Django(2.2) in which I have to implement multiple types of users as:
Personal Account - below 18
Personal Account - above 18
Parent Account
Coach Account
Admin
along with that, I also need to use email as the username field for login/authentication.
The strategy I'm trying to use is to build a custom base model as User inherited from AbstractBaseUser and also created a custom User Manager to make the email as username but it's not working.
Here's my complete model code:
class UserManager(BaseUserManager):
def _create_user(self, email, password, is_staff, is_superuser, **extra_fields):
if not email:
raise ValueError('Users must have an email address')
now = timezone.now()
email = self.normalize_email(email)
user = self.model(
email=email,
is_staff=is_staff,
is_active=True,
is_superuser=is_superuser,
last_login=now,
date_joined=now,
**extra_fields
)
user.set_password(password)
user.save(using=self._db)
return user
def create_user(self, email=None, password=None, **extra_fields):
return self._create_user(email, password, False, False, **extra_fields)
def create_superuser(self, email, password, **extra_fields):
user = self._create_user(email, password, True, True, **extra_fields)
user.save(using=self._db)
return user
def generate_cid():
customer_number = "".join([random.choice(string.digits) for i in range(10)])
return customer_number
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=255, unique=True)
is_personal_above_18 = models.BooleanField(default=False)
is_personal_below_18 = models.BooleanField(default=False)
is_parent = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
last_login = models.DateTimeField(null=True, blank=True)
date_joined = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = 'email'
EMAIL_FIELD = 'email'
REQUIRED_FIELDS = []
objects = UserManager()
def get_absolute_url(self):
return "/users/%i/" % self.pk
def get_email(self):
return self.email
class PersonalAccountAbove18(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE,
primary_key=True, related_name='profile')
customer_id = models.BigIntegerField(default=generate_cid)
class PersonalAccountBelow18(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE,
primary_key=True, related_name='profile')
customer_id = models.BigIntegerField(blank=False)
class ParentAccount(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE,
primary_key=True, related_name='profile')
customer_id = models.BigIntegerField(default=generate_cid)
I'm confused about my approach and even it's also return an error when I run makemigrations as:
users.User.user_permissions: (fields.E304) Reverse accessor for 'User.user_permissions' clashes with reverse accessor for 'User.user_permissions'.
HINT: Add or change a related_name argument to the definition for 'User.user_permissions' or 'User.user_permissions'.
Update:
I removed the PermissionMixin and related_name attributes from child models and migrations are running now but it still require the username instead of email.
The errors in makemigrations, and later (after dropping PermissionsMixin) requiring username instead of email for authentication are hints that you have not set your custom model as the default user model to be used by the Django auth app. As a result, Django is using the auth.User model as the default user model and your one being added to the project like any other model. So both of the user models exist simultaneously with the auth.User being the default/active one used for authentication purposes.
In essence, edit your settings.py to add the following:
AUTH_USER_MODEL = '<your_app>.User'
Now Django will use your customized User model instead of the one from auth app (the default) and the auth.User model will be dropped. The auth app uses the get_user_model (django.contrib.auth.get_user_model) function to get the currently active user model for the project which checks for settings.AUTH_USER_MODEL, and by default the rest of the system (e.g. the admin app) also checks for this setting to get the current user model. So as long as you're using the auth app the above should suffice.
For using email as login, I recommend the django-allauth package. It takes care of all the heavy lifting and you can use it to login with email instead of username. Will Vincent has a write up on this at:
https://wsvincent.com/django-allauth-tutorial-custom-user-model/
Will also has a good write up on creating custom user models at:
https://wsvincent.com/django-custom-user-model-tutorial/
In short, he recommends subclassing AbstractUser. Here is an example from one of my projects where a user collects points throughout their journey on the site and I must record these points.
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
points = models.IntegerField(default=0)
def user_id(self):
return self.id.__str__()
I am trying to implement the custom User model and custom authentication for my application.
I am able to create models and make migrations. But when I created a form and implemented the login template, I am getting this error -
login
User model with this Login already exists.
View where I am authenticating user -
from django.shortcuts import render_to_response, redirect, render
from django.template import RequestContext
from django.contrib.auth import login, logout , authenticate
from accounts.forms import AuthenticationForm
from django.contrib.auth.decorators import login_required
def accounts_login(request):
context = {}
if request.method == "POST":
form = AuthenticationForm(request.POST) #getting error here
print(form)
if form.is_valid():
user = authenticate(login = request.POST["login"], password = request.POST["password"])
if user is not None:
print(user)
login(request,user)
next_url = request.POST["next"]
return redirect(next_url, args=(), kwargs={})
#more code - not required here. Let me know if needed.
Authentication form:
from django import forms
from accounts.models import UserModel
class AuthenticationForm(forms.Form):
login = forms.CharField(max_length=128, required=True)
password = forms.CharField(max_length=128, required=True)
My custom user model and user manager -
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from django.utils import timezone
from django.db.models import Max
class MyUserManager(BaseUserManager):
use_in_migrations = True
def create_user(self,login, parent_type, last_name, first_name, password):
return create_superuser(self,login, parent_type, last_name, first_name, password)
def create_superuser(self,login, parent_type, last_name, first_name, password):
maxx = self.model.objects.all().aggregate(Max('sys_id'))
print(maxx)
user = self.model(
sys_id = maxx["sys_id__max"] + 1,
login = login,
password = password,
parent_type = parent_type,
last_name = last_name,
first_name = first_name,
display_name = last_name + " " + first_name,
created_when = timezone.now()
)
user.save(using=self._db)
# no difference here...actually can set is_admin = True or something like that.
return user
class UserModel(AbstractBaseUser):
# custom user class
SYSTEM = 0
TENANT = 1
parent_type_choices = (
(SYSTEM, 'System'),
(TENANT, 'Tenant')
)
sys_id = models.BigIntegerField(primary_key=True, blank=True)
parent_type = models.PositiveIntegerField(choices=parent_type_choices, null=False, blank=False)
parent_sys_id = models.ForeignKey('tenant.TenantModel', on_delete = models.SET_NULL, null=True, blank=True)
last_name = models.CharField(null=False, blank=False, max_length=40)
first_name = models.CharField(max_length=40, null=False, blank=False)
display_name = models.CharField(max_length=80, unique=True, null=False, blank=True)
login = models.CharField(max_length=40, unique=True, null=False, blank=False)
authentication_method = models.CharField(max_length=80, null=True, blank=True)
access_valid_start = models.DateTimeField(null=True, blank=True)
access_valid_end = models.DateTimeField(null=True, blank=True)
created_when = models.DateTimeField(null=True, blank=True, )
created_by = models.BigIntegerField(null=True, blank=True)
last_updated_when = models.DateTimeField(null=True, blank=True)
last_updated_by = models.BigIntegerField(null=True, blank=True)
notes = models.CharField(max_length=2048, null=True, blank=True)
is_active = models.BooleanField(default=True)
objects = MyUserManager()
USERNAME_FIELD = "login"
# REQUIRED_FIELDS must contain all required fields on your User model,
# but should not contain the USERNAME_FIELD or password as these fields will always be prompted for.
REQUIRED_FIELDS = ['parent_type', 'last_name', 'first_name']
class Meta:
app_label = "accounts"
db_table = "Users"
def __str__(self):
return self.display_name
def get_full_name(self):
return self.display_name
def get_short_name(self):
return self.last_name
def check_password(self,password):
return True
if self.password ==password:
return true
Custom backend
from django.conf import settings
from accounts.models import UserModel
class MyAuthBackend(object):
def authenticate(self, login, password):
try:
user = UserModel.objects.get(login=login)
if user.check_password(password):
return user
else:
print("wrong password")
return None
except User.DoesNotExist:
return None
except Exception as e:
print(repr(e))
return None
def get_user(self, user_id):
try:
user = User.objects.get(pk=user_id)
if user.is_active:
return user
return None
except User.DoesNotExist:
return None
Added these two variables in setting.py
AUTH_USER_MODEL = 'accounts.UserModel'
AUTHENTICATION_BACKENDS = ('accounts.backends.MyAuthBackend',)
Getting backend from command line
>>> get_backends()
[<accounts.backends.MyAuthBackend object at 0x7f2c438afe80>]
A user with username lets say xyz and password 'ABC' exists in db.
1. When I am trying to login with existing username and password it throws me error User model with this Login already exists.
2. When I try to login with non-existing username and password (lets say xyz123) it throws me error authenticate() takes 0 positional arguments but 2 were given
I followed multiple articles but mainly django official documentation for this. Read multiple articles to solve the issue but failed.
Please suggest what I am doing wrong.
Update 1: chnged the AuthenticationForm from ModelForm to normal Form class. Error 1 is solved.
Update 2: Not using my custom backend as pointed by Daniel, it is useless because I am not doing anything new in it. Hence error 2 is also solved.
Update 3: Now hashing password before saving user to db in create super user function. user.set_password(password)
Firstly, you are storing passwords in plain text in your overwritten create_superuser method. You must absolutely not do this. Quite apart from the security issues, it won't actually work, as the authentication backend will hash a submitted password before comparing it with the saved version, so it will never match.
Secondly, your actual problem is that your AuthenticationForm is a ModelForm; as such it will attempt to see if it can create a user with that email and password. There is no reason to make it a modelform, you should just have a plain form with CharFields for email and password.
Note though that you won't actually be able to log in, until you have fixed the plain text password issue. Note also that your custom auth backend - as well as breaking the authenticate method by not conforming to the interface described in the docs - does not do anything that the built-in one does not do; it is pointless and you should remove it.
I created an app authentication, code for models is,
from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import BaseUserManager
from django.db import models
class AccountManager(BaseUserManager):
def create_user(self, email, password=None, **kwargs):
if not email:
raise ValueError('Users must have a valid email address.')
account = self.model(email=self.normalize_email(email))
account.set_password(password)
account.save()
return account
def create_superuser(self, email, password, **kwargs):
account = self.create_user(email, password, **kwargs)
account.is_admin = True
account.save()
return account
class Account(AbstractBaseUser):
email = models.EmailField(unique=True)
# username = models.CharField(max_length=40, unique=True)
first_name = models.CharField(max_length=40, blank=True)
last_name = models.CharField(max_length=40, blank=True)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
objects = AccountManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
def __unicode__(self):
return self.email
def get_full_name(self):
return ' '.join([self.first_name, self.last_name])
def get_short_name(self):
return self.first_name
apart from this the changes that i did was added authentication in INSTALLED_APPS and in settings added AUTH_USER_MODEL = 'authentication.Account'
When i create a super-user from shell, its working and inserting in db, but when i try to login into the django admin console,
initially the error was is_staff not found, which i added. Now it is showing invalid username and password for all. I tried that a couple of time generating new one each time. What am i doing wrong ?
EDIT
I am adding the dbs dump that i used to validate if the user was created
sqlite> select * from authentication_account;
1|pbkdf2_sha256$24000$ZRHCXpUI3w0G$fh4z9y5vmYAZ0FEiDr908dJD3ezw62nm3lpkZxUi/Es=||me#rahulbhola.in|||1|0|2016-05-28 21:15:59.292988|2016-05-28 21:15:59.415382
Still login in django defaut admin does not work
You added the is_staff later on which seems to be false by default, so when you migrated the field was created in database with the value false in the is_staff field.
Please make the changes as below and try
class AccountManager(BaseUserManager):
def create_user(self, email, password=None, **kwargs):
if not email:
raise ValueError('Users must have a valid email address.')
account = self.model(email=self.normalize_email(email))
account.set_password(password)
account.save(using=self.db)
return account
def create_superuser(self, email, password, **kwargs):
account = self.create_user(email, password, **kwargs)
account.is_staff = True
account.is_admin = True
account.save(using=self.db)
return account
Please not account.save(using=self.db) in the code above and account.is_staff = True in the create admin method.
Please try creating the new user again i prefer using the shell ( python manage.py shell)
from authentication.models import Account
user = Account()
user.create_superuser(username='some_username', password='some_password')
user.save()
Now that the user is created please try logging in using the django admin.
This should work. I had similar issues which is fixed and i am able to create user and login from django admin.
I am assuming that you have created the custom authentication backend already, if not please create
Hope this helps you.
What is the best (easiest?) way to make it a requirement to have an email only for user registration and not make username a requirement in the django-rest-auth framework?
Would I need to write a new User Serializer, for example? Is there a flag setting that I can set True or False to turn off or on this requirement instead?
Thanks
You'll need to write your own AbstractUser and BaseUserManager classes. Fortunately it's pretty simple, add something like this to your app's models:
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from django.db import models
class Account(AbstractBaseUser):
email = models.EmailField(unique=True)
username = models.CharField(max_length=40, unique=True, blank=True)
first_name = models.CharField(max_length=40, blank=True)
last_name = models.CharField(max_length=40, blank=True)
is_admin = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
objects = AccountManager()
# This tells Django that this field is absolutely important...
USERNAME_FIELD = 'email'
# ...and username is now optional because it doesn't show up here!
REQUIRED_FIELDS = []
def __unicode__(self):
return self.email
def get_full_name(self):
return ' '.join([self.first_name, self.last_name])
def get_short_name(self):
return self.first_name
class AccountManager(BaseUserManager):
def create_user(self, email, password=None):
if not email:
raise ValueError('User must provide an e-mail address')
account = self.model(
email=self.normalize_email(email)
)
account.set_password(password)
account.save()
return account
def create_superuser(self, email, password=None):
account = self.create_user(email, password)
account.is_admin = True
account.save()
return account
Next, tell Django that this model is your project's new User class. Add the following to your settings.py file:
AUTH_USER_MODEL = 'apiapp.Account'
Once that's done, just migrate:
python manage.py makemigrations
python manage.py migrate
From that point on new users will be represented by this new model (you'd have to handle migrations manually), including anywhere you might use self.request.user!