O que é e como usar o shell do Django?
Como abrir o shell do Django, aproveitar os imports automáticos, executar um arquivo .py com manage.py shell e quando transformar o script em um comando de management.
- django
- python
Publiquei este artigo originalmente no dev.to em agosto de 2024. Nesta versão atualizei os exemplos para o Django 6.1, acrescentei os imports automáticos do shell e deixei o script de criação de usuários seguro para rodar mais de uma vez.
“Preciso executar só um trecho desse código, tem como?”
Tem, com o shell do Django. Ele é uma linha de comando interativa parecida com o prompt do Python, com a diferença de que já carrega as configurações do projeto. Dá para importar models e funções do projeto, consultar o banco e testar um trecho de código sem criar uma view ou um teste só para isso.
Abrindo o shell
Na pasta raiz do projeto, onde fica o manage.py, rode:
python manage.py shell
O prompt >>> aparece com o Django configurado, e você pode importar e usar o que quiser do projeto:
>>> from apps.core.models import Account
>>> Account.objects.all()
<QuerySet [<Account: lvleo21>]>
Imports automáticos
Desde o Django 5.2, o shell importa sozinho os models de todos os apps do INSTALLED_APPS. Ao abrir, ele avisa quantos objetos importou:
12 objects imported automatically (use -v 2 for details).
Com isso o import do exemplo acima deixa de ser necessário, e Account.objects.all() funciona direto. No Django 6.1, além dos models, entram também settings, connection, models, functions e timezone. Para ver a lista completa, abra com python manage.py shell -v 2. Para desligar, use --no-imports.
Executando um arquivo .py no shell
O shell também aceita um arquivo inteiro. Como exemplo, vamos escrever um script que cria usuários de teste.
Crie um arquivo no mesmo nível do manage.py. O nome é livre; aqui ele se chama shell.py:
# shell.py
from django.contrib.auth import get_user_model
from django.db import transaction
User = get_user_model()
QNT_USERS = 10
with transaction.atomic():
for index in range(QNT_USERS):
user, created = User.objects.get_or_create(username=f"user_{index}")
if created:
user.set_password("padrao@123")
user.save()
E mande o arquivo para o shell pela entrada padrão:
python manage.py shell < shell.py
Três coisas mudaram em relação à versão original do script. O model de usuário vem de get_user_model() em vez do import direto de User, o que mantém o script funcionando em projetos com usuário customizado. O get_or_create no lugar do create permite rodar o script de novo: com create, a segunda execução para com IntegrityError, porque o username precisa ser único. E o transaction.atomic() desfaz tudo se algo der errado no meio, em vez de deixar metade dos usuários criados.
A senha é gravada com set_password, que salva o hash e não o texto. Mesmo assim, uma senha fixa como padrao@123 só serve para o ambiente local.
Para uma linha só, não precisa de arquivo. A opção -c executa o código e sai:
python manage.py shell -c "print(User.objects.count())"
Quando transformar o script em um comando
Se o script vai ser executado com frequência, como um seed para popular o banco em desenvolvimento, vale transformá-lo em um comando de management. Ele passa a aparecer no python manage.py help, aceita argumentos e fica versionado junto com o app.
O arquivo vai em <app>/management/commands/, e o nome do arquivo vira o nome do comando. As pastas management e commands precisam de um __init__.py:
# core/management/commands/criar_usuarios.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 = "Cria usuários de teste com a senha padrao@123"
def add_arguments(self, parser):
parser.add_argument("quantidade", type=int)
@transaction.atomic
def handle(self, *args, **options):
User = get_user_model()
criados = 0
for index in range(options["quantidade"]):
user, created = User.objects.get_or_create(username=f"user_{index}")
if created:
user.set_password("padrao@123")
user.save()
criados += 1
self.stdout.write(self.style.SUCCESS(f"{criados} usuários criados"))
python manage.py criar_usuarios 15