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
TIME_ZONE = "UTC" USE_TZ = True
Now, create the model
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
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:
|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|
|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
One thing still bothers: the
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
#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
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
In the next module we are going to expose our photos' data as a REST API.