{"id":286,"date":"2025-01-20T10:00:00","date_gmt":"2025-01-20T16:00:00","guid":{"rendered":"https:\/\/douglasstarnes.dev\/?p=286"},"modified":"2025-01-15T12:42:21","modified_gmt":"2025-01-15T18:42:21","slug":"building-a-countdown-application-with-python-django-azure-and-github-1","status":"publish","type":"post","link":"https:\/\/douglasstarnes.dev\/index.php\/2025\/01\/20\/building-a-countdown-application-with-python-django-azure-and-github-1\/","title":{"rendered":"Building a Countdown Application with Python, Django, Azure and GitHub &#8211; (1)"},"content":{"rendered":"\n<p>In this series we will build a web application that counts down the number of days to events.  We will use Django to write the application and Python since Django is implemented using Python.  The application and database will eventually be hosted on Microsoft Azure.  More specifically, Azure Container Apps will host the application and Azure Database for PostgreSQL Flexible servers will be used to serve a database.  And we will use several services from GitHub: Repositories for source control, Actions for CI\/CD and Codespaces for a development environment.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">GitHub Codespaces<\/h3>\n\n\n\n<p>We will use GitHub Codespaces for the development environment.  GitHub Codespaces is Visual Studio Code running in the browser, backed by a Linux compute instance.  This means that many applications can be developed on GitHub Codespaces and all you need is a web browser and a network connection!  Mobile and desktop applications don&#8217;t work so well as GitHub Codespaces does not provide a virtual desktop.  But web, command line, data science and other applications work just fine.  Thus, a lot of what you will see in this series on GitHub Codespaces will transfer to Visual Studio Code on the desktop with little if any friction.  The extensions you use on GitHub Codespaces will work on Visual Studio Code on the desktop.  The keyboard shortcuts are also the same.  If you are using WSL, macOS or a Linux like compute the command line will work the same.  You can even connect remotely to a Codespace from Visual Studio Code.  It&#8217;s like having the best of both worlds!<\/p>\n\n\n\n<p>Now, because GitHub Codespaces uses compute in the Microsoft cloud, it&#8217;s not entirely free.  However, you can use it for free, for up to 60 hours per month.  That&#8217;s an average of 2 hours per day which is enough time to experiment with.  All you need is a personal GitHub account which you likely already have and is free.  You don&#8217;t even have to provide a credit card or payment info.  I&#8217;ll be using a free GitHub account to write the demo application.  In order to conserve your free quota, GitHub Codespaces time out after they have been idling.  By default, the time out length is 30 minutes.  You can set this anywhere from 5 minutes to 4 hours.  I find 5 minutes to be rather limiting so I usually have mine set to 10 minutes.  I&#8217;ll show you how set the time out but first we need to create a new Codespace.<\/p>\n\n\n\n<p>Go to https:\/\/github.com\/codespaces in a browser.  <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" src=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24a72311-1024x576.png\" alt=\"\" class=\"wp-image-287\" srcset=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24a72311-1024x576.png 1024w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24a72311-300x169.png 300w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24a72311-768x432.png 768w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24a72311-1536x864.png 1536w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24a72311-2048x1152.png 2048w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24a72311-1280x720.png 1280w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Here you can create a new Codespace from a template or manage existing Codespaces.  For this application, we will start from scratch by clicking the <strong>Use this template<\/strong> button for the <strong>Blank <\/strong>template.<\/p>\n\n\n\n<p>After a minute or less, you&#8217;ll see a brand new GitHub Codespace in a new tab.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" src=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24a9d197-1024x576.png\" alt=\"\" class=\"wp-image-288\" srcset=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24a9d197-1024x576.png 1024w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24a9d197-300x169.png 300w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24a9d197-768x432.png 768w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24a9d197-1536x864.png 1536w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24a9d197-2048x1152.png 2048w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24a9d197-1280x720.png 1280w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Again, if you&#8217;ve used Visual Studio Code on the desktop, this interface will not be a surprise.<\/p>\n\n\n\n<p>Before moving on, let&#8217;s see how to save your free hours by setting a time out interval.  Go back to the tabs where you created the Codespace.  In the upper right, click your profile picture to open the menu and then click <strong>Settings<\/strong>. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"506\" src=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24aed393-1024x506.png\" alt=\"\" class=\"wp-image-289\" srcset=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24aed393-1024x506.png 1024w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24aed393-300x148.png 300w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24aed393-768x380.png 768w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24aed393-1536x760.png 1536w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24aed393-2048x1013.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>In the settings, under <strong>Code, planning and automation<\/strong>, click <strong>Codespaces<\/strong>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"506\" src=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24b04524-1024x506.png\" alt=\"\" class=\"wp-image-290\" srcset=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24b04524-1024x506.png 1024w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24b04524-300x148.png 300w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24b04524-768x380.png 768w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24b04524-1536x760.png 1536w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24b04524-2048x1013.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>In the next screen, scroll down to <strong>Default idle timeout<\/strong>, change the value to between 5 and 240 minutes (4 hours) and click the <strong>Save <\/strong>button.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"506\" src=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24b1b4a2-1024x506.png\" alt=\"\" class=\"wp-image-291\" srcset=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24b1b4a2-1024x506.png 1024w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24b1b4a2-300x148.png 300w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24b1b4a2-768x380.png 768w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24b1b4a2-1536x760.png 1536w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24b1b4a2-2048x1013.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Return to the tab with the Codespace and continue the setup.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Configuring the Development Environment<\/h3>\n\n\n\n<p>To get started, we need to install a few extensions.  Click on the icon for the Extensions panel in the sidebar.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"506\" src=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24b47587-1024x506.png\" alt=\"\" class=\"wp-image-292\" srcset=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24b47587-1024x506.png 1024w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24b47587-300x148.png 300w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24b47587-768x380.png 768w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24b47587-1536x760.png 1536w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24b47587-2048x1013.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>In the search box at the top of the Extensions panel, search for <em>python<\/em>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"506\" src=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24b5b683-1024x506.png\" alt=\"\" class=\"wp-image-293\" srcset=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24b5b683-1024x506.png 1024w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24b5b683-300x148.png 300w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24b5b683-768x380.png 768w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24b5b683-1536x760.png 1536w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24b5b683-2048x1013.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>The first result, Python by Microsoft is what we want.  This provides lots of features useful for developing Python applications including syntax highlighting, automatic indentation, code hints, debugging and virtual environment support.  Click the green <strong>Install <\/strong>button to install the extension.<\/p>\n\n\n\n<p>While the Python extension is installing, search for <em>ruff <\/em>in the Extensions panel.  Install the extension from Astral Software.  Ruff is a Python linter and formatter.  And do the same for <em>sqlite viewer<\/em> and install the extension from Florian Klampfer.<\/p>\n\n\n\n<p>We&#8217;ll install more extensions for other features as we progress through the series but for now these will be enough.<\/p>\n\n\n\n<p>Now you&#8217;re going to configure some of the settings of Visual Studio Code.  Press the F1 key to open the command palette.  Search for and select <strong>Preferences: Open Settings (UI)<\/strong>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"506\" src=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24bb46a5-1024x506.png\" alt=\"\" class=\"wp-image-294\" srcset=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24bb46a5-1024x506.png 1024w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24bb46a5-300x148.png 300w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24bb46a5-768x380.png 768w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24bb46a5-1536x760.png 1536w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24bb46a5-2048x1013.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>The default font sizes are a little small for my preferences, and for the screenshots so I&#8217;m going to make them a little bigger. In the Search settings field, search for <em>editor font size<\/em> and change the size from the default to 20. Do the same thing for <em>terminal font size<\/em>. Notice that a brown bar appears to the left of the changed settings.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"506\" src=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24bf4b94-1024x506.png\" alt=\"\" class=\"wp-image-295\" srcset=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24bf4b94-1024x506.png 1024w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24bf4b94-300x148.png 300w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24bf4b94-768x380.png 768w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24bf4b94-1536x760.png 1536w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24bf4b94-2048x1013.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Search for <em>format <\/em>in the Settings.  Click the dropdown in Editor: Default Formatter and select Ruff charliemarsh.ruff.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"506\" src=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24c118d2-1024x506.png\" alt=\"\" class=\"wp-image-296\" srcset=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24c118d2-1024x506.png 1024w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24c118d2-300x148.png 300w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24c118d2-768x380.png 768w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24c118d2-1536x760.png 1536w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24c118d2-2048x1013.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>And check the boxes for Editor: Format On Paste and Editor: Format On Save to run Ruff when Python code is pasted or Python files are saved.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"506\" src=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24c28d90-1024x506.png\" alt=\"\" class=\"wp-image-298\" srcset=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24c28d90-1024x506.png 1024w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24c28d90-300x148.png 300w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24c28d90-768x380.png 768w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24c28d90-1536x760.png 1536w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_24c28d90-2048x1013.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Starting the Django Project<\/h3>\n\n\n\n<p>At this point we could create a virtual environment.  However, I don&#8217;t usually do this with working in a GitHub Codespace.  A Codespace is generally tied to a single GitHub Repository and one repository is generally going to contain a single project or at least several projects with the same requirements.  At least that&#8217;s have I&#8217;ve used them.  So I won&#8217;t be making a virtual environment.  On Visual Studio Code for the desktop, where you will have many projects with different dependencies, I would use a virtual environment for each one.<\/p>\n\n\n\n<p>Django is distributed as a Python package.  Therefore it can be installed with <code>pip<\/code>.   To keeps things manageable, we&#8217;ll create a requirements.txt file and install the dependencies from there. Open a folder for the project and inside create a new file named <code>requirements.txt<\/code>.  Place the names and optionally version numbers of the packages you wish to install.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Django<\/code><\/pre>\n\n\n\n<p>The <code>requirements.txt<\/code> file will be distributed as part of the project so that later on, we can install the dependencies again.  To install the package from the <code>requirements.txt<\/code> file, use <code>pip<\/code> with the <code>-r<\/code> option passing it the name of the requirements file.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" data-code=\"$ pip install -r requirements.txt\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #88C0D0\">$<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">pip<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">install<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">-r<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">requirements.txt<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>This will install Django and its dependencies.  As part of the Django package, a utility named <code>django-admin<\/code> has been installed and is the starting point for a new Django project.<\/p>\n\n\n\n<figure class=\"wp-block-pullquote has-text-align-left\"><blockquote><p>Throughout this series, you will be the phrase Django <em>project <\/em>and Django <em>app<\/em>. While you might think they mean the same thing, Django treats them differently. A Django project is a container for a web application build using Django. A Django app is actually a way of organizing your Django project into manageable and reusable features. For example, you could implement an e-commerce web application as a Django project. Then the project might include Django apps for a catalog, shopping cart, user management and so on.<\/p><\/blockquote><\/figure>\n\n\n\n<p>Press <strong>Ctrl-`<\/strong> to open the integrated terminal panel.  Run the <code>django-admin<\/code> utility, with the <code>startproject <\/code>command. The <code>startproject<\/code> command accepts two arguments. First is the name of the project, I&#8217;ll call mine countdown. Second is an optional folder. If this argument is omitted, the project will be placed in a new folder with the same name as the project. I find this to be unendingly redundant so I like to place the project in the folder the command is run from. So I will provide a dot for the location.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" data-code=\"$ django-admin startproject countdown .\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #88C0D0\">$<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">django-admin<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">startproject<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">countdown<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">.<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>You will notice that two items have been created. First is a folder with the same name as the project, <code>countdown<\/code>, but this is not the same as if a location was omitted. That would have put the two items you see in a new folder with the project name. Having those two folders with the same name to me can be confusing. The second is a file named <code>manage.py.<\/code> We will be making extensive use of this file as we develop the the countdown application in the series.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"688\" height=\"447\" src=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_2865ebeb.png\" alt=\"\" class=\"wp-image-306\" srcset=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_2865ebeb.png 688w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_2865ebeb-300x195.png 300w\" sizes=\"auto, (max-width: 688px) 100vw, 688px\" \/><\/figure>\n\n\n\n<p>You now have a Django project that implements a web application that you can run using the development server.  And you use the <code>manage.py<\/code> file, with the <code>runserver <\/code>command, to start the development server.  Run the server from inside of the terminal panel.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" data-code=\"$ python manage.py runserver\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #88C0D0\">$<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">python<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">manage.py<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">runserver<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>You&#8217;ll see some errors in red about unapplied migrations.  You can ignore those for now.  We&#8217;ll resolve the error when we start to work with databases in a future post.  The development server will start and listen on the localhost address (127.0.0.1) and port 8000.  And if you are running on Visual Studio Code on the desktop, you can open a browser and go to http:\/\/127.0.0.1:8000\/ and see the default page for a new Django project.  But what if you are running on a GitHub Codespace?  The localhost address is referred to a server somewhere in the Microsoft cloud.  So how do you view the Django project running on a Codespace?  You may have noticed this popup in the lower right corner of the Codespace.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"912\" height=\"221\" src=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_286be9fa.png\" alt=\"\" class=\"wp-image-307\" srcset=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_286be9fa.png 912w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_286be9fa-300x73.png 300w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_286be9fa-768x186.png 768w\" sizes=\"auto, (max-width: 912px) 100vw, 912px\" \/><\/figure>\n\n\n\n<p>It tells us that the GitHub Codespace has forwarded port 8000 to a URL generated by the GitHub Codespace.  Click the <strong>Open in Browser<\/strong> button, and the generated URL will open in a new tab.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" src=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_286e9218-1024x576.png\" alt=\"\" class=\"wp-image-308\" srcset=\"https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_286e9218-1024x576.png 1024w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_286e9218-300x169.png 300w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_286e9218-768x432.png 768w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_286e9218-1536x865.png 1536w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_286e9218-2048x1153.png 2048w, https:\/\/douglasstarnes.dev\/wp-content\/uploads\/2025\/01\/Snag_286e9218-1280x720.png 1280w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>This URL is only for development and by default, it is only accessible by you (notice the <strong>Make Public <\/strong>button on the popup).  However, it&#8217;s a great alternative to configuring it yourself.  Regardless, you should see the default Django home page pictured above.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Summary<\/h3>\n\n\n\n<p>In this post you learned about GitHub Codespaces.  You created a Codespace, configured it to save your free hours, and installed several extensions to help with Python application development.  You also installed the Django package and created your first Django project.  You saw how to run the Django development server and learned how port forwarding works in a GitHub Codespace so you can view the web application during development.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this series we will build a web application that counts down the number of&#8230;<\/p>\n","protected":false},"author":1,"featured_media":309,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[35],"tags":[38,36,37,4,24],"class_list":["post-286","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-django","tag-codespaces","tag-django","tag-github","tag-python","tag-vscode"],"_links":{"self":[{"href":"https:\/\/douglasstarnes.dev\/index.php\/wp-json\/wp\/v2\/posts\/286","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/douglasstarnes.dev\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/douglasstarnes.dev\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/douglasstarnes.dev\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/douglasstarnes.dev\/index.php\/wp-json\/wp\/v2\/comments?post=286"}],"version-history":[{"count":5,"href":"https:\/\/douglasstarnes.dev\/index.php\/wp-json\/wp\/v2\/posts\/286\/revisions"}],"predecessor-version":[{"id":310,"href":"https:\/\/douglasstarnes.dev\/index.php\/wp-json\/wp\/v2\/posts\/286\/revisions\/310"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/douglasstarnes.dev\/index.php\/wp-json\/wp\/v2\/media\/309"}],"wp:attachment":[{"href":"https:\/\/douglasstarnes.dev\/index.php\/wp-json\/wp\/v2\/media?parent=286"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/douglasstarnes.dev\/index.php\/wp-json\/wp\/v2\/categories?post=286"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/douglasstarnes.dev\/index.php\/wp-json\/wp\/v2\/tags?post=286"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}