The Django Shell
Every ORM query in this track so far has been shown as a code snippet you'd expect to find inside a view. But there's a much faster way to try out a query, check a relationship, or confirm what a filter actually returns: Django ships with an interactive Python shell that has your entire project already loaded — models, settings, and all — the moment it starts.
Starting the Shell
From the root of a real Django project, with the virtual environment active, the shell starts with:
python manage.py shell
This drops you into a regular Python REPL, except your project's settings module has already been configured and every app is ready to import from — there's no separate setup step needed before you can start querying TaskFlow's models.
A Full Walkthrough: Import, Create, Query, Update
Here's what an actual shell session looks like, working with TaskFlow's Task model —
importing it, creating a row, querying it back, and updating it:
>>> from taskflow.models import Task, Project
>>> project = Project.objects.get(name='Website Redesign')
>>> task = Task.objects.create(title='Fix navbar spacing', priority='low', project=project)
>>> task
<Task: Fix navbar spacing>
>>> Task.objects.filter(project=project).count()
4
>>> task.completed = True
>>> task.save()
>>> Task.objects.get(pk=task.pk).completed
True
Notice the line <Task: Fix navbar spacing> — that's the __str__()
method from the previous lesson at work. Without it, the same line would have printed the far less
useful <Task: Task object (12)>. This is exactly the "immediately visible" payoff
mentioned back in Custom Managers & Meta Options.
shell_plus: Auto-Importing Every Model
Typing an import line for every model you want to test gets old fast in a project with many apps.
django-extensions, a popular third-party package, adds a shell_plus command
that starts the same kind of interactive shell but automatically imports every model in your project
for you — no manual from ... import ... lines needed before you start querying.
python manage.py shell_plus
It's not part of Django core, so it needs installing separately, but it's worth knowing about as the shell workflow gets used more — it removes a small piece of friction that adds up over a long session.
A Practical Debugging Workflow
One of the most genuinely useful habits the shell enables: when a filter in a view isn't returning what you expect, don't guess — open the shell and run the exact same query by hand, checking the QuerySet at each step.
>>> from taskflow.models import Task
>>> qs = Task.objects.filter(completed=False, project__name='Website Redesign')
>>> qs.count()
0
>>> Task.objects.filter(project__name='Website Redesign').values('completed')
<QuerySet [{'completed': True}, {'completed': True}, {'completed': True}]>
That second query reveals the actual problem immediately — every task in that project is already marked complete, so the original filter was correct all along; there just weren't any matching rows. Checking assumptions like this interactively is often far faster than adding print statements to a view and reloading a page.
A Safe Place to Experiment First
The shell is also where it's worth trying out newer ORM tools before committing them to real view
code — testing a Q object combination from
Q Objects & Chaining, or checking that an
.annotate() call from Aggregation & Annotation
produces the shape you expect. Getting the query right interactively first, then copying the confirmed
version into a view, catches mistakes far earlier than debugging inside a running page.
.delete() call typed casually while exploring can permanently remove real rows. Be especially careful running destructive commands interactively against anything other than a disposable development database.