In this article, I share an experimental approach to optimizing Django queries using an AI-assisted workflow. By combining VSCode Copilot, Postgres MCP server, django-silk and Playwright, I was able to automate parts of the query analysis and optimization process. The results were promising, though the workflow had some notable limitations.
In a large Django application, I wanted to address inefficient queries, particularly N+1 problems, that could often be resolved with select_related or prefetch_related. Given the size of the codebase, I explored whether an AI coding agent could help speed up the process.
Two main obstacles emerged:
Django-silk records SQL queries in database tables whenever a webpage is requested. This makes it straightforward to spot N+1 issues through duplicate queries and to trace them back to the source code. Silk provides a web interface for manual analysis, but since the data is stored in Postgres, it can also be accessed programmatically.
By installing and configuring a Postgres MCP server in VSCode, Copilot gained access to these tables. This enabled the AI to analyze query performance directly from my local database, solving the first challenge of query measurement.
To configure a Postgres MCP server add the following to ~/.vscode/mcp.json
using your actual database url:
{
"servers": {
"postgres": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-postgres",
"postgresql://user:pass@localhost:5432/mydb"
]
}
}
}
Then open the command palette and run "MCP: List servers", then select "postgres", then select "Start server". If the connection to your database is successful the output should say "Discovered 1 tools".
For the second challenge, I used Playwright to automate browser interactions. The following script logs into the application and navigates to a specified page, allowing Copilot to trigger queries during the request/response cycle:
# request_page.py
import sys
from playwright.sync_api import sync_playwright
def send_authenticated_request(url_path: str) -> None:
"""Navigate to the specified page with authentication."""
base_url = "<http://localhost:8001>"
full_url = f"{base_url}{url_path}"
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
page.goto(f"{base_url}/login/")
page.wait_for_selector('input[name="username"]', timeout=10000)
page.fill('input[name="username"]', "mitch.harle")
page.click('button[type="submit"]')
page.fill('input[name="password"]', "*******")
page.click('button[type="submit"]')
page.wait_for_url(f"{base_url}/", timeout=10000)
page.goto(full_url)
page.wait_for_url(full_url, timeout=10000)
if _name_ == "_main_":
send_authenticated_request(sys.argv[1])
The following prompt can then be used to start the process in Copilot:
I am attempting to perform Django query optimizations across my application. When I ask you to optimize queries on the url
/abc/
you should call the following script:python3 request_page.py /abc/
If the script runs successfully please use the Postgres MCP server to query django-silk tables to measure the queries for this url. You should identify if there are any N+1 queries, this is usually identified by many duplicate queries. The 'SQLQuery' table has a field 'traceback' which will give you the code which executed the SQL query, use this to find where in the code query optimizations can be made. For now I am mostly focusing on fixing very inefficient queries, for example those that can be fixed with
select_related
/prefetch_related
.You should attempt to fix the code, then request the url again, then measure the queries again. If your code changes has reduced the number of queries keep the change, if not rollback the change and try a different approach.
Let's start with url
/abc/
Copilot then performed the following iterative loop:
This feedback loop with an explicit goal of reducing queries allowed it identify when it's changes have made a positive or negative impact. With some human oversight, this process successfully identified and resolved several inefficient queries.
The limitations of this approach included:
This experiment demonstrated that AI coding agents can meaningfully assist in query optimization workflows. While not fully autonomous, the combination of Copilot, Django-Silk, Postgres MCP server, and Playwright provided a practical, partially automated solution for identifying and resolving inefficient queries.
Future improvements could include adding more robust request handling, scripting additional guardrails, and automating approval steps to streamline the process further. Despite its limitations, setting up this workflow was quick and delivered tangible benefits, highlighting the evolving capabilities of AI-assisted development tools.