Django Tutorial in Visual Studio Code
Django Tutorial in Visual Studio Code
Django is a high-level Python framework designed for rapid, secure, and scalable
web development. Django includes rich support for URL routing, page templates,
and working with data.
In this Django tutorial, you create a simple Django app with three pages that use a
common base template. You create this app in the context of Visual Studio Code in
order to understand how to work with Django in the VS Code terminal, editor, and
debugger. This tutorial does not explore various details about Django itself, such as
working with data models and creating an administrative interface. For guidance on
those aspects, refer to the Django documentation links at the end of this tutorial.
The completed code project from this Django tutorial can be found on
GitHub: python-sample-vscode-django-tutorial.
If you have any problems, you can search for answers or ask a question on
the Python extension Discussions Q&A.
Prerequisites
To successfully complete this Django tutorial, you must do the following (which are
the same steps as in the general Python tutorial):
1. Install the Python extension.
2. Install a version of Python 3 (for which this tutorial is written). Options include:
o (All operating systems) A download from python.org; typically use
the Download Python 3.9.1 button that appears first on the page (or
whatever is the latest version).
o (Linux) The built-in Python 3 installation works well, but to install other
Python packages you must run sudo apt install python3-pip in
the terminal.
o (macOS) An installation through Homebrew on macOS using brew
install python3 (the system install of Python on macOS is not
supported).
o (All operating systems) A download from Anaconda (for data science
purposes).
3. On Windows, make sure the location of your Python interpreter is included in
your PATH environment variable. You can check the location by
running path at the command prompt. If the Python interpreter's folder isn't
included, open Windows Settings, search for "environment", select Edit
environment variables for your account, then edit the Path variable to
include that folder.
Create a project environment for the Django tutorial
In this section, you create a virtual environment in which Django is installed. Using a
virtual environment avoids installing Django into a global Python environment and
gives you exact control over the libraries used in an application. A virtual
environment also makes it easy to Create a requirements.txt file for the environment.
1. On your file system, create a project folder for this tutorial, such
as hello_django.
2. In that folder, use the following command (as appropriate to your computer)
to create a virtual environment named .venv based on your current
interpreter:
# Linux
source .venv/bin/activate
# macOS
source .venv/bin/activate
# Windows
py -3 -m venv env
.venv\scripts\activate
Note: Use a stock Python installation when running the above commands. If
you use python.exe from an Anaconda installation, you see an error because
the ensurepip module isn't available, and the environment is left in an
unfinished state.
3. Open the project folder in VS Code by running code ., or by running VS Code
and using the File > Open Folder command.
4. In VS Code, open the Command Palette (View > Command Palette or
(Ctrl+Shift+P)). Then select the Python: Select Interpreter command:
5. The command presents a list of available interpreters that VS Code can locate
automatically (your list will vary; if you don't see the desired interpreter,
see Configuring Python environments). From the list, select the virtual
environment in your project folder that starts with ./.venv or .\.venv:
You now have a self-contained environment ready for writing Django code. VS Code
activates the environment automatically when you use Terminal: Create New
Terminal (Ctrl+Shift+`). If you open a separate command prompt or terminal,
activate the environment by running source .venv/bin/activate (Linux/macOS)
or .venv\Scripts\Activate.ps1 (Windows). You know the environment is
activated when the command prompt shows (.venv) at the beginning.
This startproject command assumes (by use of . at the end) that the
current folder is your project folder, and creates the following within it:
o manage.py: The Django command-line administrative utility for the
project. You run administrative commands for the project using python
manage.py <command> [options].
When you run the server the first time, it creates a default SQLite database in
the file db.sqlite3 that is intended for development purposes, but can be
used in production for low-volume web apps. For additional information
about databases, see the Types of databases section.
3. To verify the Django project, make sure your virtual environment is activated,
then start Django's development server using the command python
manage.py runserver. The server runs on the default port 8000, and you see
output like the following output in the terminal window:
Django's built-in web server is intended only for local development purposes.
When you deploy to a web host, however, Django uses the host's web server
instead. The wsgi.py and asgi.py modules in the Django project take care of
hooking into the production servers.
If you want to use a different port than the default 8000, specify the port
number on the command line, such as python manage.py runserver 5000.
4. Ctrl+click the http://127.0.0.1:8000/ URL in the terminal output
window to open your default browser to that address. If Django is installed
correctly and the project is valid, you see the default page shown below. The
VS Code terminal output window also shows the server log.
5. When you're done, close the browser window and stop the server in VS Code
using Ctrl+C as indicated in the terminal output window.
The command creates a folder called hello that contains a number of code
files and one subfolder. Of these, you frequently work with views.py (that
contains the functions that define pages in your web app)
and models.py (that contains classes defining your data objects).
The migrations folder is used by Django's administrative utility to manage
database versions as discussed later in this tutorial. There are also the
files apps.py (app configuration), admin.py (for creating an administrative
interface), and tests.py (for creating tests), which are not covered here.
2. Modify hello/views.py to match the following code, which creates a single
view for the app's home page:
from django.http import HttpResponse
def home(request):
3. Create a file, hello/urls.py, with the contents below. The urls.py file is
where you specify patterns to route different URLs to their appropriate views.
The code below contains one route to map root URL of the app ( "") to
the views.home function that you just added to hello/views.py:
urlpatterns = [
4. The web_project folder also contains a urls.py file, which is where URL
routing is actually handled. Open web_project/urls.py and modify it to
match the following code (you can retain the instructive comments if you like).
This code pulls in the app's hello/urls.py using django.urls.include,
which keeps the app's routes contained within the app. This separation is
helpful when a project contains multiple apps.
urlpatterns = [
path("", include("hello.urls")),
path('admin/', admin.site.urls)
2. Select the link and VS Code will prompt for a debug configuration.
Select Django from the dropdown and VS Code will populate a
new launch.json file with a Django run configuration. The launch.json file
contains a number of debugging configurations, each of which is a separate
JSON object within the configuration array.
3. Scroll down to and examine the configuration with the name "Python:
Django":
{
"version": "0.2.0",
"configurations": [
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}\\manage.py",
"args": ["runserver"],
"django": true,
"justMyCode": true
The first argument to path defines a route "hello/" that accepts a variable
string called name. The string is passed to the views.hello_there function
specified in the second argument to path.
URL routes are case-sensitive. For example, the route /hello/<name> is
distinct from /Hello/<name>. If you want the same view function to handle
both, define paths for each variant.
Replace the contents of views.py with the following code to define
the hello_there function that you can step through in the debugger:
import re
def home(request):
return HttpResponse("Hello, Django!")
now = datetime.now()
if match_object:
clean_name = match_object.group(0)
else:
clean_name = "Friend"
return HttpResponse(content)
Tip: The Debug Console also shows exceptions from within the app that may
not appear in the terminal. For example, if you see a "Paused on exception"
message in the Call Stack area of Run and Debug view, switch to the Debug
Console to see the exception message.
7. Copy that line into the > prompt at the bottom of the debug console, and try
changing the formatting:
9. Change the line in the code to use different datetime format, for
example now.strftime("%a, %d %b, %y at %X"), and then save the file.
The Django server will automatically reload, which means the changes will be
applied without the need to restart the debugger. Refresh the page on the
browser to see the update.
10. Close the browser and stop the debugger when you're finished. To stop the
debugger, use the Stop toolbar button (the red square) or the Run > Stop
Debugging command (Shift+F5).
Tip: To make it easier to repeatedly navigate to a specific URL
like http://127.0.0.1:8000/hello/VSCode, output that URL using
a print statement somewhere in a file like views.py. The URL appears in the VS
Code Terminal where you can use Ctrl+click to open it in a browser.
2. 'hello',
3. Inside the hello folder, create a folder named templates, and then another
subfolder named hello to match the app name (this two-tiered folder
structure is typical Django convention).
4. In the templates/hello folder, create a file named hello_there.html with
the contents below. This template contains two placeholders for data values
named "name", and "date", which are delineated by pairs of curly
braces, {{ and }}. All other invariant text is part of the template, along with
formatting markup (such as <strong>). As you can see, template placeholders
can also include formatting, the expressions after the pipe | symbols, in this
case using Django's built-in date filter and time filter. The code, then needs
only to pass the datetime value rather than a pre-formatted string:
<!DOCTYPE html>
<html>
<head>
<title>Hello, Django</title>
</head>
<body>
</body>
</html>
print(request.build_absolute_uri()) #optional
return render(
request,
'hello/hello_there.html',
'name': name,
'date': datetime.now()
You can see that the code is now much simpler, and concerned only with data
values, because the markup and formatting is all contained in the template.
7. Start the program (inside or outside of the debugger, using Ctrl+F5), navigate
to a /hello/name URL, and observe the results.
8. Also try navigating to a /hello/name URL using a name like <a%20value
%20that%20could%20be%20HTML> to see Django's automatic escaping at
work. The "name" value shows up as plain text in the browser rather than as
rendering an actual element.
2. In that same file, add the following line at the end, which includes standard
static file URLs to the list that the project recognizes:
urlpatterns += staticfiles_urlpatterns()
.message {
font-weight: 600;
color: blue;
6. Run the app, navigate to a /hello/name URL, and observe that the message
renders in blue. Stop the app when you're done.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
{% load static %}
</head>
<body>
<div class="navbar">
<div class="body-content">
{% block content %}
{% endblock %}
<hr/>
<footer>
<p>© 2018</p>
</footer>
</div>
</body>
</html>
.navbar {
background-color: lightslategray;
font-size: 1em;
color: white;
.navbar a {
text-decoration: none;
color: inherit;
}
.navbar-brand {
font-size: 1.2em;
font-weight: 600;
.navbar-item {
font-variant: small-caps;
margin-left: 30px;
.body-content {
padding: 5px;
You can run the app at this point, but because you haven't made use of the base
template anywhere and haven't changed any code files, the result is the same as the
previous step. Complete the remaining sections to see the final effect.
"body": [
"$0",
],
},
When you select the completion, the snippet's code appears with the cursor
on the snippet's insertion point:
2. At the insertion point in the "title" block, write Home, and in the "content"
block, write <p>Home page for the Visual Studio Code Django
tutorial.</p>, then save the file. These lines are the only unique parts of
the extended page template:
3. In the templates/hello folder, create about.html, use the snippet to insert
the boilerplate markup, insert About us and <p>About page for the
Visual Studio Code Django tutorial.</p> in the "title" and "content"
blocks, respectively, then save the file.
4. Repeat the previous step to
create templates/hello/contact.html using Contact us and <p>Contact
page for the Visual Studio Code Django tutorial.</p>.
5. In the app's urls.py, add routes for the /about and /contact pages. Be
mindful that the name argument to the path function defines the name with
which you refer to the page in the {% url %} tags in the templates.
6. In views.py, add functions for the /about and /contact routes that refer to
their respective page templates. Also modify the home function to use
the home.html template.
def home(request):
def about(request):
def contact(request):
return render(request, "hello/contact.html")
The migration scripts effectively record all the incremental changes you make to your
data models over time. By applying the migrations, Django updates the database to
match your models. Because each incremental change has its own script, Django can
automatically migrate any previous version of a database (including a new database)
to the current version. As a result, you need concern yourself only with your models
in models.py, never with the underlying database schema or the migration scripts.
You let Django do that part!
In code, too, you work exclusively with your model classes to store and retrieve data;
Django handles the underlying details. The one exception is that you can write data
into your database using the Django administrative utility loaddata command. This
utility is often used to initialize a data set after the migrate command has initialized
the schema.
When using the db.sqlite3 file, you can also work directly with the database using
a tool like the SQLite browser. It's fine to add or delete records in tables using such a
tool, but avoid making changes to the database schema because the database will
then be out of sync with your app's models. Instead, change the models,
run makemigrations, then run migrate.
Types of databases
By default, Django includes a db.sqlite3 file for an app's database that's suitable
for development work. As described on When to use SQLite (sqlite.org), SQLite works
fine for low to medium traffic sites with fewer than 100 K hits/day, but is not
recommended for higher volumes. It's also limited to a single computer, so it cannot
be used in any multi-server scenario such as load-balancing and geo-replication.
For these reasons, consider using a production-level data store such
as PostgreSQL, MySQL, and SQL Server. For information on Django's support for
other databases, see Database setup. You can also use the Azure SDK for Python to
work with Azure storage services like tables and blobs.
Define models
A Django model is again a Python class derived from django.db.model.Models,
which you place in the app's models.py file. In the database, each model is
automatically given a unique ID field named id. All other fields are defined as
properties of the class using types from django.db.models such
as CharField (limited text), TextField (unlimited
text), EmailField, URLField, IntegerField, DecimalField, BooleanField. DateT
imeField, ForeignKey, and ManyToMany, among others. (See the Model field
reference in the Django documentation for details.)
Each field takes some attributes, like max_length. The blank=True attribute means
the field is optional; null=true means that a value is optional. There is also
a choices attribute that limits values to values in an array of data value/display value
tuples.
For example, add the following class in models.py to define a data model that
represents dated entries in a simple message log:
message = models.CharField(max_length=300)
def __str__(self):
date = timezone.localtime(self.log_date)
A model class can include methods that return values computed from other class
properties. Models typically include a __str__ method that returns a string
representation of the instance.
class LogMessageForm(forms.ModelForm):
class Meta:
model = LogMessage
{% extends "hello/layout.html" %}
{% block title %}
Log a message
{% endblock %}
{% block content %}
{% csrf_token %}
{{ form.as_p }}
</form>
{% endblock %}
input[name=message] {
width: 80%;
}
4. In the app's urls.py file, add a route for the new page:
5. In views.py, define the view named log_message (as referred to by the URL
route). This view handles both HTTP GET and POST cases. In the GET case
(the else: section), it just displays the form that you defined in the previous
steps. In the POST case, it retrieves the data from the form into a data object
(message), sets the timestamp, then saves that object at which point it's
written to the database:
def log_message(request):
if request.method == "POST":
if form.is_valid():
message = form.save(commit=False)
message.log_date = datetime.now()
message.save()
return redirect("home")
else:
8. Enter a message, select Log, and you should be taken back to the home page.
The home page doesn't yet show any of the logged messages yet (which you
remedy in a moment). Feel free to log a few more messages as well. If you
want, peek in the database using a tool like SQLite Browser to see that records
have been created. Open the database as read-only, or otherwise remember
to close the database before using the app, otherwise the app will fail because
the database is locked.
9. Stop the app when you're done.
10. Now modify the home page to display the logged messages. Start by
replacing the contents of app's templates/hello/home.html file with the
markup below. This template expects a context variable
named message_list. If it receives one (checked with the {% if
message_list %} tag), it then iterates over that list (the {% for message in
message_list %} tag) to generate table rows for each message. Otherwise
the page indicates that no messages have yet been logged.
{% extends "hello/layout.html" %}
{% block title %}
Home
{% endblock %}
{% block content %}
<h2>Logged messages</h2>
{% if message_list %}
<table class="message_list">
<thead>
<tr>
<th>Date</th>
<th>Time</th>
<th>Message</th>
</tr>
</thead>
<tbody>
<tr>
<td>
{{ message.message }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>No messages have been logged. Use the <a href="{% url 'log' %}">Log
Message form</a>.</p>
{% endif %}
{% endblock %}
.message_list th,td {
text-align: left;
padding-right: 15px;
}
12. In views.py, import Django's generic ListView class, which we'll use to
implement the home page:
# Remove the old home function if you want; it's no longer used
class HomeListView(ListView):
model = LogMessage
return context
15. Also in urls.py, make a variable for the new view, which retrieves the five
most recent LogMessage objects in descending order (meaning that it queries
the database), and then provides a name for the data in the template context
(message_list), and identifies the template to use:
home_list_view = views.HomeListView.as_view(
context_object_name="message_list",
template_name="hello/home.html",
17. Start the app and open a browser to the home page, which should now
display messages:
3. Use the Step Over (F10) command to step through the template code.
Observe that the debugger steps over all declarative statements and pauses at
any procedural code. For example, stepping through the {% for message in
message_list %} loops lets you examine each value in message and lets you
step to lines like <td>{{ message.log_date | date:'d M Y' }}</td>.
4. You can also work with variables in the Debug Console panel. (Django filters
like date, however, are not presently available in the console.)
5. When you're ready, select Continue ( F5) to finish running the app and view the
rendered page in the browser. Stop the debugger when you're done.
Optional
The following sections describe additional steps that you might find helpful in your
work with Python and Visual Studio Code.
path("admin/", admin.site.urls),
3. Run the server, then open a browser to the app's /admin page (such
as http://127.0.0.1:8000/admin when using the development server).
4. A login page appears, courtesy of django.contrib.auth. Enter your
superuser credentials.
5. Once you're authenticated, you see the default administration page, through
which you can manage users and groups:
You can customize the administrative interface as much as you like. For example, you
could provide capabilities to edit and remove entries in the database. For more
information on making customizations, refer to the Django admin site
documentation.