Virtual Show

Last updated: Aug. 4, 2020, 10:54 p.m.

My Wife has been helping to organise the arts, crafts & cookery section of the local agricultural show for several years. The show was cancelled for the first time in 19 years due to the COVID-19 lockdown this year and to keep it going in some way she suggested and helped organise a virtual show. You can see the entries online still at To manage the content we first looked at off-the-shelf Wordpress gallery solutions but they were not a good fit. Hosting it on Instagram or Facebook had been suggested but would have excluded a lot of the regular entrants so an email-in solution was picked to be most inclusive. In the end the show had over 250 entries.

I volunteered to write a custom site, and since Django is my preferred platform these days I wrote it in that. It was my first significant Django project in a number of years and the first time working in Python3 but as it promises the framework delivered a robust interface with minimal effort.

The solution is available on my GitHub account. Since the entry mechanism was email there was no user-facing login or upload facility, entries were all managed through the Django admin. That saved time in developing the form validation and security as well as not needing to warn users about cookies (since the site doesn't use any)!

The solution takes the form of a list home page which mimics the show schedule (list of classes). Clicking on any of the entry descriptions takes you through to a gallery view. I used Bootstrap 4.5 for the responsive layout and theming the galleries and integrated Photoswipe javascript gallery with touch gestures to make the site comfortable on both laptops and phones.

In the back-end mostly the solution involved managing photos. The code used is mostly taken from this site and scales the uploaded images down to a maximum of 1600 x 1600 pixels and also creates a thumbnail image for the gallery. In the production site these files were not renamed which turned out to be a problem as entrants often used identifiable file names which was an issue (for privacy as well as judging integrity). My wife manually renamed the files to the entry number before uploading each one. In the version published I've modified the code to automatically rename the images to classNN_entryMMM.jpg for example. A change that was so simple I wish I'd done it before the site went live!

Another issue I've fixed since the show closed was rotation of images. It's a bit of a surprise when the images first appeared on the site but some were the wrong way up. We're so used to cameras, phones and tablets including inertial sensors and viewers automatically using this metadata to correct the image rotation that when a library doesn't do it it takes a moment to realise why. In the real show I used Image Magik's mogrify command to rotate ones that looked wrong on the server's CLI. I've now included a snippet of code to use the EXIF metadata to rotate images on upload.

    for orientation in ExifTags.TAGS.keys():
        if ExifTags.TAGS[orientation]=='Orientation':


        if exif[orientation] == 3:
            image = image.rotate(180, expand=True)
        elif exif[orientation] == 6:
            image = image.rotate(270, expand=True)
        elif exif[orientation] == 8:
            image = image.rotate(90, expand=True)
    except AttributeError:
        # no Exif tags on this image

The try ... except structure was essential to catch the case where an image didn't have EXIF data. I triggered this case easily by deleting the thumbnail which causes it to be regenerated when the EntryImage object is saved, however it's now generated from the resized main image which itself has no EXIF tags. Stripping the EXIF tags is always a good idea when uploading content because you don't want to inadvertently expose information such as time, date and location.

The rest of the features worked without issue, there's a space for judges comments and a way to place entries (first, second, third etc.) which all worked fine. The powerful ordering metadata functions in Django classes made keeping the first place entries at the top of the page trivial.

There's no pagination implemented since the largest class only had 48 entries this was never an issue for my deployment.



Posting comments is not currently possible. If you want to discuss this article you can reach me on twitter or via email.



Twitter: @hairymnstr