Photos app
Django encourages to encapsulate the related things into so called apps. It provides several built-in, like an admin app we’ve already seen. And, of course you create your own. We will create one app called ‘photos’.
Create a new directory inside the backend/pixies directory and add an empt file __init__.py
which, if Python is not your forte, turns the directory into a module
mkdir pixies/photos
touch pixies/photos/__init__.py
The next step is to declare that this is Django app.
Create the file pixies/photos/apps.py
with the following content:
from django.apps import AppConfig
class PhotosConfig(AppConfig):
name = "pixies.photos"
And, finally, add a new entry into the INSTALLED_APPS:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# new
"pixies.photos.apps.PhotosConfig",
]
Ok, we have our photos app, let’s add a database model to it, so we can store entities related to this app in the database.
But first, since we are going to use DateTime information, let’s be very explicit that all our DateTime are in UTC. Add this to the settings.py
TIME_ZONE = "UTC"
USE_TZ = True
Now, create the model
touch pixies/photos/models.py
And in this file:
import uuid
from django.db import models
class Photo(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
created_at = models.DateTimeField(auto_now_add=True, editable=False)
updated_at = models.DateTimeField(auto_now=True, editable=False)
url = models.CharField(max_length=280)
caption = models.CharField(max_length=280)
details = models.CharField(max_length=280)
def __str__(self):
return self.caption
As you see, the photo will have an id
, which is going to be of the type uuid which will guarantee uniqueness. There are also two DateTime fields created_at
and updated_at
which are going to store thet timestamp when the particular record was created and updated, respectively. Notice that we won’t need to update these fields manually, that is going to be taken care by Django. Finally, 3 fields which are needed for our application, url of the photo, its caption and a sub-caption (called details). Also we override the __str__
function, so the default display will be human-readable.
Now we need to have the corresponding table in our Postgres database. Django help us with that by utilizing migrations which are little scripts which update the database schema. We don’t need to write migrations ourselves, Django analyzes the models in your app and creates migration by running:
python manage.py makemigrations photos
This will create a directory pixies/photos/migrations
which will store the migrations for this particular Django app. for now it will contain only one file 0001_initial.py
which has the initial migration. As you work on your Django app, yo may need to create new models, modify existing ones and so on. makemigrations
will detect those changes and create new migration scripts in this directory. When you apply migrations to your database, Django will replay them one by one. As you see, it is quite flexible way to incrementally manage your database, even in production. If you want to see what SQL is being generated by a particular migration you can run:
python manage.py sqlmigrate photos 0001_initial
Once you satisfied your curiosity, let’s apply the migration to our database
python manage.py migrate photos
Ok, our database is updated. It would be nice if the admin interface picked our Photo model
Managing our models with the Django admin
let us add the file pixies/photos/admin.py
with the following:
from django.contrib import admin
from pixies.photos.models import Photo
admin.site.register(Photo, admin.ModelAdmin)
That is all we need to do to get a default admin interface for the Photo model.
Run the app again and login to the admin UI at http://127.0.0.1:8000/admin/photos
Yo will see that the new section photos
has appeared. Feel free to add a couple of photos. Here are mine:
Caption | Details | Url |
---|---|---|
Pygmy Short-horned Lizard | Phrynosoma douglasii | https://photos.smugmug.com/Animals/Reptiles/Lizards/Pygmy-Horned-Lizard-2013/i-cdRKpxK/0/fe991315/X5/131005_ManastashRidge_693-X5.jpg |
American Crocodile | Crocodylus acutus | https://photos.smugmug.com/Animals/Reptiles/Crocodilians/American-Crocodile/i-vkN5pCN/0/b92acda4/5K/151029_Everglades_729-5K.jpg |
Bobcat | Lynx rufus | https://photos.smugmug.com/Animals/Mammals/Cats/Wild-Cats/Bobcat/i-kvxCNsm/0/8c16f74a/X5/190502_Backyard_258-X5.jpg |
Yellow Warbler | Setophaga petechia | https://photos.smugmug.com/Animals/Birds/Warblers/Yellow-Warbler-2013/i-Tr5rQBN/0/f058a66f/X5/191130_Camino%20de%20grava%20entre%20el%20Corchito%20y%20Estero%20de%20Chicxulub_079-X5.jpg |
Customizing the admin interface
By default the admin UI will only display the caption, thanks to our __str__
implementation on the model class. Django allows you customize you pretty much every aspect of the admin UI. To do that let’s derive from the default ModelAdmin:
from django.contrib import admin
from pixies.photos.models import Photo
class PhotosAdmin(admin.ModelAdmin):
model = Photo
list_display = (
"caption",
"details",
"created_at",
"updated_at",
"url",
)
admin.site.register(Photo, PhotosAdmin)
This will display more fields, including the created_at
and updated_at
fields.
One thing still bothers: the created_at
and updated_at
fields are presented in UTC format which is undoubtedly correct and formal, but at the same time it is hard to read. One needs to apply some mental gymnastics to understand how long ago this has happened.
We can do better. Let’s install the popular package arrow
which contains routines to display the data time in more human-friendly form.
pip install arrow
Now, go ahead to the admin.py
and add two methods to the PhotosAdmin
class:
#new
import arrow
class PhotosAdmin(admin.ModelAdmin):
model = Photo
#new
def created(self, obj):
return arrow.get(obj.created_at).humanize()
#new
def updated(self, obj):
return arrow.get(obj.updated_at).humanize()
# as before...
Finally, replace the original created_at
and updated_at
fields we these:
list_display = (
"caption",
"details",
#modified
"created",
"updated",
"url",
)
Launch the app, go to the admin UI and check the list of photos, specifically the updated
and created
columns.
Much better!
In the next module we are going to expose our photos' data as a REST API.