What is the Django shell and how do you use it?
How to open the Django shell, make use of its automatic imports, run a .py file with manage.py shell and when to turn the script into a management command.
- django
- python
I first published this article on dev.to in August 2024. In this version I updated the examples to Django 6.1, added the shell’s automatic imports and made the user creation script safe to run more than once.
“I only need to run this one piece of code. Is there a way?”
There is: the Django shell. It is an interactive command line much like the Python prompt, except that it already loads the project’s settings. You can import the project’s models and functions, query the database and try out a piece of code without writing a view or a test just for that.
Opening the shell
In the project’s root folder, where manage.py lives, run:
python manage.py shell
The >>> prompt shows up with Django configured, and you can import and use anything from the project:
>>> from apps.core.models import Account
>>> Account.objects.all()
<QuerySet [<Account: lvleo21>]>
Automatic imports
Since Django 5.2, the shell imports the models of every app in INSTALLED_APPS by itself. When it opens, it tells you how many objects it imported:
12 objects imported automatically (use -v 2 for details).
That makes the import in the example above unnecessary, and Account.objects.all() works right away. In Django 6.1, besides the models, it also brings in settings, connection, models, functions and timezone. To see the full list, open it with python manage.py shell -v 2. To turn it off, use --no-imports.
Running a .py file in the shell
The shell also accepts a whole file. As an example, let’s write a script that creates test users.
Create a file next to manage.py. Any name works; here it is called shell.py:
# shell.py
from django.contrib.auth import get_user_model
from django.db import transaction
User = get_user_model()
USER_COUNT = 10
with transaction.atomic():
for index in range(USER_COUNT):
user, created = User.objects.get_or_create(username=f"user_{index}")
if created:
user.set_password("default@123")
user.save()
Then feed the file to the shell through standard input:
python manage.py shell < shell.py
Three things changed from the original version of the script. The user model comes from get_user_model() instead of importing User directly, which keeps the script working in projects with a custom user. Using get_or_create instead of create lets you run the script again: with create, the second run stops with an IntegrityError, because username must be unique. And transaction.atomic() rolls everything back if something fails halfway, instead of leaving half of the users created.
The password is stored with set_password, which saves the hash, not the text. Even so, a fixed password like default@123 is only fit for a local environment.
For a single line, you don’t need a file. The -c option runs the code and exits:
python manage.py shell -c "print(User.objects.count())"
When to turn the script into a command
If the script is going to run often, like a seed that fills the database in development, it is worth turning it into a management command. It then shows up in python manage.py help, takes arguments and is versioned along with the app.
The file goes in <app>/management/commands/, and the file name becomes the command name. The management and commands folders need an __init__.py:
# core/management/commands/create_users.py
from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand
from django.db import transaction
class Command(BaseCommand):
help = "Creates test users with the password default@123"
def add_arguments(self, parser):
parser.add_argument("count", type=int)
@transaction.atomic
def handle(self, *args, **options):
User = get_user_model()
created_count = 0
for index in range(options["count"]):
user, created = User.objects.get_or_create(username=f"user_{index}")
if created:
user.set_password("default@123")
user.save()
created_count += 1
self.stdout.write(self.style.SUCCESS(f"{created_count} users created"))
python manage.py create_users 15