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.

Note: These shell sessions are shown as accurate, representative output — but this website's own project doesn't run a live Django server or database, so nothing here is executed on this page. Try these exact commands in your own TaskFlow project to see real results.
A caution before you go further: Everything you type in the shell is real — it runs against whatever database your project is actually configured to use. A .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.