Getting Started

Part 1, Chapter 3


Start by creating a new project directory called "taxi-app" to hold both the client and server application.

$ mkdir taxi-app && cd taxi-app

Then, within "taxi-app", create a new virtual environment to isolate our project's dependencies:

$ mkdir server && cd server
$ python3.7 -m venv env
$ source env/bin/activate
(env)$

The above commands may differ depending on your OS as well as your Python virtual environment tool (i.e., venv, virtualenvwrapper, Pipenv).

Install Django, Django REST Framework, Django Channels, channel_redis, Nose, pytest-asyncio, pytest-django, and Pillow, and then create a new Django project and app:

(env)$ pip install \
       channels-redis==2.4.0 \
       djangorestframework==3.9.2 \
       nose==1.3.7 \
       Pillow==6.0.0 \
       pytest-asyncio==0.10.0 \
       pytest-django==3.4.8
(env)$ django-admin.py startproject taxi
(env)$ cd taxi
(env)$ python manage.py startapp trips

Next, download and install Redis.

If you’re on a Mac, we recommend using Homebrew:

$ brew install redis

In a new terminal window start the Redis server and make sure that it is running on its default port, 6379. The port number will be important when we tell Django how to communicate with Redis.

$ redis-server

Then switch back to your original terminal window. Complete our project's setup by updating INSTALLED_APPS in the settings.py file within your code editor of choice:

# taxi/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework', # new
    'trips', # new
]

AUTH_USER_MODEL = 'trips.User' # new

Here, alongside the boilerplate Django apps, we added Django REST Framework along with our own trips app.

We also added an AUTH_USER_MODEL setting to make Django reference a user model of our design instead of the built-in one since we'll need to store more user data than what the standard fields allow.

Since we're creating this project from scratch, defining a custom user model is the right move. If we had made this change later in the project, we would have had to create a supplementary model and link it to the existing default user model.

Create a basic custom user model in the trips/models.py file.

# trips/models.py

from django.contrib.auth.models import AbstractUser


class User(AbstractUser):
    pass

Using this custom User model allows us to add fields to it later.

Then make our first migration:

(env)$ python manage.py makemigrations

Now we can run the Django management migrate command, which will properly set up our app to use our custom user model. All database tables will be created as well.

(env)$ python manage.py migrate

You should see something similar to:

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, trips
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0001_initial... 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 auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying trips.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying sessions.0001_initial... OK

Ensure all is well by running the server:

(env)$ python manage.py runserver

Then, navigate to http://localhost:8000/ within your browser of choice. You should see:

django landing page

Kill the server by typing Control+C (the Control and "C" key at the same time).

Let's set up the Django admin page next. Create a new superuser account with the createsuperuser management command. Choose a username, an email, and a password when prompted. (If Django deems your password "too common", it will warn you.)

$ python manage.py createsuperuser

Open your trips/admin.py file and replace the contents with the following code.

# trips/admin.py

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as DefaultUserAdmin

from .models import User


@admin.register(User)
class UserAdmin(DefaultUserAdmin):
    pass

Visit http://localhost:8000/admin/ in your browser and log in with your superuser credentials. Click on the "Users" link to see your admin user's record. (I chose [email protected] as my username.)

django landing page

Next, configure the CHANNEL_LAYERS by setting a default Redis backend and routing in the settings.py file. This can go on the bottom of the file.

# taxi/settings.py

REDIS_URL = os.getenv('REDIS_URL', 'redis://localhost:6379')

CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': {
            'hosts': [REDIS_URL],
        },
    },
}

Then, add Django Channels to the INSTALLED_APPS:

# taxi/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'channels', # new
    'rest_framework',
    'trips',
]

Try running the server again with python manage.py runserver. You should see the following error:

(env) $ python manage.py runserver
CommandError: You have not set ASGI_APPLICATION, which is needed to run the server.

Create a new file called routing.py within our taxi app:

# taxi/routing.py

from channels.routing import ProtocolTypeRouter

application = ProtocolTypeRouter({})

Are you wondering why we didn't pass any parameters to the ProtocolTypeRouter? According to the documentation, the app initializes an HTTP router by default if one isn't explicitly specified.

If an http argument is not provided, it will default to the Django view system’s ASGI interface, channels.http.AsgiHandler, which means that for most projects that aren’t doing custom long-poll HTTP handling, you can simply not specify an http option and leave it to work the "normal" Django way.

Open your taxi/settings.py file one last time. Find the WSGI_APPLICATION setting and below that line add the following.

# taxi/settings.py

ASGI_APPLICATION = 'taxi.routing.application'

Also, add a new asgi.py file alongside the wsgi.py file.

# taxi/asgi.py

import os
import django

from channels.routing import get_default_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'taxi.settings')
django.setup()
application = get_default_application()

Run with this for now. We'll look at what's happening here in an upcoming lesson.

Make sure the server now runs error-free.

(env) $ python manage.py runserver

There should no longer be any error messages.

Your directory structure should look like the following. (Virtual environment and other system files are excluded.)

.
└── server
    └── taxi
        ├── db.sqlite3
        ├── manage.py
        ├── taxi
        │   ├── __init__.py
        │   ├── asgi.py
        │   ├── routing.py
        │   ├── settings.py
        │   ├── urls.py
        │   └── wsgi.py
        └── trips
            ├── __init__.py
            ├── admin.py
            ├── apps.py
            ├── migrations
            │   ├── 0001_initial.py
            │   └── __init__.py
            ├── models.py
            ├── tests.py
            └── views.py

Before moving on, take a moment to review all that we've done thus far. Try to answer the "why" along with the "what" and "how". For example, why did we use Redis over an in-memory layer for Django Channels?




Mark as Completed