Day 1: Setup and design Backend

What will we build ?

This tutorials will focus on how to create a real world mobile chat application with Ionic 5 / Angular 12, Capacitor 3 and Django 3. The Chat application will manage private chat between two persons and is not a group chat application.

For this application, we can list these main functionalities :

  • Login / Register / Forget password screens
  • List of chats screen with search user features
  • Chat screen : real time message with WebSocket, loading previous messages, sending attachments
  • Receiving push notifications

So this is what this book is about: We will learn each steps required to create a real world chat mobile application with it’s associated backend in a day to day process.

On backend, we will use Django and Django channels to have a socket based chat and we will also learn how to save and retrieve chat history.

On frontend, we will develop a Ionic application with angular framework, which will communicate with our backend to manage the chats. we will deploy the application on mobile devices using Capacitor.

Ready to start ?

Day 1 : setup and design Backend

First we will focus on the backend development before diving into the Ionic part.

You can find the source code for this day one journey in GitHub

Setup : Software required

Before diving into the subject of this book, we need to install software and set up our environment for the backend part. If you are familiar with Django development you can skip this section.

Python 3

All the development will be done with python 3 so i will assume that python 3 is already install on your computer. If not, you will find tutorials on the internet on how to install it.

Pip

PIP is the Python Package Installer and is really helpful to install Django and all the libraries required to develop any Django project.

If you are on a Mac just open a terminal and enter

sudo easy_install pip

Virtualenv

Virtualenv is a tool to create isolated python environment. It is a recommended and standard way to develop Django projetcs. So let’s begin.
First go in a directory where you would like to create the backend.

cd Programmation
mkdir ChatTuto
mkdir ChatTuto/Backend
mkdir ChatTuto/Frontend

I create a ChatTuto directory and inside this directory, i create the Backend in which we will develop our backend and a Frontend directory in which we will develop the Ionic application.

To install virtualenv

sudo pip install virtualenv

And now we create the virtual environment into a directory named venv

virtualenv -p python3 venv

Once installed, we need to activate the environment using the command:

source venv/bin/activate

Django

We can install Django 3

pip install Django==3

And now we can create our django backend which will be called chattuto

django-admin startproject chattuto
cd chattuto

Ok now we can begin to implement our models that will be used to deal with our Chat application.

Conception

For each mobile app that you need to develop, the process is always the same. Starting from the storyboard of the application, we can deduce which entities will be needed. From these entities, we can conceptualize the models, the back-office (web administration) required to manage data and finally as last step, design the API which will be consumed by the frontend application.

Usually, i don’t recommend to implement the API without having seen application storyboards. Because it is better to implement API endpoints that will fulfill each screen requirements and then improve drastically the performance.
If you don’t proceed that way, to provide data to a screen, then may be you will need to make multiple http requests whereas only one request could have done the job.

For our Chat application, we need to display a list of chats and messages inside a choosen chat. And of course a chat is between two users, so we can easily deduce the following models:

  • User
  • Chat
  • Message

This is a good start to design our Django models.

Designing models

We need to create our first app in Django to declare our models and then we will add a backoffice which will be used by the administrator of the mobile application. To create this chat application in Django:

python manage.py startapp chat

Inside the newly created chat directory, Django will initialize some standard files for us. At this stage, we are interested with the models.py file.

I didn’t mentioned earlier but i use PyCharm to develop all my Django projects. You will find a community edition (FREE to use) or a PRO edition. But you can also use Eclipse with PyDev plugin, or VSCode. It’s really up to you.

User model

Most of mobile application have users who can subscribe or login, so this will be the first entity we need. By default, Django includes a User model entity and all required methods to deals with authentification.

But by default in a Django project, the field used to manage authentication is a username whereas for a mobile application it makes more sense to use an email for authentication process.

However it is possible to easily modify the Django default mechanism to use an email. To do so we have to extend the default User entity.

So let’s do it:

class UserManager(BaseUserManager):
    use_in_migrations = True

    def _create_user(self, email, password, **extra_fields):
        """
        Creates and saves a User with the given email and password.
        """
        if not email:
            raise ValueError('The given email must be set')
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_user(self, email, password=None, **extra_fields):
        extra_fields.setdefault('is_superuser', False)
        return self._create_user(email, password, **extra_fields)

    def create_superuser(self, email, password, **extra_fields):
        extra_fields.setdefault('is_superuser', True)

        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self._create_user(email, password, **extra_fields)

class User(AbstractBaseUser, PermissionsMixin):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    email = models.EmailField(_('email address'), unique=True)
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
    last_name = models.CharField(_('last name'), max_length=30, blank=True)
    date_joined = models.DateTimeField(_('date joined'), auto_now_add=True)
    is_active = models.BooleanField(_('active'), default=True)
    is_staff = models.BooleanField(_('active'), default=True)
    avatar = models.ImageField(upload_to='avatars/', null=True, blank=True)
    lastConnexionDate = models.DateTimeField(null=True, blank=True)
    valid = models.BooleanField(default=True)

    objects = UserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)

    @property
    def last_login(self):
        return self.lastConnexionDate

    def __str__(self):
        return u'%s' % (self.email)

Now User can be authenticated with email because we set

USERNAME_FIELD = 'email'

The lastConnexionDate can be used to track last connexion of the user into the mobile application.

Chat models

A chat is a dialog conversation between two users. The dialog is composed of a list of text messages but it also can be something else than text such as photos, videos, …
We can deduce that we will need two more models to store Chats and Messages.

class Chat(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    fromUser = models.ForeignKey(User, db_index=True,on_delete=models.SET_NULL, null=True,related_name="fromuser")
    toUser = models.ForeignKey(User, db_index=True,on_delete=models.SET_NULL, null=True,related_name="toUser")
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)
    unique_together = (("fromUser", "toUser"),)

    def __str__(self):
        return u'%s - %s' % (self.fromUser,self.toUser)

class Message(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    refChat = models.ForeignKey(Chat, db_index=True,on_delete=models.CASCADE)
    message = models.TextField()
    msg_type = (
        (0, "TEXT"),
        (1, "GEOLOC"),
        (2, "PHOTO"),
    )
    type = models.IntegerField(choices=msg_type, default=0)
    extraData = models.CharField(default='', null=True, blank=True, max_length=255)
    author = models.ForeignKey(User, db_index=True,related_name='author',on_delete=models.SET_NULL,null=True)
    isRead = models.BooleanField(default=False)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)

    def __str__(self):
        return u'%s - %d' % (self.refChat,self.type)

A chat must be unique between two users so we set a unique_together key based on users primary keys.

A message refers to a chat, can be a text, a geolocation position (using latitude/longitude) or a photo. We could extend this message type based on the application requirements like videos, sound… And of course a message has an author (the User who wrotes the message)

Setup database

To create our models, we need to install a database. I will use PostgreSQL with postgis extension.

On Mac, you will need Postres software and you can use Postico on your local machine while developping. It’s quite easy to use and install.

On Windows or Linux, you will have to setup your database which can be quite painful. To connect to your database, you can use the free DBeaver software.

I will assume that this step is done and will move forward on to next section.

I strongly recommend to use Clever cloud services, a french provider that i will use for these tutorials. With Clever cloud services, you can focus on your development features and not architecture. They offer a FREE database hosting for development. It’s like Heroku but cheaper and more performant.

Configure Django project

We can configure our Django project to use our database. First we will need the psycopg2 package which is the PostgreSQL python driver. Let’s add it to our requirements.txt file

Django==3.1.7
pillow==8.0.1
psycopg2==2.8.5 --no-binary psycopg2

I’m using the non binary version to be sure that the driver will be accurate with the server system libraries (it was a recommendation from the Clever cloud team).

As you could have notice, i’m always specifiying the version of the library that i want to use. If you don’t do that while deploying your project on a server, the latest version of libraries will be installed and could lead to a bug (if something changed in a library). Specifying the version will avoid that.

Don’t forget to install the libraries

pip install -r requirements.txt

Now, we can configure Django to use our database. We edit the settings.py file inside the chattuto directory and will replace the DATABASES section with:


AUTH_USER_MODEL = "chat.User"

# Database
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
POSTGRESQL_ADDON_URI = os.getenv("POSTGRESQL_ADDON_URI")
POSTGRESQL_ADDON_PORT = os.getenv("POSTGRESQL_ADDON_PORT")
POSTGRESQL_ADDON_HOST = os.getenv("POSTGRESQL_ADDON_HOST")
POSTGRESQL_ADDON_DB = os.getenv("POSTGRESQL_ADDON_DB")
POSTGRESQL_ADDON_PASSWORD = os.getenv("POSTGRESQL_ADDON_PASSWORD")
POSTGRESQL_ADDON_USER = os.getenv("POSTGRESQL_ADDON_USER")
REDIS_URL= os.getenv("REDIS_URL")
DATABASES = {
    'default': {
        'ENGINE': 'django.contrib.gis.db.backends.postgis',
        'NAME': POSTGRESQL_ADDON_DB,
        'USER': POSTGRESQL_ADDON_USER,
        'PASSWORD': POSTGRESQL_ADDON_PASSWORD,
        'HOST': POSTGRESQL_ADDON_HOST,
        'PORT': POSTGRESQL_ADDON_PORT,
        'CONN_MAX_AGE': 1200,
    }
}

I’m getting my database connexion parameters from environment variables and then set the databases dictionary keys with these values.

Using environment variables it’s easy to switch from a development database to a production database. Values are not hardcoded.

Please notice the line AUTH_USER_MODEL = "chat.User". Since we override the default Django User entity, this line is very important and tells Django that it needs to use our custom model.

With PyCharm you can declare your environment variables in the settings of your project, or you can use a environment.env file and use this plugin to declare them, which is more easy.

Within my environment.env file, i declare my database connection parameters like this:

POSTGRESQL_ADDON_URI=postgresql://u6rogoiqgpupp3mvhqi9:F1Kc6J1pKEt5X28iX4Fn@bx8gb6agpf58jh3rtupa-postgresql.services.clever-cloud.com:5432/bx8gb6agpf58jh3rtupa
POSTGRESQL_ADDON_PORT=5432
POSTGRESQL_ADDON_HOST=bx8gb6agpf58jh3rtupa-postgresql.services.clever-cloud.com
POSTGRESQL_ADDON_DB=b9pgqzgccizfpklha4gv
POSTGRESQL_ADDON_USER=u6rogoiqgpupp3mvhqi9
POSTGRESQL_ADDON_PASSWORD=<YOURPASSWORD>
DEBUG=True
MEDIA_URL_PREFIX=/media
STATIC_URL_PREFIX=/static

Don’t forget to replace these values with your own database connexion values

We could also do the same for the DEBUG variable

DEBUG = os.getenv("DEBUG")

and add the DEBUG value in our environment.env file:

DEBUG=True

We can try to run our Django project with PyCharm and if database connexion is OK, we should see the following:

Performing system checks...

System check identified no issues (0 silenced).

You have 17 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
August 07, 2020 - 11:24:32
Django version 3.0, using settings 'chattuto.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

To launch Django with command line, you will need to export your environment variables and then run in a terminal:

python manage.py runserver

To export my environment variables, i usually use a text file and then source it (on Mac or Linux).

Create database tables

At this point, Django tells us that we need to run migrations. You can learn more about migrations here but basically it means that we have modifications in our models.py file that are not reflected on the database which is true since we haven’t created it yet.

First we need to edit again the settings.py file to add our application chat to the INSTALLED_APPS dictionnary:

# Application definition
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'chat'
]

Now because we have overrided the default Django User, we first need to make a migrations with our chat application (which will tell Django where to find the User model) and then we can migrate to create all other tables required by Django.
So let’s do it by typing in a terminal:

python manage.py makemigrations chat
python manage.py migrate

Implement Chat server with Websockets and django channels

This content is for members only ! You can learn more here here to access the content

Christophe Surbier