This tutorial focuses on create a web app with the Python framework Django. Lets get started.
If you head to their website https://www.djangoproject.com/ django is described as a "Django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design". Lets install this framework so we can begin developing applications.
To intsall Django you need to have Python install on your computer. Then you can run the following command
python -m pip install Django
This will instlal Django on your computer.
Head to the root directroy where you want to create your project folder and run the following command
django-admin startproject djangotutorial
Where djangotutorial
can be replaced with the foldername of the project you want to get started.
[dj_installation.png]
Now that you have a project started you can create a app within the project. To do this run the following command
python manage.py startapp employees
Where employees
can be changed to reflect the name of the app you want to create.
To run the app you just created you can use the following command
python manage.py runserver 8080
This will run a server you can access the link via http://localhost:8080
.
[dj_install_complete.png]
Lets create our hello world app. To do this we need to create a route an send some information from the server to the browswer.
In our employees
directory we head to the views.py
file. This is where we can
send information to the broswer. We create a function called hello
.
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
def hello(request):
return HttpResponse("Hello World")
We use the HttpResponse
function to send Hello World to the browser.
In our employees
directory we then create a file called urls.py
. This will have
our urls paths for the app.
from django.urls import path
from . import views
urlpatterns = [
path('', views.hello, name="hello")
]
In our main project urls.py
we need to import our app urls.py
file.
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('employees/', include('employees.urls'))
]
From above we do two main things. First we get the include
function.
from django.urls import path, include
Then we add a new path using include
to add our employees.urls
file.
path('employees/', include('employees.urls'))
Now we can access this via http://localhost:8080/employees/
. This path will direct to the
views function hello
and it has a shortcut name as hello
.
You can view the results below.
[dj_hello_world.png]
In the above hello world example we learnt about routing. Lets dig a little deeper into routing.
Lets create a new route called list
in urls.py
.
urlpatterns = [
path('', views.hello, name="hello"),
path('list/', views.list, name="emp_list")
]
In our views.py
we create the list
function.
def list(request):
return HttpResponse("Employees list")
[dj_routing_list.png]
Lets crate one more route called create
.
path('create/', views.create, name="emp_create")
Now in our views we add the create
function.
def create(request):
return HttpResponse("Create an employee")
So we can access this path via http://localhost:8080/employees/create/
.
Lets get data from the url via parameters. First we create a route in
urls.py
path('update/<id>/', views.update, name="emp_update")
Notice how we add the <id>
. This is the placeholder for the paramter we will be
passing.
In the view.py
we can create the update function.
def update(request, id):
return HttpResponse("Update an employee with id="+ id)
In the update
function we pass the id
parameter and we return it in the
HttpResponse
function.
The results can be seen below.
[dj_routing_paramter.png]
Lets try another type of route. We create a simple route.
path('view/', views.view, name="emp_view")
Now in our views.py
we add the view
function
def view(request):
name = request.GET.get('name') or "n/a"
return HttpResponse("Viewing the employee by username=" + name)
In the above example we are not using any placeholders. Instead
we are taking the name
parameter if it exists directly from the
GET
request.
The results are shown below.
With no name we can see
[dj_no_param.png]
With a name in the paramter we can see the results
[dj_name_param.png]
Controllers in Django
are called views. Views contain the logic of our application.
Templates hold our html
elements.
Lets create a help
view with some html
content
def help(request):
return HttpResponse("<h1>This is a help view</h1>")
[dj_help_view.png]
We can redirect our views using the redirect
function.
First we import it from django.shortcuts
from django.shortcuts import render, redirect
Then in our help
function we can do
def help(request):
return redirect("hello")
Note hello is the shortcut name we gave the hello path as seen below here
path('', views.hello, name="hello"),
The results can be seen below
[dj_route_redirect.gif]
We can give POST data from the request
object.
We can create a new route, we will call it api in the urls.py
file
path('api/', views.api, name="emp_api")
Now in the views.py
file we create the function api
.
#import JsonResponse first at the top of the files
from django.http import HttpResponse, JsonResponse
def api(request):
return JsonResponse({'name':'hello'})
Now we use postman to check out route http://localhost:8080/employees/api
.
[dj_api_test.png]
Now lets try sending some POST
data and getting it in our view
.
Lets modify the api
function to look like
@csrf_exempt
def api(request):
name = request.POST.get("name") or "n/a"
email = request.POST.get("email") or "n/a"
return JsonResponse(
{'name':name,
'email' :email}
)
In the above sample we use request.POST.get("param")
to retrieve the POST data.
The annotation @csrf_exempt
is used to bypass CSRF
protection.
Disable
CSRF
should not be dont in production site. Unless you know what your are doing.
To use the @csrf_exempt
annotation we had to import it at the top of the views.py
file.
from django.views.decorators.csrf import csrf_exempt
Now using Postman
we can test this route.
[dj_post_request.png]
Lets look at how we can render HTML files. First head to the settings.py
file
in your base directory and make sure you app in in the list of installed applications.
[app_installed.png]
Once that is done we can create a templates directory inside our app directory. It will take the format as shown below
/templates/employees/index.html
This means you may have to create two folders. One called templates
and the other would be
the same as your app. In my case employees
. Then you can create the index.html
file and
add your html
content.
<h1>Hello World</h1>
<p>Welcome to wfTutorials</p>
Now in our views we create the welcome
function.
def welcome(request):
return render(request, 'employees/index.html')
Pretty simple. When we head to http://localhost:8080/employees/welcome/
we can see
the output.
[dj_render_html.png]
Say we wanted to display some dynamic content we could pass data from our views to our templates. To do this lets create an dictionary.
data = {'pagetitle' : 'wfTutorials', 'header' : "Welcome to wftutorials"}
Now we will pass this to our template as the third arugment in the render
function.
def welcome(request):
data = {'pagetitle' : 'wfTutorials', 'header' : "Welcome to wftutorials"}
return render(request, 'employees/index.html', data)
Its that simple. Now in our template we can access these values using {{ }}
double curly braces.
<title>{{ pagetitle }}</title>
<h1>{{ header }}</h1>
<p>Welcome to wfTutorials</p>
The result is shown below.
[dj_passing_data.png]
Lets see what we can do with templates. First we can primary template for all or some of your templates. So we don't have to rewrite the same code over and over.
In our templates
directory we create a file called primary.html
. This is our primary template.
<html>
<h1>This is the Primary layout</h1>
<div style="padding:15px; background: #fcfcfc; border: 1px solid red;">
{% block content %}
{% endblock %}
</div>
<footer>
<p> This is a template file footer</p>
</footer>
</html>
Take note of the following section with {% block content %}
{% block content %}
{% endblock %}
Other templates that extends the primary.html
template will be placed with this content
block.
Now in our views.py
we create our view
def faq(request):
return render(request, 'employees/faq.html')
Now lets add our html
to faq.html
{% extends "primary.html" %}
{% block content %}
<h4>This is the faq template</h4>
<p>Some text goes here</p>
{% endblock %}
In our faq.html
we first extend our view using the primary.html
layout. Then we
create a block content section.
The results can be seen below.
[dj_primary_layout.png]
You can add multiple content blocks in your layouts if you wish.
Below we change primary.html
to add a second content2
block.
<html>
<h1>This is the Primary layout</h1>
<div style="padding:15px; background: #fcfcfc; border: 1px solid red;">
{% block content %}
{% endblock %}
</div>
<div>
{% block content2 %}
{% endblock %}
</div>
<footer>
<p> This is a template file footer</p>
</footer>
</html>
Then in our faq.html
template we can add more data to this content block.
{% extends "primary.html" %}
{% block content %}
<h4>This is the faq template</h4>
<p>Some text goes here</p>
{% endblock %}
{% block content2 %}
<h4>Second Content Block</h4>
<p>Some text goes here</p>
{% endblock %}
The results are shown below.
[dj_multiple_content_blocks.png]
We can use conditionals within our templates lets see how. In our faq.html
template we
add the following code
{% if show %}
<p>Some text goes here</p>
{% else %}
<p>No text goes here</p>
{% endif %}
Above if show
we output a certain type of text else we output a default
text.
In our views.py
we can send the data to our template
def faq(request):
data = {'show': None}
return render(request, 'employees/faq.html', data)
[dj_conditional_layout.png]
We can use loops within our templates. Lets see how. In our faq.html
we can add
<ul>
{% for dog in dogs %}
<li> {{ dog }} </li>
{% endfor %}
</ul>
We are using the expression {% for x in y %}
where y
is the list and x
is the
a element in the list. We can use the double curly braces to access the element.
In our view we can see how we pass this data.
def faq(request):
myDogs = ["bobby", "pepper", "sugar"]
data = {'show': None, 'dogs': myDogs}
return render(request, 'employees/faq.html', data)
The results is shown below.
[dj_layouts_loops.png]
We can work with external assets like css frameworks by first create a static folder in our employees directory. Within the static folder we create a employees directory and within that folder we add bootstrap.css. Check the picture below to get an idea.
[dj_static_path.png]
Once we have our bootstrap.css
file we can create a new layout to use. We call it
the secondary.html
.
{% load staticfiles %}
<html>
<head>
<link href="{% static 'employees/bootstrap.css' %}" rel="stylesheet">
</head>
<body>
{% block content %}
{% endblock %}
</body>
</html>
Using {% load staticfiles %}
loads the files from the directory so we can access them.
We can then link to them as shown below
<link href="{% static 'employees/bootstrap.css' %}" rel="stylesheet">
Now in our views we create a new function.
def help(request):
return render(request, 'employees/help.html')
In in our help.html
we add some bootstrap structures and ofcourse extend the secondary.html
layout.
{% extends "secondary.html" %}
{% block content %}
<div class="container">
<div class="row">
<div class="col">
<h1>Help view</h1>
<div class="alert alert-success">
A simple primary alert—check it out!
</div>
<div class="alert alert-warning">
A simple secondary alert—check it out!
</div>
<ul class="list-group">
<li class="list-group-item">Cras justo odio</li>
<li class="list-group-item">Dapibus ac facilisis in</li>
<li class="list-group-item">Morbi leo risus</li>
<li class="list-group-item">Porta ac consectetur ac</li>
<li class="list-group-item">Vestibulum at eros</li>
</ul>
</div>
</div>
</div>
{% endblock %}
The results we can see below.
[dj_static_files.png]
Lets see how we can work with the database in Django. First lets create a model. Head to the models.py
file and start creating your model as a class.
In the models.py
file add the code below
class Employees(models.Model):
firstname = models.CharField(max_length=125)
lastname = models.CharField(max_length=125)
department = models.CharField(max_length=50)
employeeid = models.IntegerField()
email = models.CharField(max_length=65)
age = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
This is our model. We will be created an employees table with the following field names. You can view fmore field types here.
Now when you are finish adjusting your model you can run the following command
python manage.py migrate
[dj_run_migrations.png]
Once this is done you would have your models created in your database.
Django uses sqlite to start but you could always change this. In your settings.py
file you will see
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
You just need to update this dictionary to the right configuration for your MySQL or any other database. Check out this stackoverflow question to learn more.
Lets see how we can save data to your models. First you need and sqlite browswer. Check one out here.
Then open your db.sqlite3
database with it to see an overview of the records you created
so far.
[dj_sqlite_db.png]
Now lets create a view to save our data.
def saveemployee(request):
emp = Employees()
emp.firstname = "Wynton"
emp.lastname = "frannklin"
emp.email = "[email protected]"
emp.department = "IT"
emp.age = "25"
emp.employeeid = "005"
try:
emp.save()
return HttpResponse("Employee saved")
except Exception as e:
return HttpResponse(e)
In the above code we create the saveemployee
function. It creates and model
emp = Employees()
then it adds some default values and uses emp.save()
to save the data.
We use try
and except
to test for errors.
In our urls.py
we can add this new route
path('save/',views.saveemployee, name="emp_save")
Now when we test this we can see the results.
[dj_save_employee.png]
If you have any errors they will be displayed.
[dj_save_employee_error.png]
You can view the insert data using the sqlite browswer.
[dj_inserted_data.png]
We can find an row from the database using objects
. In our new view display
def display(request):
emp = Employees.objects.get(pk=1) # get the employee object
return HttpResponse("Employee name is: " + emp.firstname + " " + emp.lastname)
[dj_view_model_data.png]
We can do the same thing for any other attribute of the table. For eg the email
def display(request):
emp = Employees.objects.get(email="[email protected]")
return HttpResponse("Employee name is: " + emp.firstname + " " + emp.lastname)
We can delete an object using model.delete()
lets see how
def remove(request):
emp = Employees.objects.get(email="[email protected]")
try:
emp.delete()
return HttpResponse("object deleted")
except Exception as e:
return HttpResponse(e)
We create a new view called remove
that calls the delete function on
emp.delete()
.
We can change the data of an object and save it. This will update the object.
def update(request):
emp = Employees.objects.get(pk=1)
emp.age = "27"
try:
emp.save()
return HttpResponse("object age updated to " + emp.age)
except Exception as e:
return HttpResponse(e)
[dj_update_model.png]
Using forms we have a more intutitve way of woring with our models. It creates
an abstraction so we do need to work with models directely. Lets see how.
In our employees directory create a file called forms.py
.
Add the imports that we need
from django.forms import ModelForm
from .models import Employees, Departments
Now we can get started created our forms.
In forms.py
we add the following code
class EmployeeForm(ModelForm):
class Meta:
model = Employees
exclude = ('created_at','id')
class DepartmentForm(ModelForm):
class Meta:
model = Departments
fields = ('name', 'code')
Using the Meta
class you need to either add an exclude
or fields
option.
Exclue says what attributes to remove from the form. With fields you choose
what options you want to be shown in the form.
Now in our views.py
we import our forms.
from employees.forms import EmployeeForm, DepartmentForm
then we create a view for display the form.
def create(request):
empForm = EmployeeForm()
return render(request,'employees/create.html', {'form':empForm})
In our create.html
we can render our form using the secondary.html
layout so we can
use bootstrap
{% extends "secondary.html" %}
{% block content %}
<div class="container">
<h1>Create an Employee</h1>
<form action="{% url 'emp_create' %}" method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
{% endblock %}
Of important note is form.as_p
we could have just used form
but using form.as_p
wraps
each element in a p
element. Learn more here.
Some other things to note
- The
url "emp_create"
creates a url from the shorthand name - The
csrf_token
line is required to submit the form. - If you are not using layout templates you dont need to add the
extends
orblock content
sections.
The results is shown below
[dj_create_employee_form.png]
Now that we have our form up and running. We create going to submit the form and save our data.
We modify our views.py
function called create
to accept a post request.
def create(request):
if request.method == "POST":
empForm = EmployeeForm(data=request.POST)
if empForm.is_valid():
empForm.save()
return HttpResponse("Employee saved")
else:
empForm = EmployeeForm()
return render(request,'employees/create.html', {'form':empForm})
Above if the request.method
is post we load the data from the request into our EmployeeForm
.
Then if the form is valid we save it. Otherwise we display the form.
[dj_submit_form.png]
The results can be seen from the DB browser.
[dj_form_submit_results.png]
Lets see how we can update our database using a form. First we update our update
view in
views.py
.
def update(request, id):
if request.method == "POST":
emp = Employees.objects.get(pk=id)
empForm = EmployeeForm(request.POST, instance=emp)
if empForm.is_valid():
empForm.save()
return redirect(request.path)
else:
emp = Employees.objects.get(pk=id)
empForm = EmployeeForm(instance=emp)
return render(request, 'employees/update.html',{'form':empForm})
Lets review this code. First note we added the id
def update(request, id):
in the function call. This means this is required. Of course our route in urls.py
reflects this.
path('update/<id>/', views.update, name="emp_update"),
The first section in the update
function contains an if
statement check for
a POST
request. We the load the employee model and then we add the POST data
and the model in the EmployeeForm
constructure.
emp = Employees.objects.get(pk=id)
empForm = EmployeeForm(request.POST, instance=emp)
This way the form knows its needs to update and not create. Learn more here. The rest is rote. Just
check for validation and save. We return a redirect using the request.path
.
If there is not POST
request we just load the employee form from the employee model and display
the current data in the model.
emp = Employees.objects.get(pk=id)
empForm = EmployeeForm(instance=emp)
In our update.html
template note the request.path
in the form action paramter.
<form action="{{ request.path }}" method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">Submit</button>
</form>
We use request.path
so we can main the id
parameter when we submit the form.
So we can see the results below.
[dj_update_form_database.gif]
TO get started with authentication we need to have a login view. With Django this process
is made quite easy. Lets head to our urls.py
file. First we need to import some views.
At the top add this code
from django.contrib.auth.views import LoginView
Above we import the LoginView
from django. Now we can add path in our urls.py
file add the following lines
path('login/',LoginView.as_view(template_name="employees/login.html"),name="emp_login")
Above we add the login
path and we direct this path to the LoginView
. We have to provide
a template_name
in the LoginView.as_view()
function. We choose login.html
lets create
this.
<h1>Login Page</h1>
Our login.html
file is simple for now. Lets test this and see what happens
Note we did not add any views in
views.py
we did all this inurls.py
file.
[dj_simple_login_view.png]
Lets finish the login view. In login.html
add the following content.
<h1>Login Page</h1>
<form role='form' action="{% url 'emp_login' %}" method="post">
{% csrf_token %}
<p>Please Login</p>
{{ form.as_p}}
<button type="submit" class="btn btn-success">Sign in</button>
</form>
<br><br>
We have access to a login form in this login.html
template so we display it. The post
url
is our login url.
[dj_login_view_final.png]
Currently we have no users so the form will no work as it should. But it already comes with validation.
[dj_login_attempt.png]
Lets work on logging out users. We can head to the views.py
file and create a new
view.
def logout_view(request):
logout(request)
return HttpResponse("User logged out")
As seen above we have logout_view
. We are using a logout
function that we imported.
from django.contrib.auth import logout
We need to now add the url
path in the urls.py
path('logout/',views.logout_view,name="emp_logout")
[dj_logout_view.png]
Lets create a super user so we can test this login and logout views. To do this we run the following command.
python manage.py createsuperuser
[dj_create_super_user.png]
Once we answer the question we will have created a superuser.
Lets test the views we created. First we head to the login page view
http://localhost:8000/employees/login/
and we can attempt to login using the user
we just created.
Now in our main settings.py
file we need to tell Django where to go after we logged in.
LOGIN_REDIRECT_URL= "/employees/profile"
At the bottom of settings.py
we added the LOGIN_REDIRECT_URL
variable. This will tell
Django where to go after we login.
Now in our views.py
we have
def profile(request):
return render(request,'employees/profile.html',{'model':request.user})
Above we get the User
object from the request
object.
Now in our profile.html
we can add the code below to display our user information.
<h1>Showing the current user</h1>
<p>UserName: {{ model.username }}</p>
<p>Email: {{ model.email }}</p>
The results is shown below.
[dj_showing_loggedin_user.png]
Now since we can login we can implement access control on our views.
We can make begin logged in an required for certain views. To do this we need to import a
decorator. In our views.py
file we can add
from django.contrib.auth.decorators import login_required
Do this at the top of the file. Now on a view we want the user to login to access we can do
@login_required
def create(request):
if request.method == "POST":
empForm = EmployeeForm(data=request.POST)
if empForm.is_valid():
empForm.save()
return HttpResponse("Employee saved")
else:
empForm = EmployeeForm()
return render(request,'employees/create.html', {'form':empForm})
In the above code we add the @login_required
decorator for the create
view.
What happens with this is if we go to http://localhost:8000/employees/create
if the
user is not logged in Django will redirect to the login page. In order for this to happen
we need to tell Django where the login page is.
We can do this in the settings.py
file.
LOGIN_URL="/employees/login"
At the bottom of the settings.py
file we add the LOGIN_URL
as seen above.
Now when we head to employees/create
it sends us to the login page.
The results is shown below.
[dj_login_redirect_authentication.gif]
In a view we can check to see if a user is logged in by using user.is_authenticated
.
Lets see how. In our profile.html
we add some logic to test if the user is logged in
{% if user.is_authenticated %}
<h1>Showing the current user</h1>
<p>UserName: {{ model.username }}</p>
<p>Email: {{ model.email }}</p>
{% else %}
<h1>user is not authenticated</h1>
{% endif %}
If the user is not logged in we will display User is not authenticated otherwise we will show the username and email.
Django has by default an admin section. We can manage our users and models using the admin site.
Lets head to http://localhost:8000/admin/
we should be greated with a login page.
[dj_admin_login.png]
We already create a super user so we can log in.
[admin_create_user.png]
Under Authentication and Authorization we can create new users if we wanted to. We can also edit and delete users in this page.
We can add models we created to the admin site. Lets see how.
Head the the admin.py
file and add the following lines.
First import your models
from .models import Employees, Departments
Then register them use the code below.
admin.site.register(Employees)
admin.site.register(Departments)
Thats it we can now manage Employees
and Departments
from our admin page.
[dj_admin_models.png]
So now we know how to get started with web development using Django. We look routing, rendering views, working with the database, forms and access control. There is alot more Django can do this is just the basics.
Thanks for taking the time to learn with me.