Dummies Introduction to Django. Part 3 – Databases

The next big step our application needs is to talk to a database. For the purposes of this, I’ll assume you’re using Postgres.

Step 1 – Installing Postgres.

If you’re using a Linux system, then you can install Postgres using your distribution’s package manager. e.g.

# apt-get install postgresql

If you’re using Mac there are multiple ways you can install Postgres. Homebrew is one (“brew install postgresql”) or download Postgres.App which is a macOS application which contains Postgres and all the normal command line tools)

Step 2 – Setting up Postgres

In this step I’ll assume we’re starting with a brand new setup. There are multiple ways you could configure this, but this is how I would configure the Postgres environment for Django.

To start with, connect to the Postgres database engine with some kind of administrative privileges. On Linux, you might do this by connecting to the Postgres engine as the unix user “postgres”.

$ sudo - U postgres psql

First, we create a user that Django can connect to Postgres with.

postgres=# CREATE USER djangouser LOGIN UNENCRYPTED PASSWORD 'secret';

Next, we create a database:

postgres=# CREATE DATABASE djangodb WITH OWNER = djangouser;

Then we create a database schema to hold our tables:

postgres=# CREATE SCHEMA djangoschema AUTHORIZATION djangouser;

We give our Django user rights to use this schema:

postgres=# GRANT USAGE ON SCHEMA djangoschema TO djangouser;

And finally we make this schema the default one for our Django user by setting its schema search path:

postgres=# ALTER ROLE djangouser SET search_path TO djangoschema;

We can now test this all works by connecting to Postgres as our Django user and performing a couple of basic operations:

$ psql -W djngodb djangouser
djangodb=> CREATE TABLE test1 (id SERIAL, msg VARCHAR);
djangodb=> INSERT INTO test1 (msg) VALUES ('Hello World');
djangodb=> SELECT * FROM test1;
djangodb=> DROP TABLE test1;

If you get an error message connecting to postgres and you’re on linux, try the following command instead:

$ sudo -U postgres psql djngodb djangouser

Step 3 – Setting up Django connection

Now Postgres is ready for us, we are ready to tell Django about Postgres. First, we need to install a Django driver for Postgres. Within our Python virtual environment:

(Django_Project) $ pip install psycopg2--binary

Now we’ve installed the driver, we need to get Django to use it.

Edit the file project/settings.py and search for the section “DATABASES”. It currently says:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

We have to change this to use our Postgres database. Hopefully, all the settings we need should be self explanatory:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'djangodb',
        'USER': 'djangouser',
        'PASSWORD': 'secret',
        'HOST': 'localhost',
    }
}

Step 4 – Using the database

Now Django knows how to talk to our database, we need to actually use it.

We need to create a model – a mapping or representation of a database table into a python/Django data structure. These are stored in the application/models.py file. Edit the file main/models.py

from django.db import models


class Message(models.Model):
  msg = models.TextField()

  def __str__(self):
    return(msg)

This defines a database table called “message” with just one field/column called “msg” which is a text field. We’ve also defined the __str__ method to return the message field if we ever print out the class.

Note that we aren’t going to create any tables in Postgres ourselves: We’ll get Django to do that for us later on.

Next, we’ll need to create a new view for this feature. In views.py, add the following import line at the top:

from main.models import *

Then add the following function call at the bottom of the file:

def messages(request):
  messages = Message.objects.all()
  context = {
    'messages': messages,
  }
  return render(request, 'messages.html', context)

Add the following entry to the list of urlpatterns in the projects/urls.py file:

    path("messages", views.messages, name="messages"),

Finally, we create our view file in main/templates/messages.html

<!DOCTYPE html>
<html>
  <head>
    <title>List of messages</title>
  </head>
  <body>
    <h2>A list of messages</h2>
    <table border="1">
      <thead>
        <tr>
          <th>Message</th>
          <th>Action</td>
        </tr>
      </thead>
      <tbody>
       {% if messages is not None %}
         {% for message in messages %}
           <tr>
            <td>{{ message.msg }}</td>
            <td></td>
           </tr>
         {% endfor %}
       {% endif %}
      </tbody>
   </table>
  </body>
</html>

Now we’re almost ready to run our Django app. But before we can do that, we need to get Django to update our database schema. The following command should do the trick:

python (Django_Project)$ manage.py migrate

If you don’t get a series of “OK”s, check your database connections. Otherwise, you can go for “python manage.py runserver” and go to http://127.0.0.1:8000/messages and..Blank List of Messages

As we haven’t created any means (yet) to add messages, we’ll quickly add a couple direct into the database:

djangodb=> INSERT INTO main_message (msg) VALUES ('Message 1'), ('Message 2');

And reload the web page:

List of messages 1

Success!

Now we want to be able to add messages through the web page, rather than having to delve into SQL.

First, edit main/views.py and add “, redirect” to the initial import line so it now looks:

from django.shortcuts import render, redirect

Then add the following function to the bottom of the file:

def add_message(request):
   new_message = request.POST.get('newMessage')
   if new_message is not None:
     message = Message(msg=new_message)
     message.save()

   return redirect('messages')

Now, edit main/templates/messages.html and add the following section after the “</table>” tag (But before the “</body>” tag:

<h2>Add Message</h2>
    <form action="addMessage" method="post">
      {% csrf_token %}
      <input type="text" name="newMessage" />
      <input type="submit" value="Add Message" />
    </form>

Finally, edit the project/urls.py and add the following line to the urlpatterns section:

path("addMessage", views.add_message, name="add_message"),

*Phew*. Now, reload the page http://127.0.0.1:8000/messages and you should see the new add message table.

List of messages add button

Put some words of wisdom in the text box and click “Add Message” and you should now see your message in the table:

List of messages Hello World

Let’s add the final basic feature: Deleting a message.

First, edit the messages.html file, and in the blank table cell (which is in the column “Action”) add the following HTML code:

<form method="post" action="deleteMessage">
  {% csrf_token %}
  <input type="hidden" name="messageId" value="{{ message.id }}" />
  <input type="submit" value="Delete" />
</form>

Next, add the following method into views.py:

def delete_message(request):
   messageId = request.POST.get('messageId')
   try:
     msg =  Message.objects.get(**{'id': messageId})
     msg.delete()
   except Message.DoesNotExist:
     # Do something
     return redirect('messages')

   return redirect('messages')

Finally, add an entry to urls.py

 path("deleteMessage", views.delete_message, name="delete_message"),

Reload the /messages page in your browser and you should now see a Delete button next to each message:

List of messages with delete button

Hopefully, you should be able to click “Delete” on a message and it will disappear.

Alternative Python Model Organization

In the above example, we’d store all the database models in one file: models.py. But once you start getting a number of model classes, this file can start to get large and unwieldy. So there’s another way to organize the models.

In the application directory, create a directory called “models”. In that directory, create files containing your models. Finally, create a file called “__init__.py” that just has a series of import statements. e.g.

from .message import Message

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s