Build a Dashboard with Django and Bootstrap: Part 3

This is Part 3 of Building a Dashboard with Django and Bootstrap:

Introduction

If you follow the first part of this blog topic, you have a running Django dashboard.

But, the content ist still static. Lets review the current state:

Prepare our Django project

Right now, the whole content of our Django project is provided by the dashboard template

dashboard/template/site/base.html

Looking at our web site, you will see the different side menu items. So, intentionally, our web site should display different pages. And each page should provide the dynamic content.

The final goal of this part is to change our web app, so that each side item navigates us to a different page. For this, we have to take care about two things:

  • Navigation: how to we get to another page in our app
  • Project Structure: where to place the required components for each page

Basics of Navigation

Navigation usually is the process of getting from one page to another by clicking on a link.

So, we need to things:

  • the source page, containing the link
  • the destination page
  • the link, pointing to the destination page

Let’s take a look into the site template with the side menu:

The corresponding code in the side template is

<div id="collapseTwo" class="collapse" aria-labelledby="headingTwo">
	<div class="bg-white py-2 collapse-inner rounded">
		<h6 class="collapse-header">Custom Components:</h6>
		<a class="collapse-item" href="buttons.html">Buttons</a>
		<a class="collapse-item" href="cards.html">Cards</a>
	</div>
</div>

This is the result:

Linking to a html page is not possible, because Django does not work with html pages. Navigation in Django works with urls (in urls.py) and views in (views.py).

We must replace the html link tag (<a href="buttons.html">) with an Django-conform code. Read here for more details and the basics.

The idea behind the navigation is

Define the needed links/buttons
Give each link a name“buttons”
Define, which view to call for this linkcomponents/buttons/views.py
Tell Django, how to insert this link in a html page<a href="{% url 'buttons' %}">Buttons</a>

With this in mind, we change our site template for the side navigation (e. g. for the components menu):

But, if you save the template and try to view the web page, you will see this error:

We missed to tell Django, what to do when the corresponding link for this name is requested. We have to tell Django to use the view defined in buttons/views.py to generate the resulting view/page.

So, change the global url mapping file dashboard/urls.py

import dashboard.apps.components.buttons.views as ButtonsViews
import dashboard.apps.components.cards.views as CardsViews

urlpatterns = [
    path('', include('dashboard.apps.urls')),

    path('buttons', 	ButtonsViews.IndexView.as_view(),   name='buttons'),
    path('cards',       CardsViews.IndexView.as_view(),     name='cards'),

    path('admin/', admin.site.urls),
]

dashboard/apps/components/buttons/views.py

from django.shortcuts import render
from django.views import generic

class IndexView(generic.TemplateView):
    template_name = 'buttons/base.html'

dashboard/apps/components/cards/views.py

from django.shortcuts import render
from django.views import generic

class IndexView(generic.TemplateView):
    template_name = 'cards/base.html'

dashboard/apps/components/cards/templates/cards/base.html

{% extends 'site/base.html' %}
{% load static %}

{% block content %}
<h1 style="text-align: center">CARDS</h1>
{% endblock content %}

dashboard/apps/components/cards/templates/buttons/base.html

{% extends 'site/base.html' %}
{% load static %}

{% block content %}
<h1 style="text-align: center">BUTTONS</h1>
{% endblock content %}

Save everything and view at the resulting page

What happens, from showing the page, clicking on the link until you see the final result:

  • Django create the side menu item Cards
  • with the corresponding link localhost:9000/cards
  • which will be the destination page, if you click on the link
  • and finally, Django creates the desired page (through view.py)

Namespaces and structure

Now, think about the names, e. g. buttons and cards in our Components.

Your project is getting bigger and you plan to add an additional page info with general information for both Components and Utilities menu.

So, you will have to additional files, for example

  • dashboard/apps/components/info/views.py
  • dashboard/apps/utilities/info/views.py

The corresponding url mapping (urls.py) could look like this:

import dashboard.apps.components.info.views as ComponentInfoViews
import dashboard.apps.utilities.info.views as UtilitiesInfoViews

urlpatterns = [
    path('', include('dashboard.apps.urls')),

    path('info', ComponentInfoViews.IndexView.as_view(), name='info'),
    path('info', UtilitiesInfoViews.IndexView.as_view(), name='info'),

Two pages with the same name (info) in different views.py!

Of course, we could choose different names and link (component_info, utilities_info), but this not a good programming style.

We will choose a more modern way of programming

  • Spread the responsibility over separate, independent modules
  • Name these modules with different names

What does this mean? We will have

  • a separate module frontend, only responsible for the start page (frontend)
  • a separate module components, responsible for all components
  • a separate module utilities, responsible for all utilities
  • a separate module pages, responsible for all pages

Resulting folder structure and file content

File dashbard/urls.py

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

File dashbard/apps/urls.py

from django.urls import path
from django.urls.conf import include

from dashboard.apps.frontend.views import IndexView

app_name = 'app'

urlpatterns = [
    path('', IndexView.as_view(), name='index'),
    path('pages/', include('dashboard.apps.pages.urls')),
    path('components/', include('dashboard.apps.components.urls')),
    path('utilities/', include('dashboard.apps.utilities.urls')),
]

File dashbard/apps/components/urls.py

from django.urls import path

import dashboard.apps.components.buttons.views as ButtonsViews
import dashboard.apps.components.cards.views as CardsViews

app_name = 'components'

urlpatterns = [
    path('', ButtonsViews.IndexView.as_view(), name='index'),
    path('buttons/', ButtonsViews.IndexView.as_view(), name='buttons'),
    path('cards/', CardsViews.IndexView.as_view(), name='cards'),
]

File dashbard/apps/utilities/urls.py

from django.urls import path

import dashboard.apps.utilities.colors.views as ColorsViews
import dashboard.apps.utilities.borders.views as BordersViews
import dashboard.apps.utilities.animations.views as AnimationsViews
import dashboard.apps.utilities.others.views as OthersViews

app_name = 'utilities'

urlpatterns = [
    path('', BordersViews.IndexView.as_view(), name='index'),
    path('animations/', AnimationsViews.IndexView.as_view(), name='animations'),
    path('borders/', BordersViews.IndexView.as_view(), name='borders'),
    path('colors/', ColorsViews.IndexView.as_view(), name='colors'),
    path('others/', OthersViews.IndexView.as_view(), name='others'),
]

File dashbard/apps/pages/urls.py

from django.urls import path

import dashboard.apps.pages.blank.views as BlankViews
import dashboard.apps.pages.login.views as LoginViews
import dashboard.apps.pages.pagenotfound.views as PageNotFoundViews
import dashboard.apps.pages.password.views as PasswordViews
import dashboard.apps.pages.register.views as RegisterViews
import dashboard.apps.pages.charts.views as ChartsViews
import dashboard.apps.pages.tables.views as TablesViews

app_name = 'pages'

urlpatterns = [
    path('', ChartsViews.IndexView.as_view(), name='index'),
    path('blank', BlankViews.IndexView.as_view(), name='blank'),
    path('charts', ChartsViews.IndexView.as_view(), name='charts'),
    path('login', LoginViews.IndexView.as_view(), name='login'),
    path('pagenotfound', PageNotFoundViews.IndexView.as_view(), name='pagenotfound'),
    path('password', PasswordViews.IndexView.as_view(), name='password'),
    path('register', RegisterViews.IndexView.as_view(), name='register'),
    path('tables', TablesViews.IndexView.as_view(), name='tables'),
]

Let’s finally check the namespace structure:

$ find . -name urls.py
./dashboard/urls.py
./dashboard/apps/utilities/urls.py
./dashboard/apps/components/urls.py
./dashboard/apps/urls.py
./dashboard/apps/pages/urls.py

We create three levels for our namespaces:

Djane URL FileNamespace
./dashboard/urls.py
./dashboard/apps/urls.pyapp
./dashboard/apps/utilities/urls.py
./dashboard/apps/components/urls.py
./dashboard/apps/pages/urls.py
app:utilities
app:components
app:pages

These namespaces must be used in the template files, for example:

<a href="{% url 'app:components:buttons' %}" class="collapse-item" >Buttons</a>
<a href="{% url 'app:components:cards' %}" class="collapse-item" >Cards</a>
<a class="collapse-item" href="{% url 'app:utilities:colors' %}">Colors</a>
<a class="collapse-item" href="{% url 'app:utilities:borders' %}">Borders</a>
<a class="collapse-item" href="{% url 'app:utilities:animations' %}">Animations</a>
<a class="collapse-item" href="{% url 'app:utilities:others' %}">Other</a>

Install the Django Extensions for additional commands:

pip install django-extensions

Add Django Extensions to the INSTALLED_APPS

INSTALLED_APPS = [
    ...

    'django_extensions'
]

Show URLs and Namespaces (only for out apps, admin urls are removed)

python3 manage.py show_urls

Preparing required components and pages

In summary, these are the steps to create the desired folder structure:

mkdir -p dashboard/apps/components/buttons/templates/buttons
mkdir -p dashboard/apps/components/cards/templates/cards
mkdir -p dashboard/apps/pages/blank/templates/blank
mkdir -p dashboard/apps/pages/charts/templates/charts
mkdir -p dashboard/apps/pages/login/templates/login
mkdir -p dashboard/apps/pages/pagenotfound/templates/pagenotfound
mkdir -p dashboard/apps/pages/password/templates/password
mkdir -p dashboard/apps/pages/register/templates/register
mkdir -p dashboard/apps/pages/tables/templates/tables
mkdir -p dashboard/apps/utilities/animations/templates/animations
mkdir -p dashboard/apps/utilities/borders/templates/borders
mkdir -p dashboard/apps/utilities/colors/templates/colors
mkdir -p dashboard/apps/utilities/others/templates/others
python3 manage.py startapp buttons dashboard/apps/components/buttons
python3 manage.py startapp cards dashboard/apps/components/cards
python3 manage.py startapp blank dashboard/apps/pages/blank
python3 manage.py startapp charts dashboard/apps/pages/charts
python3 manage.py startapp login dashboard/apps/pages/login
python3 manage.py startapp pagenotfound dashboard/apps/pages/pagenotfound
python3 manage.py startapp password dashboard/apps/pages/password
python3 manage.py startapp register dashboard/apps/pages/register
python3 manage.py startapp tables dashboard/apps/pages/tables
python3 manage.py startapp animations dashboard/apps/utilities/animations
python3 manage.py startapp borders dashboard/apps/utilities/borders
python3 manage.py startapp colors dashboard/apps/utilities/colors
python3 manage.py startapp others dashboard/apps/utilities/others
echo "{% extends 'site/base.html' %}

{% block    content %}
{% endblock content %}"> base.html
cp base.html dashboard/apps/components/buttons/templates/buttons
cp base.html dashboard/apps/components/cards/templates/cards
cp base.html dashboard/apps/pages/blank/templates/blank
cp base.html dashboard/apps/pages/charts/templates/charts
cp base.html dashboard/apps/pages/login/templates/login
cp base.html dashboard/apps/pages/pagenotfound/templates/pagenotfound
cp base.html dashboard/apps/pages/password/templates/password
cp base.html dashboard/apps/pages/register/templates/register
cp base.html dashboard/apps/pages/tables/templates/tables
cp base.html dashboard/apps/utilities/animations/templates/animations
cp base.html dashboard/apps/utilities/borders/templates/borders
cp base.html dashboard/apps/utilities/colors/templates/colors
cp base.html dashboard/apps/utilities/others/templates/others
rm base.html

Each of the folders has the same structure, for example the buttons component. We will delete some unnecessary files

Replacing Projects with dynamic data

Replacing the static parts with dynamic content could be achieved by the following approach:

  • Identify the dynamic parts
  • Move these parts from the site template into the view template base.html of the component
  • Modify frontend view.py to generate dynamic content from data

The steps are the same for all components (all items of the side menu).

Find the

Identify dynamic parts in template

Create templates for side menu pages

For every side menu item, their is a corresponding html file in the install folder of the sb-admin-2 template:

Remember the environment variable we create in part 1 for the start of our project

DASHBOARD_ROOT=$(pwd)
cd $DASHBOARD_ROOT
find dashboard/apps install/sb-admin-2 -name *.html

Each template file base.html has a corresponding html file unter sb-admin-2. Look at the following table to find the mapping:

apps/components/buttons/templates/buttons/base.htmlsb-admin-2/buttons.html
apps/components/cards/templates/cards/base.htmlsb-admin-2/cards.html
apps/pages/blank/templates/blank/base.htmlsb-admin-2/blank.html
apps/pages/charts/templates/charts/base.htmlsb-admin-2/charts.html
apps/pages/login/templates/login/base.htmlsb-admin-2/login.html
apps/pages/pagenotfound/templates/pagenotfound/base.htmlsb-admin-2/404.html
apps/pages/password/templates/password/base.htmlsb-admin-2/forgot-password.html
apps/pages/register/templates/register/base.htmlsb-admin-2/register.html
apps/pages/register/templates/tables/base.htmlsb-admin-2/tables.html
apps/utilities/animations/templates/animations/base.htmlsb-admin-2/utilities-animation.html
apps/utilities/borders/templates/borders/base.htmlsb-admin-2/utilities-border.html
apps/utilities/colors/templates/colors/base.htmlsb-admin-2/utilities-color.html
apps/utilities/others/templates/others/base.htmlsb-admin-2/utilities-html

Each template base.html should have the following content:

{% extends 'site/base.html' %}

{% block    content %}
{% endblock content %}

And each corresponding view.py file should have the following content, only the template_name should be different (the name of the template base.html file)

from django.views import generic

class IndexView(generic.TemplateView):
    template_name = 'buttons/base.html'

So, for each template file, we have to

  • locate the corresponding html file from the install folder (see table above)
  • copy the content between these tags to the template file:
        <!-- Begin Page Content -->
        <div class="container-fluid">
        ....
        </div>
        <!-- /.container-fluid -->

Build a Dashboard with Django and Bootstrap: Part 2

This is Part 2 of Building a Dashboard with Django and Bootstrap:

Introduction

If you follow the first part of this blog topic, you have a running Django dashboard.

But, unfortunately, the content is still static. Let’s review the current state:

Perfect. We are done with the basic setup.

Still, some work to do, because our dashboard is only a static dashboard. All content is programmed in the dashboard template file dashboard/templates/site/sb-admin-2/base.html

For example, look at the cards with the earnings at the top:

To achieve a more dynamic content, we need to move the desired parts of the dashboard from the template file to the frontend view file.

We will do this by following these steps:

  • Identify the dynamic parts
  • Move these parts from the template into for frontend view template index.html
  • Modify frontend view.py to generate dynamic content from data

Identify dynamic parts

How to find the parts, which are dynamic.

One way is to ask:

  • Which parts should be on every page (unchanged) and
  • What should change on every page

You mostly get the same answers by the question:

  • What are the main components of a web page (including navigation and content)

For answer the first question, take a look at the current page and “name” the areas:

So, these “names” are also the answer for the 3. Question:

  • sidemenu
  • top bar
  • content

And maybe you find additional “names”

  • header
  • footer
  • top menu
  • left and right sidebar

Find identified parts in template

Next step is, to find the identified parts in our dashboard template

dashboard/templates/site/sb-admin-2/base.html

This is an easy step, because the developer of the SB Admin 2 template documented their template well:

Looking into the code of the template, you will find comment lines describing the content:

  • <!-- Sidebar -->
  • <!-- Topbar -->
  • <!-- Top Search -->
  • <!-- Top Navbar -->
  • <!-- Begin Page Content-->

So, it is obvious what do to next:

  • get the dynamic part (lines under)<!-- Begin Page Content-->
    the green box in the following image
  • move it to the frontend template
  • place some information in the dashboard template, that the real content should be displayed here
    the blue curly braces in the following image

This is the way, the template system of django works:

Let’s explain this with a simple example: the page title

We declare a title (which should be considered as the default title). And in the frontend page, we declare the title for this page (the frontend page).

To achieve this, we have to tell our template system the following:

Now, we do the same with the content:

Looking at our resulting page, nothing changes. This is the desired result, but how could we be sure, that we really change the structure?

Well, let’s make a test and try to include a different content in the dashboard template.

Change the lines, where we include the content into this:

{% block content_missing %}
MISSING CONTENT
{% endblock content_missing %}

Did you notice the other name of the content: content_missing?

Change the template, save the file and have a look at the result:

Change content back, so your template is working again:

{% block content %}
MISSING CONTENT
{% endblock content %}

The final step in Part 3 will be replacing all static content of the dashboard with dynamic content.

Build a Dashboard with Django and Bootstrap: Part 1

This is Part 1 of Building a Dashboard with Django and Bootstrap:

Introduction

Building a complete web app isn’t always an easy task. Designing and Implementing on both sides (backend and front-end) requires mostly a lot of knowledge. So, why don’t using tools or framework, which helps and makes our life easier. Django is one of these frameworks:

Django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design. Built by experienced developers, it takes care of much of the hassle of Web development

So, let’s get started.

Create project

For subsequent steps, we will remember our starting directory

DASHBOARD_ROOT=$(pwd)
echo $DASHBOARD_ROOT
... here you will see your current folder...

First step is to create our main Django project

django-admin startproject dashboard
cd dashboard
python manage.py migrate
python manage.py runserver 8080
python manage.py startapp app_base

View current project in browser

Create first apps

mkdir -p dashboard/apps/core
python manage.py  startapp Core dashboard/apps/core

mkdir -p dashboard/apps/frontend
python manage.py createapp Frontend dashboard/apps/frontend

Add new apps to project

Modify dashboard/settings.py

INSTALLED_APPS = [
   ...

    'dashboard.apps.core',
    'dashboard.apps.frontend',
]

Modify dashboard/urls.py

from django.urls import path

import dashboard.apps.frontend.views as views

urlpatterns = [
    path('', views.IndexView.as_view(), name='index'),
    path('admin/', admin.site.urls),
]

Create view

Modify view in apps/frontend/views.py

from django.shortcuts import render
from django.views import generic


class IndexView(generic.TemplateView):
    """
    IndexView:
    """
    module = 'indexView'
    template_name = 'frontend/index.html'

Create template for frontend View

Create template file dashboard/apps/frontend/templates/frontend/index.html

<h1>
Let's get started
</h1>

View current project in browser

Current folder Structure

So far, we have the following folder structure

Prepare for administrate your project

Create admin user

python manage.py createsuperuser
Username (leave blank to use 'user'): admin
Email address: admin@localhost
Password: 
Password (again): 
Superuser created successfully.

Add Bootstrap support

Download requires files for Bootstrap, jQuery ad PopperJS

mkdir -p install/bootstrap
wget  -q https://github.com/twbs/bootstrap/archive/v4.4.1.zip -O install/bootstrap/4.4.1.zip
wget  -q https://github.com/twbs/bootstrap/releases/download/v4.4.1/bootstrap-4.4.1-dist.zip -O install/bootstrap/4.4.1-dist.zip
rm -rf   dashboard/static/vendor/bootstrap/bootstrap-4.4.1-dist
unzip -q install/bootstrap/4.4.1-dist.zip -d       dashboard/static/vendor/bootstrap/
rm -rf   dashboard/static/vendor/bootstrap/4.4.1
mv       dashboard/static/vendor/bootstrap/bootstrap-4.4.1-dist dashboard/static/vendor/bootstrap/4.4.1
mkdir -p dashboard/static/vendor/jquery/3.4.1/js
wget -q https://code.jquery.com/jquery-3.4.1.min.js      -O dashboard/static/vendor/jquery/3.4.1/js/jquery.min.js
wget -q https://code.jquery.com/jquery-3.4.1.slim.min.js -O dashboard/static/vendor/jquery/3.4.1/js/jquery.slim.min.js
make install-popperjs
mkdir -p dashboard/static/vendor/popper/2.0.6/js
wget  -q https://unpkg.com/@popperjs/core@2/dist/umd/popper.min.js          -O dashboard/static/vendor/popper/2.0.6/js/popper.min.js
wget  -q https://unpkg.com/@popperjs/core@2.0.6/dist/umd/popper.min.js.map  -O dashboard/static/vendor/popper/2.0.6/js/popper.min.js.map

Create site templates in dashboard/templates/site

Add templates path to settings

File dashboard/settings.py

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            BASE_DIR + '/dashboard/templates',
        ],

Add static path to settings

File dashboard/settings.py

STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, APP_NAME + '/static')
]

Modify frontend view template

File dashboard/apps/frontend/templates/index.html

{% extends 'site/base.html' %}
{% load static %} {% block title %}Dashboard with Django and Bootstrap{% endblock title %}
{% block nav-style %}mkt-nav{% endblock nav-style %}

{% block content %}
<div class="jumbotron">
	<div class="container"></div>
		FRONTEND
	<div class="container full"></div>
</div>
{% endblock content %}

View current status of project

Add dashboard from an existing template

For a first start, we will use this sb-admin-2 dashboard template from here:

Download required files

Bootstrap templates consist of at least 3 different types of files

  • main index.html page, responsible for collection all elements and set up your page
  • CSS files defining the style of your page
  • JavaScript files, containing needed frameworks and code

So, first start by downloading the sample template from here. Be sure, you start in our project root folder:

cd $DASHBOARD_ROOT
git clone https://github.com/BlackrockDigital/startbootstrap-sb-admin-2 install/sb-admin-2

Next, find out, what we need for our template in addition to the file index.html

cd install/sb-admin-2
grep -E "<(link|script)" index.html 
  <link href="vendor/fontawesome-free/css/all.min.css" rel="stylesheet" type="text/css">
  <link href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i" rel="stylesheet">
  <link href="css/sb-admin-2.min.css" rel="stylesheet">
  <script src="vendor/jquery/jquery.min.js"></script>
  <script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
  <script src="vendor/jquery-easing/jquery.easing.min.js"></script>
  <script src="js/sb-admin-2.min.js"></script>
  <script src="vendor/chart.js/Chart.min.js"></script>
  <script src="js/demo/chart-area-demo.js"></script>
  <script src="js/demo/chart-pie-demo.js"></script>

That’s a lot of additional files.

To keep the file structure consistent, these files should be stored under the dash/board/static folder (same as the bootstrap files before).

To achieve this, there are two possibilities:

  • Create desired folder and copy each of the source files to the destination folder
  • Copy the complete static folder from the github repository for this post.

We use the second option to keep the focus on creating our dashboard.

So, first, clone the repository:

cd $DASHBOARD_ROOT/install
git clone https://github.com/rgoestenmeier/Dashboard_Django-Bootstrap

Then, copy the requred files

cd $DASHBOARD_ROOT
cp -R install/Dashboard_Django-Bootstrap/dashboard/static dashboard
cp -R install/Dashboard_Django-Bootstrap/dashboard/templates dashboard

Having everything needed for the dashboard template, the next step is to modify the front-end template

File dashboard/apps/frontend/templates/frontend/index.html

{% extends 'site/sb-admin-2/base.html' %}
{% load static %} {% block title %}Dashboard with Django and Bootstrap{% endblock title %}

{% block content %}
{% endblock content %}

View current project in browser

Perfect. We are done with the basic setup.

Still some work to do, because our dashboard is only a static dashboard. All content is programmed in the dashboard template file dashboard/templates/site/sb-admin-2/base.html

For example, look at the cards with the earnings at the top:

To achieve a more dynamic content, we need to move the desired parts of the dashboard from the template file to the front-end view file.

This will be described in the next step: Part 2: Prepare for dynamic content

Django | Cookbook

First steps

The following steps are based on a summary of the Django Tutorial

Create project

$ django-admin startproject main
$ cd working_with_django
$ python manage.py migrate
$ python manage.py runserver 8080
$ python manage.py startapp app_base

Create view

Create view in app_base/views.py

from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello, world. You're at the polls index.")

Add view to app_base/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
]

Add urls to project main/urls.py

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

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

Create admin user

$ python manage.py createsuperuser
Username (leave blank to use 'user'): admin
Email address: admin@localhost
Password: 
Password (again): 
Superuser created successfully.

Create data and database

Create database model in app_base/models.py

from django.db import models

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

Activating models in main/settings.py

INSTALLED_APPS = [
    'app_base.apps.AppBaseConfig',

    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
$ python manage.py makemigrations app_base
$ python manage.py sqlmigrate app_base 0001

Make app modifiable in the admin (app_base/admin.py)

from django.contrib import admin
from .models import Question

admin.site.register(Question)

Writing more views

Create views in app_base/views.py

def detail(request, question_id):
    return HttpResponse("You're looking at question %s." % question_id)

def results(request, question_id):
    response = "You're looking at the results of question %s."
    return HttpResponse(response % question_id)

def vote(request, question_id):
    return HttpResponse("You're voting on question %s." % question_id)

Add new views into app_base/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),

    path('<int:question_id>/', views.detail, name='detail'),
    path('<int:question_id>/results/', views.results, name='results'),
    path('<int:question_id>/vote/', views.vote, name='vote'),
]

Add template in app_base/templates/polls/index.html

{% if latest_question_list %}
    <ul>
    {% for question in latest_question_list %}
        <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}

Modify view in app_base/views.py

from django.shortcuts import render
...
def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    context = {'latest_question_list': latest_question_list}
    return render(request, 'polls/index.html', context)

Raising a 404 error in app_base/views.py

from django.http import HttpResponse
from django.shortcuts import render, get_object_or_404

from .models import Question
# ...
def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})

Create template app_base/templates/polls/detail.html

<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>

Removing hardcoded URLs in app_base/templates/polls/index.html

<li>
   <a href="{% url 'detail' question.id %}">{{ question.question_text }}</a>
</li>

The way this works is by looking up the URL definition as specified in the app_base/urs.py

...
# the 'name' value as called by the {% url %} template tag
path('<int:question_id>/', views.detail, name='detail'),
...

Namespacing URL names in app_base/urls.py

app_name = 'app_base'

urlpatterns = [
...

Then, modify link in app_base/templates/polls/index.html

from url ‘detail’ to url ‘app_base:detail’

<li>
    <a href="{% url 'app_base:detail' question.id %}">{{ question.question_text }}</a>
</li>

Use generic views: Less code is better

Create class in app_views/views.py

class HomeView(generic.TemplateView):
    template_name = 'index.html'

Create template app_views/templates/index.html

<h1>App Views:</h1>
Welcome

Modify app_views/urls.py

urlpatterns = [
    path('', views.HomeView.as_view(), name='home'),
]

Add another app to main project

Create app

$ python manage.py startapp app_view

Modify main/urls.py

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

Add data model in app_views/models.py

from django.db import models

class DataItem(models.Model):
    text = models.CharField(max_length=200)
    data = models.IntegerField(default=0)

    def __str__(self):
        return self.text

Register data in app_views/admin.py

from django.contrib import admin
from .models import DataItem

admin.site.register(DataItem)

Activate models

$ python manage.py makemigrations app_views
$ python manage.py sqlmigrate app_views 0001
$ python manage.py migrate app_views

Navigation / Redirection

Set root page of Django project

When accessing your Django project, the root page will normaly doesn’n show your app homepage.

To change this, you hate to modiy the url handling.

In the following sample, replace <appname> with the name of your app

Define a redirection view in your app (/<appname>/urls.py)

def redirect_to_home(request):
    return redirect('/<appname>')

Define path in the global urls.py (/main/urls.py)

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

from <appname> import views

urlpatterns = [
    path('',            views.redirect_to_home, name='home'),
    path('<appname>/',  include('<appname>.urls')),
    path('admin/',      admin.site.urls)
]

Highlight current page in navigation menu

<div class="list-group">
    <a href="{% url 'app:upload:basic' %}" class="list-group-item{% if request.path == '/upload/basic/' %} active{% endif %}">
            Basic Upload
    </a>
    <a href="{% url 'app:upload:progress' %}" class="list-group-item{% if request.path == '/upload/progress/' %} active{% endif %}">
            Progress Bar Upload
    </a>
</div>

Additional reading

Tutorials

Testing

Blogs and Posts

Resolving problems

Wrong template is used

The template system is using a search approach to find the specified template file, e.g. ‘home.html’.

If you created more than one apps with the same filenames for templates, the first one will be used.

Change the template folders and add the app name, e.g.

template/
        app_base/
                home.html

Resolving error messages and erors

‘app_name’ is not a registered namespace

One reason for this error is the usage of a namespace in a link.

Back to <a href="{% url 'app_views:home' %}">Home</a>

If you want to use this way of links, you have to define the namespace/appname in your <app>/urls.py file

app_name = 'app_views'
urlpatterns = [
    path('', views.HomeView.as_view(), name='home'),
]

dependencies reference nonexistent parent node

  • Recreate database and migration files
  • Remove all migration files under */migrations/00*.py
  • Remove all pycache folders under */__pycache__ and */*/__pycache__
  • Run migration again
$ python manage.py makemigrations
$ python manage migrate

ValueError: Dependency on app with no migrations: customuser

$ python manage.py makemigrations

Project Structure

Running tasks with Makefile

PREFIX_PKG := app

default:
	grep -E ':\s+#' Makefile

clearcache:	# Clear Cache
	python3 manage.py clearcache

run:		# Run Server
	python3 manage.py runserver 8000

deploy:		# Deploy
	rm -rf dist $(PREFIX_PKG)*
	rm -rf polls.dist
	cd polls && python3 setup.py sdist
	mkdir polls.dist && mv polls/dist/* polls/$(PREFIX_PKG)* polls.dist

install_bootstrap:	# Install Bootstrap Library
	cd .. && yarn add bootstrap
	rm -rf  polls/static/bootstrap
	mkdir   polls/static/bootstrap
	cp -R ../node_modules/bootstrap/dist/* polls/static/bootstrap

install_jquery:		# Install jQuery Library
	cd .. && yarn add jquery
	rm -rf polls/static/jquery
	mkdir  polls/static/jquery
	cp ../node_modules/jquery/dist/* polls/static/jquery

install_bootstrap_from_source:	# Install Bootstrap from Source
	mkdir -p install && \
	wget https://github.com/twbs/bootstrap/releases/download/v4.1.3/bootstrap-4.1.3-dist.zip -O install/bootstrap-4.1.3-dist.zip && \
	unzip install/bootstrap-4.1.3-dist.zip -d polls/static/bootstrap/4.1.3
Django Cookbook

Working with Django

Tutorial: Django-Polls App

We start with this amazing tutorial in building a Django polls app. The final code of the tutorial is here.

After this, we will extend the App with additional feature like Bootstrap and Angular 8.

Starting with Django

Installation with Pip

$ pip install Django
$ python -m django --version
$ python -c "import django; print(django.__file__)"
<BASEDIR>/python/lib/python3.7/site-packages/django/__init__.py

Install from Source

$ git clone https://github.com/django/django.git

Tutorial

https://docs.djangoproject.com/en/2.2/intro/tutorial02/

Configure development environment

Visual Studio Code

pip install pylint-django

Helpfull links and documentation

Links

https://www.fullstackpython.com/django.html

Tipps and Tricks

Customize 404 Page

If Django could not find a page for a given url, the following page ist used as error page:

<DJANGOINSTALLDIR>/views/templates/technical_404.html

Find the line with

{% for pat in pattern %}
{{ pat.pattern }}
{% if forloop.last and pat.name %}[name='{{ pat.name }}']{% endif %}
{% endfor %}

Replace the content with

{% for pat in pattern %}
<a href="{{ request.build_absolute_uri }}/{{ pat.pattern }}">{{ pat.pattern }}</a>
{% if forloop.last and pat.name %}[name='{{ pat.name }}']{% endif %}        
{% endfor %}

Resources

https://github.com/gothinkster/thinkster-django-angular-tutorial