Basic tutorial for Django with Docker[Python3]


Django is the most famous web framework for Python.
In this article, I would like to share basic tutorial of Django for beginner.

🗽 Create Project Directory

mkdir PROJECT_DIR
cd PROJECT_DIR

🐹 Create requirements.txt

Create requirements.txt:

Django==2.0.7
django-bootstrap4==0.0.6

You will install following libraries:

🍄 Dockerfile

Add Dockerfile:

FROM python

# Environment variable
ENV PYTHONUNBUFFERED 1

# update apt-get
RUN apt-get update -y && apt-get upgrade -y

# install libraries
RUN apt-get install -y groff-base

# change work directory
RUN mkdir -p /app
WORKDIR /app

ADD requirements.txt /app/
RUN pip install -r requirements.txt

EXPOSE $PORT

CMD ["manage.py runserver 0.0.0.0:$PORT"]

😼 docker-compose.yml

Add docker-compose.yml:

version: '3.5'

services:
app:
build: .
tty: true
stdin_open: true
restart: always
expose:
- "8000"
ports:
- "8000:8000"
links:
- db
working_dir: /app
volumes:
- .:/app
- ./ssh:/root/.ssh
command: ./manage.py runserver 0.0.0.0:8000

db:
image: postgres:10.4

🤔 Start Docker container

Start a Docker container and use bash in the container by the following terminal commands:

docker-compose up -d
docker-compose exec app /bin/bash

If you want to stop the docker containers, please execute docker-compose down.

🐡 Create Django project

In docker container, please execute it:

django-admin.py startproject myapp
cd myapp

🐞 Create Application in the Project

Create new application in your project:

python manage.py startapp cms

🍮 Initial configuration

python manage.py migrate

🚌 Start server

python manage.py runserver

After then, please execute open http://$(docker-machine ip):8000/ in your local terminal(not docker container).

🎉 Configuration for project

Configure myapp/setting.py:

ALLOWED_HOSTS = ['DOCKER_MACHINE_IP'] # Please add docker machine ip

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'cms', # your application
'bootstrap4', # django-bootstrap4
]

🎳 Create superuser and logging into admin

# Input user id and password
python manage.py createsuperuser

You can logging into admin by open http://$(docker-machine ip):8000/admin with the above user id and password

😸 Admin configuration

Modify cms/admin.py:

from django.contrib import admin
from cms.models import Book, Impression


class BookAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'publisher', 'page',)
list_display_links = ('id', 'name',)


class ImpressionAdmin(admin.ModelAdmin):
list_display = ('id', 'comment',)
list_display_links = ('id', 'comment',)
raw_id_fields = ('book',)


admin.site.register(Book, BookAdmin)
admin.site.register(Impression, ImpressionAdmin)

🐠 Model

Modify cms/models.py:

from django.db import models


class Book(models.Model):
name = models.CharField('title', max_length=255)
publisher = models.CharField('publisher', max_length=255, blank=True)
page = models.IntegerField('page_number', blank=True, default=0)

def __str__(self):
return self.name


class Impression(models.Model):
book = models.ForeignKey(Book, verbose_name='book', related_name='impressions', on_delete=models.CASCADE)
comment = models.TextField('comment', blank=True)

def __str__(self):
return self.comment

🗻 Execute migration

# Create migration file
python manage.py makemigrations cms

# Confirm SQL of the migration
python manage.py sqlmigrate cms 0001

# Execute the Migration
python manage.py migrate cms

🐮 Views

Modify cms/views.py:

from django.shortcuts import render, get_object_or_404, redirect

from cms.models import Book
from cms.forms import BookForm


def book_list(request):
books = Book.objects.all().order_by('id')
return render(request,
'cms/book_list.html',
{'books': books})


def book_edit(request, book_id=None):
if book_id:
book = get_object_or_404(Book, pk=book_id)
else:
book = Book()

if request.method == 'POST':
form = BookForm(request.POST, instance=book)
if form.is_valid():
book = form.save(commit=False)
book.save()
return redirect('cms:book_list')
else:
form = BookForm(instance=book)

return render(request, 'cms/book_edit.html', dict(form=form, book_id=book_id))


def book_del(request, book_id):
book = get_object_or_404(Book, pk=book_id)
book.delete()
return redirect('cms:book_list')

Create cms/templates/cms/base.html:

{% load i18n static %}
<!DOCTYPE html>{% get_current_language as LANGUAGE_CODE %}
<html lang="{{ LANGUAGE_CODE|default:"en-us" }}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css">
{% block extra_css %}{% endblock %}
<title>{% block title %}My books{% endblock %}</title>
</head>
<body>
<div class="container">
{% block content %}
{{ content }}
{% endblock %}
</div>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.bundle.min.js"></script>
{% block extra_js %}{% endblock %}
</body>
</html>

Create cms/templates/cms/book_list.html:

{% extends "cms/base.html" %}

{% block title %}List of books{% endblock title %}

{% block content %}
<h4 class="mt-4 border-bottom">List of books</h4>
<a href="{% url 'cms:book_add' %}" class="btn btn-primary btn-sm my-3">Add</a>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Title</th>
<th scope="col">Publisher</th>
<th scope="col">Page number</th>
<th scope="col">Control</th>
</tr>
</thead>
<tbody>
{% for book in books %}
<tr>
<th scope="row">{{ book.id }}</th>
<td>{{ book.name }}</td>
<td>{{ book.publisher }}</td>
<td>{{ book.page }}</td>
<td>
<a href="{% url 'cms:book_mod' book_id=book.id %}" class="btn btn-outline-primary btn-sm">Edit</a>
<button class="btn btn-outline-danger btn-sm del_confirm" data-toggle="modal" data-target="#deleteModal" data-pk="{{ book.id }}" data-url="{% url 'cms:book_del' book_id=book.id %}">Delete</button>
</td>
</tr>
{% endfor %}

{# Confirmation dialog #}
<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="deleteModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteModalLabel">Confirmation</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
</div>
<div class="modal-body">
<p>Delete ID: <span id="del_pk"></span>?</p>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-primary" id="del_url">OK</a>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
</div>
</div>
</div>
</div>
</tbody>
</table>
{% endblock content %}

{% block extra_js %}
<script>
$(function() {
$('.del_confirm').on('click', function () {
$("#del_pk").text($(this).data("pk"));
$('#del_url').attr('href', $(this).data("url"));
});
});
</script>
{% endblock %}

Create cms/templates/cms/book_edit.html:

{% extends "cms/base.html" %}
{% load bootstrap4 %}

{% block title %}Edit book{% endblock title %}

{% block content %}
<h4 class="mt-4 mb-5 border-bottom">Edit book</h4>
{% if book_id %}
<form action="{% url 'cms:book_mod' book_id=book_id %}" method="post">
{% else %}
<form action="{% url 'cms:book_add' %}" method="post">
{% endif %}
{% csrf_token %}
{% bootstrap_form form layout='horizontal' %}
<div class="form-group row">
<div class="offset-md-3 col-md-9">
<button type="submit" class="btn btn-primary">Send</button>
</div>
</div>
</form>
<a href="{% url 'cms:book_list' %}" class="btn btn-secondary btn-sm">Back</a>
{% endblock content %}

🍣 Form

Create cms/forms.py:

from django.forms import ModelForm
from cms.models import Book


class BookForm(ModelForm):
class Meta:
model = Book
fields = ('name', 'publisher', 'page',)

😎 URL Routing

Create cms/urls.py

from django.urls import path
from cms import views

app_name = 'cms'
urlpatterns = [
# Book
path('book/', views.book_list, name='book_list'), # list
path('book/add/', views.book_edit, name='book_add'), # create
path('book/mod/<int:book_id>/', views.book_edit, name='book_mod'), # edit
path('book/del/<int:book_id>/', views.book_del, name='book_del'), # delete
]

Modify admin/urls.py:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
path('admin/', admin.site.urls),
path('cms/', include('cms.urls')),
]

🐝 Confirmation CRUD pages

http://$(docker-machine ip):8000/cms/book/
http://$(docker-machine ip):8000/cms/book/add/
http://$(docker-machine ip):8000/cms/book/mod/1/
http://$(docker-machine ip):8000/cms/book/del/2/

😀 References

🖥 Recommended VPS Service

VULTR provides high performance cloud compute environment for you. Vultr has 15 data-centers strategically placed around the globe, you can use a VPS with 512 MB memory for just $ 2.5 / month ($ 0.004 / hour). In addition, Vultr is up to 4 times faster than the competition, so please check it => Check Benchmark Results!!