Day Log App in Django Part 1: Day Log model

This is a Django implementation of the original Laravel application.

The scope of this post is from creation of the project to the test-driven development of the Daylog model.

Table of Contents

Steps

Please refer to the official tutorial for the necessary applications to start Django development.

Create project

Make a folder to contain the project and go inside it. I chose daylogapp-django.

Create the Django project with:

django-admin startproject api

api is the name of the project and the command should have the following structure:

daylogapp-django/
└── api*/
    ├── api/
    │   ├── __init__.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    └── manage.py

NOTE: *In virtue of the original project I renamed the topmost api folder to daylogapp. This will not affect other parts of the project.

Django can have many applications inside that can interact with each other and this initial project api will be the “central command”. New apps of the project will be registered in this api that will be discussed later.

Create daylog app

Go to the daylogapp folder with the file manage.py and run:

python manage.py startapp daylog

The folder daylog should be present after. Now we have two “apps” - api and daylog.

Next, go to api/settings.py to register our new app in the INSTALLED_APPS variable together with the Django Rest Framework:

    'rest_framework',

    'daylog',

Create Daylog model

Create test case

Go to daylog/tests.py and add the following:

from django.test import TestCase

from .models import Daylog

class ModelTests(TestCase):

    def setUp(self):
        self.daylog_title = "Lorem ipsum dolor sit amet"
        self.daylog_slug = "lorem-ipsum"
        self.daylog_location = "ABC XYZ-112233"
        self.daylog_log_at = "2017-01-01"
        self.daylog_category = Daylog.CATEGORY_ADEQUATE

        self.daylog = Daylog(
            title = self.daylog_title,
            slug = self.daylog_slug,
            location = self.daylog_location,
            log_at = self.daylog_log_at,
            category = self.daylog_category,
        )

    def test_model_can_create_a_daylog(self):
        old_count = Daylog.objects.count()
        self.daylog.save()
        new_count = Daylog.objects.count()

        self.assertTrue(old_count < new_count)

The test will check if a Daylog instance is actually created after saving.

If we run the test, we should expect that it will fail:

$ python manage.py test
Creating test database for alias 'default'...
E
======================================================================
ERROR: daylog.tests (unittest.loader._FailedTest)
----------------------------------------------------------------------
ImportError: Failed to import test module: daylog.tests
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/loader.py", line 428, in _find_test_path
    module = self._get_module_from_name(name)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/loader.py", line 369, in _get_module_from_name
    __import__(name)
  File "/path/to/daylogapp-node/daylogapp/daylog/tests.py", line 3, in <module>
    from .models import Daylog
ImportError: cannot import name 'Daylog'


----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)
Destroying test database for alias 'default'...

Define model structure

The structure of the Daylog is the same as the previous implementations. Go to daylog/models.py and add:

from django.db import models

class Daylog(models.Model):
    CATEGORY_ADEQUATE = "Adequate"
    CATEGORY_MINOR = "Minor"
    CATEGORY_MAJOR = "Major"
    CATEGORY_CHOICES = (
        (CATEGORY_ADEQUATE, CATEGORY_ADEQUATE),
        (CATEGORY_MINOR, CATEGORY_MINOR),
        (CATEGORY_MAJOR, CATEGORY_MAJOR),
    )

    title = models.CharField(max_length=255, blank=False)
    slug = models.CharField(max_length=50, blank=False, unique=True)
    location = models.CharField(max_length=100, blank=False)
    log_at = models.DateField(blank=False, unique=True)
    category = models.CharField(max_length=25, choices=CATEGORY_CHOICES, blank=False)

    created_at = models.DateTimeField(auto_now_add=True)
    modified_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return "%s: %s" % (self.log_at, self.title)

CATEGORY_CHOICES is just a constant attribute and the __str__ method was overriden to expect a string representation similar to <Daylog: 2016-01-01: Lorem ipsum>.

Migrate tables

There are two succeeding commands required whenever any changes in the models are made: makemigrations and migrate.

$ python manage.py makemigrations
Migrations for 'daylog':
  0001_initial.py:
    - Create model Daylog
$ python manage.py migrate
Operations to perform:
  Apply all migrations: auth, contenttypes, sessions, admin, daylog
Running migrations:
  Rendering model states... DONE
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying daylog.0001_initial... OK
  Applying sessions.0001_initial... OK

Rerun tests

The model test case should pass:

$ python manage.py test
Creating test database for alias 'default'...
.
----------------------------------------------------------------------
Ran 1 test in 0.002s

OK
Destroying test database for alias 'default'...

References

Twitter, LinkedIn