Django: Create a superuser in a data migration - python

Goal: automatically creating a superuser
I'm trying to create a default user, specifically a superuser, in an early data migration, so whenever my Django application is run in my Docker container, it already has a superuser with which I can access the admin site.
I had already tried different options for creating said superuser, and although I have some functioning ones (based on the command parameter of my docker-compose file), I've seen when adding initial data to a Django project, the best practice is to do it through a Data Migration.
My custom user
In my Django project I've extended the AbstactBaseUser so I can change the default username field requirement for the email field. My User is as such:
class UserManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
# Implementation...
def create_superuser(self, email, password, **extra_fields):
# Implementation...
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(
verbose_name="email address",
max_length=255,
unique=True,
)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = "email"
REQUIRED_FIELDS = []
def __str__(self):
return self.email
Failed attempts
By following the Django documentation here I tried making my superuser in a data migration with the following code, located in a file called 0002_data_superuser in the migrations folder of my app:
def generate_superuser(apps, schema_editor):
User = apps.get_model("users.User")
User.objects.create_superuser(
email=settings.DJANGO_SUPERUSER_EMAIL,
password=settings.DJANGO_SUPERUSER_PASSWORD,
)
print("\nInitial superuser created\n")
class Migration(migrations.Migration):
dependencies = [
('users', '0001_initial'),
]
operations = [
migrations.RunPython(generate_superuser)
]
When running my docker-compose, however, I run into this error:
AttributeError: 'Manager' object has no attribute 'create_superuser'
I've tried debugging by printing the Manager and, indeed, it does not have the create_superuser necessary for this. My next thought was to try to reproduce what create_superuser does myself, but I find it quite impossible given that a lot of methods for managing passwords, hashing, normalizing emails and stuff are not available to me.
I gather from all of this that the problem is that the Manager, for some reason, is not available during migrations, and I'd like to know if there's a solution to this.

Add use_in_migrations = True to your custom manager to make it available during migrations
class UserManager(BaseUserManager):
use_in_migrations = True
...
Then you should be able to use User.objects.create_superuser in your data migration
Be careful though, there are some implications for doing this here in the docs

Reproducing create_user functionality manually
Checking out a bit more the source code of Django I found at there is a way to reproduce the functionality from create_superuser. By importing BaseUserManager and the classmethod make_password from django.contrib.auth.hashers, I came up with this:
from django.contrib.auth.models import BaseUserManager
from django.contrib.auth.hashers import make_password
def generate_superuser(apps, schema_editor):
User = apps.get_model("users.User")
email = settings.DJANGO_SUPERUSER_EMAIL
password = settings.DJANGO_SUPERUSER_PASSWORD
user = User()
user.email = BaseUserManager.normalize_email(email)
user.password = make_password(password)
user.is_staff = True
user.is_superuser = True
user.save()
which does the trick.
Still, I don't like it much as a solution given that it requires reeimplementing an already existing Django method that just is not accessible at this point of the program.

The Django documentation provides clear guide on doing something similar Data Migrations.
First create a empty migration in your user app
python manage.py makemigrations --empty yourappname
This will create a empty migration file, then
import logging
import environ
from django.db import migrations
logger = logging.getLogger(__name__)
def generate_superuser(apps, schema_editor):
from django.contrib.auth import get_user_model
env = environ.Env()
USERNAME = env.str("ADMIN_USERNAME")
PASSWORD = env.str("ADMIN_PASSWORD")
EMAIL = env.str("ADMIN_EMAIL")
user = get_user_model()
if not user.objects.filter(username=USERNAME, email=EMAIL).exists():
logger.info("Creating new superuser")
admin = user.objects.create_superuser(
username=USERNAME, password=PASSWORD, email=EMAIL
)
admin.save()
else:
logger.info("Superuser already created!")
class Migration(migrations.Migration):
dependencies = [("users", "0009_user_full_name")]
operations = [migrations.RunPython(generate_superuser)]
This workflow works well with Docker, once python migrate gets run this will generate a new user.

Related

How to authenticate user form credentials from PostgreSQL with Django

I am able to register users to my custom table in the Postgres database. I would like to know how can I use the credentials from my Postgres table to authenticate login from my django web app. I know I have to create a custom backend but I don't know how we can import the credentials from Postgres to authenticate against the credentials that user entered.
My model:
from django.conf import settings
from django.db import models
from django.contrib.auth.models import User
from django.contrib.auth.models import AbstractBaseUser
# Create your models here.
# Database model Users has one to one relationship with Django User table
from django.db.models.signals import post_save
from django.dispatch import receiver
class Profile(AbstractBaseUser):
first_name = models.TextField()
last_name = models.TextField()
user = models.OneToOneField(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
user_name = models.TextField(unique=True)
email = models.TextField()
password1 = models.TextField()
password2 = models.TextField()
date_created = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = 'user_name'
REQUIRED_FIELDS = []
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def update_profile_signal(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
I have my django project pointing towards my custom user model too.
I just don't know how to create a custom backend for authentication. If I could be guided towards it, I would really appreciate.
If you set the password for each user when creating a new user, the default authentication backend for Django will work just fine for you.
When saving a new Profile you should just do a instance.set_password(password) after validating if password and password2 are the same.
Also I would recommend not using receiver but rather maintaining a CustomManager for your table.
For now this should do the trick for you:
#receiver(post_save, sender=User)
def update_profile_signal(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
if password == password2:
instance.set_password(password)
instance.profile.save()
Then you can limit access to Views using the #login_required decorator. Some other ways are mentioned here

How to create an authentication system in django for a SQLServer database?

I am recently working in a django project with a SQLServer database. I already connected the database with SQLServer, and I want to make an authentication system for a table I have in that database.
I know django comes with a built-in authentication system, but there is no way to tell django to use a specific table in the database to make the authentication, it just seems to look for users in the default admin page.
Is there any way for django to look for data inside a specific table in a SQLServer database and validate the information put by an user?
You can do it by implementing you own user model and then telling django how to authenticate the user. Your model should look something like this:
class Users(models.Model):
id = models.IntegerField(primary_key=True)
is_active = models.IntegerField(default=1)
date_joined = models.DateTimeField(default=timezone.now)
last_login = models.DateTimeField(default=timezone.now)
username = models.CharField(max_length=30, unique=True)
password = models.CharField(max_length=30)
#property
def is_authenticated(self):
return True
You can add extra fields, but those are required by django. The property is_authenticated shall always return true, as defined in the documentation.
Next step is to define how will your login authenticate. Create a file name backends.py anywhere in your project and inside of it declared two methods: authenticate and get_user, it should something like this:
from django.contrib.auth.backends import ModelBackend
from users.models import Users
from django.contrib.auth.hashers import *
from login.util import *
class PersonalizedLoginBackend(ModelBackend):
def authenticate(self, request=None, username=None, password=None, **kwars):
#Here define you login criteria, like encrypting the password and then
#Checking it matches. This is an example:
try:
user = Users.objects.get(username=username)
except Users.DoesNotExist:
return None
if check_password(password, user.password):
return user
else:
return None
def get_user(self, user_id):
#This shall return the user given the id
from django.contrib.auth.models import AnonymousUser
try:
user = Users.objects.get(id=user_id)
except Exception as e:
user = AnonymousUser()
return user
Now you need to tell django that where is the backends located, on your settings.py:
AUTHENTICATION_BACKENDS = (
# ... your other backends
'path.to.backends.PersonalizedLoginBackend',
)
From there you should be able to do your login like normal, first authenticate and then use do_login function.
Read more details in here:
https://docs.djangoproject.com/en/2.2/topics/auth/customizing/

Problems extending my Django user model

I have a simple django app, and am trying to set up a custom Django User Model so that I can have users log in with their email field. I think I set everything up well enough, its all fairly straightforward, however when trying to actually run the migrations I get this error:
django.db.migrations.exceptions.InconsistentMigrationHistory: Migration admin.0001_initial is applied before its dependency accounts.0001_initial
Seems weird?
I'm having trouble understanding why it's having trouble understanding.
Heres the User model Im implementing
from __future__ import unicode_literals
from django.db import models
from django.core.mail import send_mail
from django.contrib.auth.models import PermissionsMixin
from django.contrib.auth.base_user import AbstractBaseUser
from django.utils.translation import ugettext_lazy as _
from .managers import UserManager
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('email address'), unique=True)
first_name = models.CharField(_('first name'), max_length=30, blank=True)
last_name = models.CharField(_('last name'), max_length=30, blank=True)
display_name = models.CharField(_('display name'), max_length=30, blank=True)
bio = models.TextField(blank=True)
date_joined = models.DateTimeField(_('date joined'), auto_now_add=True)
is_active = models.BooleanField(_('active'), default=True)
avatar = models.ImageField(upload_to='avatars/', null=True, blank=True)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
class Meta:
verbose_name = _('user')
verbose_name_plural = _('users')
def get_full_name(self):
'''
Returns the first_name plus the last_name, with a space in between.
'''
full_name = '%s %s' % (self.first_name, self.last_name)
return full_name.strip()
def get_short_name(self):
'''
Returns the short name for the user.
'''
return self.first_name
def email_user(self, subject, message, from_email=None, **kwargs):
'''
Sends an email to this User.
'''
send_mail(subject, message, from_email, [self.email], **kwargs)
"Using a custom user model when starting a project"
If you’re starting a new project, it’s highly recommended to set up a custom user model, even if the default User model is sufficient for you. This model behaves identically to the default user model, but you’ll be able to customize it in the future if the need arises." reference->django docs
Then you have to specify to setting.py that you are using a new user model (again before you migrate for the first time.)
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
pass
# in settings.py
AUTH_USER_MODEL = 'myapp.MyUser'
I highly recommend checking out the link above.
Now to solve your problem you can delete your migrations in your app path under the migrations directory and reset your database and follow the instructions above.
Changing user model mid project is difficult in django and requires manually editing your schema so since your project is for educational purposes just reset it
Basically, you have already created some User accounts or at least have created superuser account. Since you are changing the User model after I assume you have already made migrations at least once, Django has conflict with old user model with the new one.
Solution is to delete all accounts in admin including superuser. Then delete your migrations files in migrations folder. And also I think delete your sqlite data file. Run migrations again and it should work.
You should have created custom user model before any migration.
One solution is to truncate the django_migrations table.
To do this you have to:
login to your database (In case you use Mysql) using mysql -u root -p
use your_db_name;
SET FOREIGN_KEY_CHECKS=0; to prevent relation errors
DELETE FROM django_migrations; truncates the django_migrations table
SET FOREIGN_KEY_CHECKS=1;
Then you can run your migrations!
Another solution is to run these commands before your migration to avoid migrations error:
python manage.py migrate admin zero --fake
python manage.py migrate auth zero --fake
python manage.py migrate contenttypes zero --fake
python manage.py migrate sessions zero --fake
Warning :
This solution may leave you with a database schema that's
inconsistent with the migrations applied, so only do this if
you know what you're doing. Thanks to #AKX
You can also use this solution too!

How to use user profiles with MongoEngine (Django)?

So, I'm trying to build up a simple site with a MongoDB database and Django (using MongoEngine), but I am stuck trying to understand how the user profiles work. I can save the mongo_auth.MongoUser document to the database just fine, but when it comes down to actually saving the profile, I'm not doing the right thing. Here is the User Profile model/document I am trying to setup with the MongoUser:
from mongoengine.django.auth import User
from mongoengine import *
[...]
class UserProfile(Document):
user = EmbeddedDocumentField('User')
telephone = models.CharField(max_length=30,null=True,blank=True)
address = models.CharField(max_length=100, null=True, blank=True)
birthdate = models.DateField(null=True, blank=True)
def create_user_profile(sender, instance, created, **kwargs):
if created:
profile, created = UserProfile.objects.get_or_create(user=instance)
post_save.connect(create_user_profile, sender=User)
In Django with relational databases, the UserProfile was a models.Model and the user field was a OneToOne relationship, but I don't know how to map that with the MongoUser, so I guessed the following lines:
class UserProfile(Document):
user = EmbeddedDocumentField('User')
But, apparently, it's not right since Django can't load the profile:
Unable to load the profile model, check AUTH_PROFILE_MODULE in your project settings
I think my settings.py is configured correctly since I can save a MongoUser just fine (without a profile), and the following line tells Django where to find the UserProfile:
AUTH_PROFILE_MODULE = 'comptes.UserProfile'
I am still new to Django and MongoDB, so all help will be greatly appreciated.
Thanks!
You must use mongoengine.fields.ReferenceField. Something like this
class UserProfile(Document):
user = ReferenceField('User')

Django custom user model and usermanager

i'm building a web application with Django 1.5. I'm using a custom User model with a custom UserManager.
I followed the instructions and examples of the official Django documentation.
Now, when i'm trying to create a new user via UserManager.create_user(...) i'm getting a NoneType error: It seems the UserManager's attribute models is of type None.
I think i'm setting up the UserManager correctly in the User model ( objects = UserManager() )
I really don't know where i'm making a mistake. Booth my coding partners and i are new to Django. Maybe you can help us out.
Here is the code:
class UserManager(BaseUserManager):
"""
create a new user
#param username: the name for the new user
#param password: the password for the new user. if none is provided a random password is generated
#param person: the corresponding person object for this user
"""
def create_user(self, username, person, password=None):
if not username:
raise ValueError('User must have a valid username')
user = self.model(username=username, created=datetime.now(), must_change_password=True, deleted=False, person=person)
user.set_password(password)
user.save(using=self._db)
return user
class User(AbstractBaseUser):
## the id of the user. unique through the application
user_id = models.AutoField(primary_key=True)
## the name of the user. unique through the application
username = models.CharField(max_length=32, unique=True)
## the date when the user was created
created = models.DateTimeField()
## iff this is true the user must set a new password at next login
must_change_password = models.BooleanField(default=True)
## iff true the user is marked as deleted and can not login
deleted = models.BooleanField(default=False)
## iff true the user is admin and has all permissions. use with care!
is_admin = models.BooleanField(default=False)
## reference to the person entity that is linked to this specific user
person = models.ForeignKey(Person)
## indicates if the user is active or not
active = models.BooleanField(default=True)
## define the user manager class for User
objects = UserManager()
# necessary to use the django authentication framework: this field is used as username
USERNAME_FIELD = 'username'
I'm getting the NoneType Error at line user = self.model(..) in the create_user() method in the UserManager
To create new user you shouldn't call UserManager.create_user(...). Instead you should use :
from django.contrib.auth import get_user_model
get_user_model().objects.create_user(...)
This is how django managers work. You can read docs here
I also had problems saving the custom user model and it took me while to figure it our
I think the important line in your code is:
objects = UserManager()
within the User class, so in order to save the new user you need to call
new_user=User.objects.create_user(args, args, args, etc)
the "objects" is the item that calls the UserManager class and is called a manager in django
Important caveat to update the solutions...
If you're facing this kind of problem, you've probably tried various solutions around the web telling you to add AUTH_USER_MODEL = users.CustomUser to settings.py and then to add the following code to views.py forms.py and any other file that calls User:
from django.contrib.auth import get_user_model
User = get_user_model()
And then you scratch your head when you get the error:
Manager isn't available; 'auth.User' has been swapped for 'users.User'
Anytime your code references User such as:
User.objects.get()
Cause you know you already put objects = UserManager() in your custom user class (UserManager being the name of your custom manager that extends BaseUserManager).
Well as it turns out (thank you to #Aldarund) doing:
User = get_user_model() # somewhere at the top of your .py file
# followed by
User.objects.get() # in a function/method of that same file
Is NOT equivalent to:
get_user_model().objects.get() # without the need for User = get_user_model() anywhere
Perhaps not intuitive, but it turns out that that in python, executing User = get_user_model() once at the time of import does not then result in User being defined across subsequent calls (i.e. it does not turn User into a "constant" of sorts which you might expect if you're coming from a C/C++ background; meaning that the execution of User = get_user_model() occurs at the time of imports, but is then de-referenced before subsequent called to class or function/method in that file).
So to sum up, in all files that reference the User class (e.g. calling functions or variables such as User.objects.get() User.objects.all() User.DoesNotExist etc...):
# Add the following import line
from django.contrib.auth import get_user_model
# Replace all references to User with get_user_model() such as...
user = get_user_model().objects.get(pk=uid)
# instead of user = User.objects.get(pk=uid)
# or
queryset = get_user_model().objects.all()
# instead of queryset = User.objects.all()
# etc...
Hope this helps save others some time...
I had to add an answer as I don't have enough rep to comment. But the link in #Aldarund's answer does not describe the use of get_user_model() at all. However, this link should help...
In my case if you got "manager_method" missing 1 required positional argument: 'self' django err you should notice that
unlike model.Manager the UserManager need to set as blow
from django.contrib.auth.models import AbstractUser, UserManager
from django.db.models import Q
class CostumUserManager(UserManager ):
def authors(self):
return self.filter(Q(is_author=True) | Q(is_superuser=True))
class User(AbstractUser):
is_author = models.BooleanField(default=False, )
objects = CostumUserManager() # Use () in end of it

Categories

Resources