Last week we went through the second stage of the rather painful process of setting up a simple Django example.  For most people, this is not really a simple process, and if you have managed to survive through it, you have done well.

A project from scratch

The quick summary of beginning to set up a Django project from scratch is quite a long list:

  1. Install a database server
  2. Create a database to work with
  3. Install Python
  4. Install database drivers and Django
  5. Create a Django project (startproject)
  6. Create a Django app (startapp)
  7. Set up the Django project and app as necessary for database access
  8. “Migrate” (create necessary database tables, etc.)

Of course, the amount of work depends on where you are in the process.  If you already have access to a suitable database and Python installed on your computer, it can seem a much more achievable task.  Regardless of where you are in your practice or understanding, the hints in the last two newsletters should be able to help if you get stuck.  If you need more help, feel free to ask by return email.

One difference…

In our original consideration of this example as a data modelling problem, we did not specify a primary key or unique identifier.  Primary keys or uniqueness constraints are useful for avoiding the  duplication of data.  Did you notice in our table description in the previous newsletter that an “id” column had been added automatically by Django?  This column with its unique identifier for each row is provided automatically by Django, but there is no attempt made to avoid any data duplication at all.  We will discuss this further in a later newsletter.

Create/Insert pots

When we worked directly with database tables in earlier newsletters, we didn’t show the process of inserting the rows because we were discussing the concepts and content more than the process.  Furthermore, the process can vary between databases and it would have been very easy to get bogged down in the details of differences between databases.  This time we look at the process using Django and find that it does not vary at all between databases.

Let’s look at a Python script to do the work of creating/inserting the three different pots into a database. To access our Django project most easily, let’s use the shell provided by the manage.py script.

python manage.py shell

Our Python script is:

# Session setup import django
from pots.models import POT

# See what's there already
POT.objects.all()

# Create 3 new pots in the database
p1 = POT(TYPE='Saucepan', SIZE='16cm', AVAILABLE=True, DIRTY=False)
p1.save()
p2 = POT(TYPE='Poacher', SIZE='1 egg', AVAILABLE=True, DIRTY=False)
p2.save()
p3 = POT(TYPE='Frying pan', SIZE='20cm (base)', AVAILABLE=True, DIRTY=False)
p3.save()

# See what's there now
POT.objects.all() 

The final command produces the output:

<QuerySet [<POT: POT object>, <POT: POT object>, <POT: POT object>]>

This is helpful in telling us that there are three pots, but not in giving any distinctive information about any of the pots.

Tweaking the model

So let’s look at a very quick example of the ease with which a Django model can be enhanced.  The function __str__ is called to translate an object into its written representation, so let’s add one of our own to the POT class.  With this extra function, our entire class now reads:

from django.db import models

class POT(models.Model):

     TYPE = models.CharField(max_length= 20)
     SIZE = models.CharField(max_length= 20)
     AVAILABLE = models.BooleanField()
     DIRTY = models.BooleanField()

     def __str__(self):
        return '%s (%s)' % (self.TYPE, self.SIZE) 

If we repeat the last command in the Django shell, the output is now:

<QuerySet [<POT: Saucepan (16cm)>, <POT: Poacher (1 egg)>, <POT: Frying pan (20cm (base))>]>

This output is much more informative, particularly if we are only selecting a few items from a larger set.
Thus a very simple change achieves a useful result.

3-database example

For those who are interested, here we insert the same three pots into the three different databases.  Once again, let’s use the shell provided by the manage.py script.

python manage.py shell

Our Python script is:

# Session setup
import django
from pots.models import POT
from pots_ora.models import POT as oPOT
from pots_pos.models import POT as pPOT

# See what’s there already
POT.objects.all()
oPOT.objects.all()
pPOT.objects.all()

# Create a new pot in each database
p = POT(TYPE='Saucepan', SIZE='16cm', AVAILABLE=True, DIRTY=False)
p.save()
op = oPOT(TYPE='Saucepan', SIZE='16cm', AVAILABLE=True, DIRTY=False)
op.save()
pp = pPOT(TYPE='Saucepan', SIZE='16cm', AVAILABLE=True, DIRTY=False)
pp.save()

# See what’s there now
POT.objects.all()
oPOT.objects.all()
pPOT.objects.all()

# Now add another pot to each
p = POT(TYPE='Poacher', SIZE='1 egg', AVAILABLE=True, DIRTY=False)
p.save()
op = POT(TYPE='Poacher', SIZE='1 egg', AVAILABLE=True, DIRTY=False)
op.save()
pp = POT(TYPE='Poacher', SIZE='1 egg', AVAILABLE=True, DIRTY=False)
pp.save()

# And another pot to each
p = POT(TYPE='Frying pan', SIZE='20cm (base)', AVAILABLE=True, DIRTY=False)
p.save()
op = POT(TYPE='Frying pan', SIZE='20cm (base)', AVAILABLE=True, DIRTY=False)
op.save()
pp = POT(TYPE='Frying pan', SIZE='20cm (base)', AVAILABLE=True, DIRTY=False)
pp.save()

# See what’s there at the end
POT.objects.all()
oPOT.objects.all()
pPOT.objects.all() 

In this case, since we have three separate apps, the __str__ function needs to be added to models.py for each app.