{
"$type": "site.standard.document",
"canonicalUrl": "https://rednafi.com/python/django-and-jupyter-notebook/",
"description": "Connect Jupyter Notebook to Dockerized Django apps using ipykernel and django-extensions for interactive debugging and data exploration.",
"path": "/python/django-and-jupyter-notebook/",
"publishedAt": "2023-01-14T00:00:00.000Z",
"site": "at://did:plc:fgtm2c26vfcj74rfmeggbyqj/site.standard.publication/3mnl6f7ob462z",
"tags": [
"Python",
"Django",
"Docker"
],
"textContent": "Back in the days when I was working as a data analyst, I used to spend hours inside Jupyter\nnotebooks exploring, wrangling, and plotting data to gain insights. However, as I shifted my\ncareer gear towards backend software development, my usage of interactive exploratory tools\ndwindled.\n\nNowadays, I spend the majority of my time working on a fairly large Django monolith\naccompanied by a fleet of microservices. Although I love my text editor and terminal\nemulators, I miss the ability to just start a Jupyter Notebook server and run code snippets\ninteractively. While Django allows you to open up a shell environment and run code snippets\ninteractively, it still isn't as flexible as a notebook.\n\nSo, I wanted to see if I could connect a Jupyter notebook server to a containerized Django\napplication running on my local machine and interactively start making queries from there.\nTurns out, you can do that by integrating three tools into your Dockerized environment:\n[ipykernel], [jupyter], and [django-extensions]. Before I start explaining how everything is\ntied together, here's a [fully working example] of a containerized Django application where\nyou can log into the Jupyter server and start debugging the app.\n\nThe app is just a Dockerized version of the famous polls-app from the Django tutorial. The\ndirectory structure looks as follows:\n\nWe define and pin the dependencies required for the Jupyter integration in the\nrequirements-dev.txt file:\n\nThe application dependencies are defined in the requirements.txt file:\n\nIn the mysite/mysite/_debug_settings.py file, we import the configs from the primary\nsettings file and add the Jupyter configuration attributes there. Here's the full content of\nthe extended _debug_settings.py file:\n\nNotice how we're appending the django_extensions app to the INSTALLED_APPS list defined\nin the main settings file. Then we're setting the shell to ipython with the SHELL_PLUS\nattribute. The NOTEBOOK_ARGUMENTS defines the port of the Jupyter server and some\nauth-specific settings.\n\nNext, in the Dockerfile, we're defining the application like this:\n\nFinally, we're orchestrating the application and the Jupyter server in the\ndocker-compose.yml file. Here's how it looks:\n\nWe're orchestrating three services here: webserver, jupyter, and debug. All of them\nextend the base web service that builds the Dockerfile. The webserver service is where\nthe Django app is run and exposed via the 8000 port. The jupyter service runs the\nJupyter server and makes it accessible through your browser via the 8895 port.\nAdditionally, note how we are using our extended version of the main settings by overriding\nthe DJANGO_SETTINGS_MODULE environment variable and setting it to\nmysite._debug_settings. The debug container is spun up to run the migration commands and\nperform other maintenance tasks within the container network. All the maintenance commands\nare defined in the Makefile for your convenience. You can run any of these by running\nmake <target> from the root directory.\n\nAnd that's it!\n\nIf you have [Docker] and [docker-compose] installed on your local system, you can give it a\ntry. Clone the [fully working example] repo, navigate to the root directory and run:\n\nThen run the migration command:\n\nNow head over to your browser and go to http://localhost:8000. You should see an empty\npage with a simple header like this:\n\n![Django polls app empty page running on localhost:8000][image_1]\n\nIf you go to http://localhost:8895, you'll be able to open a new notebook that\nautomatically connects to your database and allows you to write interactive code\nimmediately.\n\n![Jupyter notebook connected to Django showing interactive Python code execution][image_2]\n\nYou can run the following snippet and it'll create two questions and two choices in the\ndatabase.\n\nIf you run this and refresh your application server, you'll that the objects have been\ncreated and they appear in the view:\n\n![Django app displaying Question and Choice objects created via Jupyter notebook][image_3]\n\nFurther reading\n\n- [How to access Jupyter notebook in a Docker container]\n\n\n\n\n[ipykernel]:\n https://ipython.readthedocs.io/en/stable/install/kernel_install.html\n\n[jupyter]:\n https://jupyter.org/\n\n[django-extensions]:\n https://django-extensions.readthedocs.io/en/latest/\n\n[fully working example]:\n https://github.com/rednafi/django-jupyter\n\n[docker]:\n https://www.docker.com/\n\n[docker-compose]:\n https://docs.docker.com/compose/\n\n[how to access jupyter notebook in a docker container]:\n https://stackoverflow.com/questions/62193187/django-shell-plus-how-to-access-jupyter-notebook-in-docker-container\n\n[image_1]:\n https://blob.rednafi.com/static/images/django_and_jupyter_notebook/img_1.png\n\n[image_2]:\n https://blob.rednafi.com/static/images/django_and_jupyter_notebook/img_2.png\n\n[image_3]:\n https://blob.rednafi.com/static/images/django_and_jupyter_notebook/img_3.png",
"title": "Debugging a containerized Django application in Jupyter Notebook"
}