Stavros' Stuff

On programming and other things.

Using FastAPI with Django

FastAPI actually plays very well with Django

You know me, I’m a Django fan. It’s my preferred way of developing web apps, mainly because of the absolutely vast ecosystem of apps and libraries it has, and the fact that it is really well-designed. I love how modular it is, and how it lets you use any of the parts you like and forget about the ones you don’t want. This is going to be emphasized rather spectacularly in this article, as I’m going to do things nobody should ever have to do.

My only issue with Django was that it never really had a good way of making APIs. I hate DRF with somewhat of a passion, I always found its API way too complicated and verbose, and never managed to grok it. Even the simplest things felt cumbersome, and the moment your API objects deviated from looking exactly like your DB models, you were in a world of hurt. I generally prefer writing a simple class-based view for my APIs, but then I don’t get automatic docs and other niceties.

It’s no surprise, then, that when I found FastAPI I was really excited, I really liked its autogenerated docs, dependency injection system, and lack of magical “request” objects or big JSON blobs. It looked very simple, well-architected and with sane defaults, and I seriously considered developing the API for my company’s next product on it, but was apprehensive about two things: It lacked Django’s ecosystem, and it didn’t have an ORM as good and well-integrated as Django’s. I would also miss Django’s admin interface a lot. Three things.

It would have been great if FastAPI was a Django library, but I guess the asynchronicity wouldn’t have been possible. Still, there’s no reason for DRF not to have an API as nice as FastAPI’s, but there’s no helping that. A fantastical notion caught hold of me: What if I could combine FastAPI’s view serving with Django’s ORM and apps? Verily, I say unto thee, it would be rad.

And that’s exactly what I did. Here’s how:

Continue reading…

How to deploy Django on Dokku

It's a dream come true

Ever since I was a wide-eyed little boy, I would look up at the stars and wonder in wonder: “What if I could lease my very own, beefy, dedicated Hetzner server and have an easy way to deploy all my projects onto that?” But lo, my dreams were dashed because Docker wouldn’t be invented for another twenty years, and Hetzner did not accept Mastercard at the time.

Decades later, with Docker finally invented and Hetzner accepting all major credit cards, my dream lay all but forgotten, because Docker could not do zero-downtime deploys natively and I hated it. That was how things remained, until my friend Theodore told me that he tried Dokku and that it worked very well.

I had heard of Dokku (and Fig, Deis, Flynn, Kubernetes, etc etc), but I never paid too much attention, as these PaaSaaSes struck me as too webcale for my simple projects. All I wanted was a way to skip through all the boilerplate configuration of deploying a Django app, and Ansible wasn’t cutting it, as it was still too much plumbing.

Since Theodore tried it and said it was apparently pretty easy to deploy with, though, I figured I’d give it a shot and see. It helped that Dokku was explicitly designed to be light and self-contained, whereas Kubernetes is for much larger deployments, so Dokku fit my use case exactly.

Trying Dokku out

To try Dokku out, I needed a project. Luckily,

Continue reading…

How to deploy Django with Docker

Finally, Django, with Docker, on production!

I finally managed to deploy Django in a Docker container on production! I’ve been trying to switch to a full Docker development/production model since Docker came out, but only recently did the ecosystem mature enough to allow me to easily use Docker both for development (where it excels, in my opinion) and on production (where it’s pretty okay and quite useful).

In this post, I will quickly give you all the relevant details and files you need to go from a newly checked-out repository to a full development environment in one command, and to deploy that service to production. As a bonus, I’ll show you how to use Gitlab (which is awesome) to build your containers and store them in the Gitlab registry.

Let’s begin!

Development

Continue reading…

Show page generation time in Django

Ever longed for the good old PHP days? No? Good.

Earlier today, i.e. a few minutes ago, I was working on my latest guinea pig, TiThess. It’s an events guide for my city, and the latest project I’m trying everything on. I like having a project I can try new things on, as it helps keep my skills sharp.

As I was testing page load times with the excellent Web Page Test, trying to get them down to the absolute minimum, I was getting an F in time-to-first-byte. This is very odd, because the whole site is supposed to be cached, so I was wondering whether the cache is doing something wrong and slowing page generation down.

To make sure, I needed a simple way to show how long page generation took, like the old “page generated in X seconds” footer that was all the rage with PHP sites way back when. Here’s how I did it:

Continue reading…

Standalone Django scripts: The definitive guide

Jeez, why is this so hard to find info on?

You know the deal, you have your fantastic Django application and it’s working great and everything, but you need to make a small change which is too cumbersome to do in the shell, so you figure “duh, I’ll just write a script to do it”. You write your external script in two minutes and then struggle for two hours to figure out how to load the models and the rest of the context so it will work with your app’s settings and all your Django goodness.

You visit StackOverflow and a bunch more sites, and they either tell you to use a management command, which is great advice, except your thing is a one-off and you don’t want to have to check it to git and go through all that hassle to get it deployed just to do this simple thing, or they give you some arcane lines that just don’t work.

Fear not, for I am here. I will give you five simple lines that will make everything work perfectly. Perfectly, I say!

Without further ado (all the previous ado was just so I could fill the paragraph so the side-box doesn’t look weird with short text), I give you the magic commands! Here they are:

Continue reading…

Calling single-argument methods in Django templates

No longer are your methods confined to bare calls!

One of my pet peeves when it comes to Django is that you can’t call methods that require arguments in templates. While this is fine most of the time, it does mean that you need to have one property or method per call you want to make, which sometimes gets very cumbersome.

I needed a way to define various dynamic permissions that are calculated at runtime (for irrelevant reasons, Django’s permissions framework wasn’t a good fit), and writing properties like can_register, can_add_tags, can_subscribe got tedious. These tended to be defined all over the place, rather than in one central spot, and it was hard to add more checks without cluttering the classes.

I would much prefer to have a single method (let’s call it can()) that accepted a string with the permission I wanted to check, and return True or False, depending. This is easy to do in the views, but templates would never be able to call it with an argument.

However, since Django can do dictionary-style attribute lookups, I could add a dictionary interface over the method, and allow

Continue reading…

Django's per-site caching doesn't work

Surprise! Your cache doesn't.

A few days ago, I wrote a post about a peculiar piece of code that a friend of mine had sent me. Since it was interesting bit of code, I thought Hacker News would enjoy it, so I posted it there. To my great pleasure, the post shot up to the first place in a few minutes and continued there for a full day, bringing just over 50,000 visitors to this blog, in total.

I was very happy that people were liking and discussing this post (and the discussion was very interesting in its own right), but I noticed that AppEngine, where this blog is hosted, was struggling to serve it. I had to create new instances because the average latency was about ten seconds(!), even though this blog is pretty much only text and static media, and I use Django’s per-site cache to cache every single page.

Continue reading…