Introduction to Django

Overview

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 you can focus on writing your app without needing to reinvent the wheel. It’s free and open source. The core Django framework can be seen as an MVC architecture. It consists of an object-relational mapper that mediates between data models and a relational database (Model), a system for processing HTTP requests with a web templating system (View), and a regular-expression-based URL dispatcher (Controller).

Project and Apps

Django comes with the script django-admin.py that can be used to create projects. During the installation of Django, a system variable will be added for it. So you can use the tool django-admin in every directory. To create a project, use the command django-admin startproject <project-name>, which creates a project structure for you. Usually, the created structure can already be used, but you can also customize it to make it more flexible and transparent. The modules manage.py, urls.py and settings.py are the most important inside the created structure. The Python module manage.py provides several functions to manage the project. With the command python manage.py help, you will see all options that you can use. The module urls.py contains a list of patterns that are used by the URL-resolver. Inside the module settings.py the settings for the web application will be defined. It’s highly recommended to have a settings-file for the development and a separated file for production. For our purpose, the following structure was chosen:

application
├── IAGOS
│ ├── apps
│ │ ├── api
│ │ ├── database
│ │ │ ├── components
│ │ │ ├── dataseries
│ │ │ └── deployments
│ │ └── workflow
│ │ │ ├── exports
│ │ │ ├── imports
│ │ │ ├── jobs
│ │ │ └── transfer
│ └── settings.py
├── scripts
├── static
├── templates
└── manage.py

In the directory IAGOS.apps all apps will be created. For the sake of clarity, it’s good practice to separate the apps logically. Therefore, all database apps will be added to the directory IAGOS.apps.database. You can create an app with the command python manage.py startapp <appname>. During the execution, a subdirectory will be made with the name of the app. To use the app in the project, you have to add the app to the list INSTALLED_APPS in the settings-files. As the project structure, the app structure will also be customized. The generated file tests.py will be splitted into the files test_models.py, test_forms.py, and test_views.py. These files will be stored in the subdirectory tests. With this approach, you have a better overview of the tests. It’s also good practice to create an own URL-file for each app and merge them in the project URL file. Furthermore, the files errors.py, filters.py, and forms.py will be added. As a result every app has the following structure:

deployments
├── tests
│ ├── test_forms.py
│ ├── test_models.py
│ └── test_views.py
├── admin.py
├── apps.py
├── errors.py
├── filters.py
├── forms.py
├── models.py
├── urls.py
└── views.py

All tests will be implemented in the subdirectory deployments.tests. To implement forms that can interact with users, use the module forms.py. In the module admin.py, all properties, that should be displayed in the admin menu, can be defined.

Tables and Constraints

To implement the tables and constraints with the framework Django, you have to import the module django.db.models. Every class-based implementation of a table (also known as Model) must be inherit from the base class django.db.models.Model. The attributes of the class represent fields in the table. If the primary key is not set, Django generates the field id for it. Django provides an automatically generated database-access API for each model that makes it possible to create, retrieve, update, and delete entries. In the following, an example implementation of the table project will be presented:

from django.db import models
from django.db.models import CheckConstraint
from django.db.models import Q
from django.db.models import F

class Project(models.Model):
    name = models.CharField(unique=True, max_length=32)
    long_name = models.CharField(unique=True, max_length=128)
    description = models.TextField()
    start = models.DateTimeField(null=False, blank=False)
    end = models.DateTimeField(null=False, blank=False)
    is_infrastructure = models.BooleanField(default=False)

    class Meta:
        db_table = "project"
        constraints = [
            CheckConstraint(check=Q(start__lt=F("end")),
                            name="project_period")
        ]

    def __str__(self):
        return "{0}".format(self.short_name)

Every model has a metaclass that describes the metadata. The attribute db_table defines the name of the table. If a name isn’t defined, Django will generate a name. Moreover, you can define constraints for the model. In the following code section, the constraint defines that the project’s start date must be before the end date of the project. Some constraints can also be defined in the field declaration. For example, the parameter unique defines that the attribute must be unique. If you want to implement complex constraints, you can override the super-method save().

Testserver and Migration

Django provides a web server for developing. The web server is useful because you don’t have to install your own web server, and it restarts automatically by changes. To start the server, change to the root directory and execute the command python manage.py runserver. If the server is running, you can reach it under http://127.0.0.1:8000/. Django provides an admin area where you can handle the data. Before you can access the admin area, you need to create a super-user. Therefore execute the command python manage.py createsuperuser. You can set up the database with the following commands:

  1. python manage.py makemigrations

  2. python manage migrate

  3. python manage createsuperuser

After this, you can start the server and reach the admin area under http://127.0.0.1:8000/admin

Automated Testing

In computer programming, unit testing is a software testing method by which individual units of source code-sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures-are tested to determine whether they are fit for use. Unit tests are typically automated tests written and run by software developers to ensure that a section of an application (also known as the unit) meets its design and behaves as intended. In object-oriented programming, a unit is often an entire interface, such as a class, but could be an individual method. To isolate issues that may arise, each test case should be tested independently. Substitutes such as method stubs, mock objects, fakes, and test harnesses can be used to assist testing a module in isolation. During development, a software developer may code criteria, or results that are known to be good, into the test to verify the unit’s correctness. During test case execution, frameworks log tests that fail any criterion and report them in a summary. The following listing shows an example of a unit test to check the functionality of a constrain.

Django provides the base class django.test.TestCase to implement unit tests. Every test class has to inherit from the base class. To create a relation between the models and the tests, it’s good practice to name the class ModuleNameTest. Note that every test function and every test module must start with the prefix test. Otherwise, the test will not be detected by Django. In the following listing, the date constraint of the project will be tested. Note that the start date and the end date of the project are switched to force an IntegrityError. If an error is raised, the test passes. To run all tests, execute the command python manage.py test.

from datetime import datetime
from django.test import TestCase
from django.db.utils import IntegrityError
import pytest

from IAGOS.apps.database.deployments import models as deployments

class ProjectTest(TestCase):

    @pytest.mark.django_db(transaction=True)
    def test_project_period(self):
        short = "IAGOS"
        name = "In-Service Aircraft for a Global Observing System"
        start = datetime.now()
        end = datetime(2015, 1, 1)
        with pytest.raises(IntegrityError):
            with transaction.atomic():
                deployments.Project.objects.create(name=short, long_name=name, start=start, end=end)

References