Create django super user in a docker container wit

2019-01-25 09:29发布

问题:

I am tring to createsuperuser in a django docker container with fabric.

To create the super user in django, I need run this in a django interactive mode:

./manage.py createsuperuser

And because I want to make it run in a fabric script, so I find this command could avoid inputing password

echo "from django.contrib.auth.models import User; User.objects.create_superuser('admin', 'admin@example.com', 'pass')" | ./manage.py shell

Then I put this together with "docker exec" to run it in my django container

docker exec container_django echo "from django.contrib.auth.models import User; User.objects.create_superuser('admin', 'admin@example.com', 'pass')" | ./manage.py shell

The problem comes out with the linux pipe, the pipe(|) all the contents on its left(including the docker exec) to its right(./manage.py shell)

And this is not only difficult part, considering to put all these junks into a fabric run, which means they need quotes on both end. It will make whole thing very urgly.

fabric run:
run("docker exec container_django {command to create django super user}")

I am still struggling on how to make at least the junk work in a fabric run, but I don't know how to do it.

回答1:

I handle this by evaluating the python code line in Dockerfile.

ENV DJANGO_DB_NAME=default
ENV DJANGO_SU_NAME=admin
ENV DJANGO_SU_EMAIL=admin@my.company
ENV DJANGO_SU_PASSWORD=mypass

RUN python -c "import django; django.setup(); \
   from django.contrib.auth.management.commands.createsuperuser import get_user_model; \
   get_user_model()._default_manager.db_manager('$DJANGO_DB_NAME').create_superuser( \
   username='$DJANGO_SU_NAME', \
   email='$DJANGO_SU_EMAIL', \
   password='$DJANGO_SU_PASSWORD')"

Note that this is different from calling

User.objects.create_superuser('admin', 'admin@example.com', 'pass')

as django.contrib.auth.get_user_model will work fine with custom user model if you should have any (which is quite common), while with User.objects.create you only create a standard user entity, ignoring any custom user model.

Also, it's the same call that django's createsuperuser command does under the hood, so it should be pretty safe to do.



回答2:

I recommend adding a new management command that will automatically create a superuser if no Users exist.

See small example I created at https://github.com/dkarchmer/aws-eb-docker-django. In particular, see how I have a python manage.py initadmin which runs:

class Command(BaseCommand):

    def handle(self, *args, **options):
        if Account.objects.count() == 0:
            for user in settings.ADMINS:
                username = user[0].replace(' ', '')
                email = user[1]
                password = 'admin'
                print('Creating account for %s (%s)' % (username, email))
                admin = Account.objects.create_superuser(email=email, username=username, password=password)
                admin.is_active = True
                admin.is_admin = True
                admin.save()
        else:
            print('Admin accounts can only be initialized if no Accounts exist')

(See Authentication/management/commands).

You can see how the Dockerfile then just runs CMD to runserver.sh which basically runs

python manage.py migrate --noinput
python manage.py initadmin
python manage.py runserver 0.0.0.0:8080

Obviously, this assumes the Admins immediately go change their passwords after the server is up. That may or may not be good enough for you.



回答3:

Get the container ID and run the command.

docker exec -it container_id python manage.py createsuperuser


回答4:

Might be easiest to just put together a Python script to create the Django superuser for you, instead of trying to feed all those commands through manage.py shell. Can you put your commands in a .py file, let's say yourfile.py:

#!/usr/bin/env python

from django.contrib.auth.models import User
User.objects.create_superuser('admin', 'admin@example.com', 'pass')

And then, after doing chmod +x yourfile.py:

fabric run:
run("docker exec container_django yourfile.py")

Depending on your setup you may need to make sure that the DJANGO_SETTINGS_MODULE environment variable is set appropriately for that run() command.



回答5:

I took @hoefling's answer, and changed it a little.

I needed to create super user AFTER the build step. So i put it inside a supervisor script. That means it will be executed every time i run the container. So i added a simple if / else control to check if superuser is already created. That reduces the execution time. And we need to set DJANGO_SETTINGS_MODULE environment variable as well.

python -c "import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'project_name.settings'
import django
django.setup()
from django.contrib.auth.management.commands.createsuperuser import get_user_model
if get_user_model().objects.filter(username='$DJANGO_SUPERUSER_USERNAME'): 
    print 'Super user already exists. SKIPPING...'
else:
    print 'Creating super user...'
    get_user_model()._default_manager.db_manager('$DJANGO_DB_NAME').create_superuser(username='$DJANGO_SUPERUSER_USERNAME', email='$DJANGO_SUPERUSER_EMAIL', password='$DJANGO_SUPERUSER_PASSWORD')
    print 'Super user created...'"


回答6:

I would suggest running a Data Migration, so when you startup your Docker services (e.g. app & db) via docker-compose up, you can execute all migrations exactly once docker-compose exec web python code/manage.py migrate

So your migration would look like this (assuming you store credentials etc. in environment variables)

import os
from django.db import migrations

class Migration(migrations.Migration):

    dependencies = [
        ('<your_app>', '<previous_migration>'),
    ]

    def generate_superuser(apps, schema_editor):
        from django.contrib.auth.models import User

        DJANGO_DB_NAME = os.environ.get('DJANGO_DB_NAME', "default")
        DJANGO_SU_NAME = os.environ.get('DJANGO_SU_NAME')
        DJANGO_SU_EMAIL = os.environ.get('DJANGO_SU_EMAIL')
        DJANGO_SU_PASSWORD = os.environ.get('DJANGO_SU_PASSWORD')

        superuser = User.objects.create_superuser(
            username=DJANGO_SU_NAME,
            email=DJANGO_SU_EMAIL,
            password=DJANGO_SU_PASSWORD)

        superuser.save()

    operations = [
        migrations.RunPython(generate_superuser),
    ]

This allows you to use a built container to execute against a database, whether it's a local db in the same container or a separate service. And it's not done every time you rebuild your container, but only when the migration is necessary.