Creating users in data migrations is discouraged as doing so represents a potential security risk, as passwords are stored in plaintext in the migration. However, doing so in proof-of-concepts or in special cases may be necessary, and the steps below will demonstrate how to create and remove new users in a Django data migration.
The django-improved-user
package intentionally disallows use of UserManager
in data migrations (we forgo the use of model managers in migrations). The create_user()
and create_superuser()
methods are thus both unavailable when using data migrations. Both of these methods rely on User
model methods which are unavailable in Historical models, so we could not use them even if we wanted to (short of refactoring large parts of code currently inherited by Django).
We therefore rely on the standard Manager
, and supplement the password creation behavior.
In an existing Django project, you will start by creating a new and empty migration file. Replace APP_NAME
in the command below with the name of the app for which you wish to create a migration.
$ python manage.py makemigrations --empty --name=add_user APP_NAME
We start by importing the necessary tools
from django.conf import settings from django.contrib.auth.hashers import make_password from django.db import migrations
We will use RunPython
to run our code. RunPython
expects two functions with specific parameters. Our first function creates a new user.
def add_user(apps, schema_editor): User = apps.get_model(*settings.AUTH_USER_MODEL.split(".")) User.objects.create( email="migrated@jambonsw.com", password=make_password("s3cr3tp4ssw0rd!"), short_name="Migrated", full_name="Migrated Improved User", )
NB: Due to the lack of UserManager
or User
methods, the email
field is not validated or normalized. What’s more, the password
field is not validated against the project’s password validators. It is up to the developer coding the migration file to provide proper values.
The second function is technically optional, but providing one makes our lives easier and is considered best-practice. This function undoes the first, and deletes the user we created.
def remove_user(apps, schema_editor): User = apps.get_model(*settings.AUTH_USER_MODEL.split(".")) User.objects.get(email="migrated@jambonsw.com").delete()
Finally, we use our migration functions via RunPython
in a django.db.migrations.Migration
subclass. Please note the addition of the dependency below. If your file already had a dependency, please add the tuple below, but do not remove the existing tuple(s).
class Migration(migrations.Migration): dependencies = [ ("improved_user", "0001_initial"), ] operations = [ migrations.RunPython(add_user, remove_user), ]
The final migration file is printed in totality below.
1from django.conf import settings 2from django.contrib.auth.hashers import make_password 3from django.db import migrations 4 5 6def add_user(apps, schema_editor): 7 User = apps.get_model(*settings.AUTH_USER_MODEL.split(".")) 8 User.objects.create( 9 email="migrated@jambonsw.com", 10 password=make_password("s3cr3tp4ssw0rd!"), 11 short_name="Migrated", 12 full_name="Migrated Improved User", 13 ) 14 15 16def remove_user(apps, schema_editor): 17 User = apps.get_model(*settings.AUTH_USER_MODEL.split(".")) 18 User.objects.get(email="migrated@jambonsw.com").delete() 19 20 21class Migration(migrations.Migration): 22 23 dependencies = [ 24 ("improved_user", "0001_initial"), 25 ] 26 27 operations = [ 28 migrations.RunPython(add_user, remove_user), 29 ]
You may wish to read more about Django Data Migrations and RunPython
.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4