Getting Started with Python and Django - Part 2

Continued from Part 1

Django Models

Database tables are represented in Python code using Models, which use Django's built-in ORM (Object-Relational Mapping). A model class is a database table and a model instance is a database table row. There's extensive documentation on this subject matter, but for now lets basically keep in mind that:

  • We need to access a data store/RDBMS (via SQLite, MySQL, PostgreSQL, etc.)
  • We need to be able to Create/Read/Update/Delete (CRUD) data from the RDBMS

Setting Up a Database

When we first ran runserver in Part 1, Django created a free SQLite database for development so we will use this for this example. It is located in the project's root folder:

whateverproject/  
    db.sqlite3

In whateverproject/settings.py, you can see the default database is already enabled:

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

Creating a Model

In whateverapp/models.py we do the following to create two models City and Hotel. We will have more than one Hotel in each City, so we set City as the ForeignKey (a standard one-to-many relationship):

from django.db import models


class City(models.Model):  
    name = models.CharField(max_length=64)


class Hotel(models.Model):  
    name = models.CharField(max_length=64)
    room_charge = models.DecimalField(max_digits=6, decimal_places=2)
    rooms_available = models.BooleanField(default=False)
    rating = models.CharField(choices=(
        ('1', "1 Star"),
        ('2', "2 Stars"),
        ('3', "3 Stars"),
        ('4', "4 Stars"),
        ('5', "5 Stars")),
        max_length=1
    )
    city = models.ForeignKey("City")
  • A table created in the database will be the app name + _ + model name... so, in this case: whateverapp_person
  • The attributes become the columns of the table
  • Django will auto-create primary keys if we don't declare them ourselves

Database Migrations

We run the following command to create an initial migration

$ python manage.py makemigrations

Results:

Migrations for 'whateverapp':  
  0001_initial.py:
    - Create model City
    - Create model Hotel

We then run the following command to scan through the Model objects in the project and create the database(s) for us:

$ python manage.py migrate

You should see something like this:

Operations to perform:  
  Synchronize unmigrated apps: staticfiles, messages
  Apply all migrations: admin, whateverapp, contenttypes, auth, sessions
Synchronizing apps without migrations:  
  Creating tables...
    Running deferred SQL...
  Installing custom SQL...
Running migrations:  
  Rendering model states... DONE
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... 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 sessions.0001_initial... OK
  Applying whateverapp.0001_initial... OK

Populating the Database using Fixtures

Now that we have the schema set up, we're going to populate the data model with some initial values using fixtures. Django takes different formats, including XML, YAML and JSON.

Create the file initial_data.json in the whateverapp folder with the following JSON data in it:

[
    {
        "model": "whateverapp.city",
        "pk": 1,
        "fields": {
            "name" : "New York City"
        }
    },
    {
        "model": "whateverapp.city",
        "pk": 2,
        "fields": {
            "name" : "Los Angeles"
        }
    },
    {
    "model": "whateverapp.hotel",
        "pk": 1,
        "fields": {
            "name" : "Atlantic Hotel",
            "room_charge" : "199.95",
            "rooms_available" : "True",
            "rating" : "4",
            "city_id" : "1"
        }
    },
    {
    "model": "whateverapp.hotel",
        "pk": 2,
        "fields": {
            "name" : "Downtown Hotel",
            "room_charge" : "245.99",
            "rooms_available" : "False",
            "rating" : "5",
            "city_id" : "1"
        }
    },
    {
    "model": "whateverapp.hotel",
        "pk": 3,
        "fields": {
            "name" : "Pacific Hotel",
            "room_charge" : "101.00",
            "rooms_available" : "True",
            "rating" : "3",
            "city_id" : "2"
        }
    }
]

Import the data using the following command:

$ python manage.py loaddata whateverapp/initial_data.json

Django Views

We need to alter the whateverapp/views.py file in order to retrieve data from the database, and then have it available for the template to render the data (in this case, an HTML formatted page). You can see the previous index view function we created in Part 1. In this example, we are creating a hotels function to handle the request for cities and hotels.

from django.http import HttpResponse  
from django.template import RequestContext, loader  
from .models import City  
from .models import Hotel


def index(request):  
    return HttpResponse("Hello world!")


def hotels(request):  
    cities_list = City.objects.order_by('name')
    hotels_list = Hotel.objects.order_by('name')
    template = loader.get_template('whateverapp/hotels.html')
    context = RequestContext(request, {
        'cities_list': cities_list,
        'hotels_list': hotels_list
    })
    return HttpResponse(template.render(context))

Templates

Templates are basically combinations of HTML and logic to output pages with data embedded in them.

Let's create a directory to store a template for our whateverapp via:

$ mkdir -p whateverapp/templates/whateverapp 
$ touch whateverapp/templates/whateverapp/hotels.html

In hotels.html, we're gonna retrieve the hotels in each city and list them:

<html>  
  <head>
    <title>Hotel Listing</title>
  </head>
  <body>
    <h1>All Hotels</h1>

    {% if cities_list %}
        <ul>
            {% for city in cities_list %}
                <li>{{ city.name }}
                    <ul><li>{{  city.hotel_set.all.count }} Hotels:</li>
                        <ul>
                         {% for hotel in city.hotel_set.all %}
                             <li>{{ hotel.name }}</li>
                             <ul>
                             <li>Rating: {{ hotel.rating }}</li>
                             <li>Room Charge: ${{ hotel.room_charge }}</li>
                             <li>Rooms Available? {{ hotel.rooms_available }}</li>
                             </ul>
                          {%  endfor %}
                        </ul>
                    </ul>
                </li>
            {% endfor %}
        </ul>
    {% else %}
        <p>No cities are available.</p>
    {% endif %}

  </body>
</html>  

Now if we re-launch the page in our browser and go to the /hotels page, we should see a nested list of Cities with their Hotels listed in HTML.

To launch the web server again, run the command:

$ python manage.py runserver

Point the URL in the web browser to http://127.0.0.1:8000/hotels and you should see this page:

Now we have Django accessing and rendering data from a database in HTML. In the following section, we'll take a look at how to test the Django app.

Next: Getting Started with Python and Django - Part 3

Source Code on GitHub: https://github.com/justinbellamy/whateverprojectPython