Pular para o conteúdo
_lvleo21
← Todos os artigos

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

Referências