Changelog: Unified Posts (aka Fat Posts)

Last night I deployed a huge change I’ve been working on here and there for weeks, maybe months now. I implemented fat posts.

I’ve already fixed a few bugs with it and will continue to let bugs shake out, but I’m happy to have it live.

Last night I deployed a huge change I’ve been working on here and there for weeks, maybe months now. I implemented fat posts.

Change Overview

I pretty much did what I had proof-of-concepted about it, but I wanted to do it as incrementally as possible.

The original architecture had a base FeedItem Django model and each post type inherited from that base model and added any additional fields and functionality it needed. Django handles this by creating a table for the base model and additional tables for the child models with a ptr_id field connecting each record to the FeedItem record. For the home composite stream of multiple post types, I can query the FeedItem. The additional child properties are available as a property on the base object. For post type feeds, I can query the child model directly.

Now, everything is a FeedItem. Post type feeds filter on the new post_type field.

Approach to Implementation

Properties and Methods First

I don’t like doing big changes all at once. I started moving the methods and properties of each child model to the parent FeedItem. Deploying as I went. That was generally straightforward. Add new fields to FeedItem, write a migration with Python operations to copy the values from the child to the parent, remove the field from the child. The only issue was that using the same field name meant I had to add it as a different name first to FeedItem and then rename it later to avoid conflicts.

Some child properties belonged to multiple post types, so they needed to be migrated at the same time. Because of this, I generally migrated a property across models, instead of all of a model’s properties.

I would deploy these as I went, since there shouldn’t be any noticeable change.

With the properties moved, I moved methods to the base model.

The Unavoidable Big Commit

The properties and methods were easy. The big shift was adding the post_type field to FeedItem and using that instead of the child model for handling different post types.

This had to happen all at once. The child model needed a migration to update all of it’s records with the correct post_type, a proxy model needed to be added to assist in post creation in the admin, and templates needed to be updated.

And I couldn’t do it once post type at a time. Or I didn’t see a good way to do that.

A side effect of this work is I saw just how duplicative a lot of my templating had become, but it was hidden by the child models being separate.

Two Notable Side Effects

As a result of this change, all post detail urls are now post/{id} instead of {post_type}/{id}. With post_type as a field, I can much more easily change a post to a different type so this additional unification makes sense to me. The old urls 301 redirect to the new urls.

Also, images, as a thing in the database, are now distinct from photo posts. Instead of a FeedItem having an image field, FeedItems have a many Images and Images have many FeedItems through a PostImage field. This will allow me to add images to Notes, connect images to Articles, create multi-photo posts, and even implement a photo stream that can show all photos regardless of what post type they’re used in.

Deployed and What Next

I deployed and everything generally worked! My biggest fear was missing some kind of data loss and needing to restore from a backup. I queried for any FeedItems with null or blank post_type fields, and there were none.

I wrote a test note and that worked. But I got an error trying to syndicate that to mastodon. And then I noticed that reply to url wasn’t showing in the feed.

I fixed the reply to url in the template, and then fixed the mastodon syndication. Today, I noticed tag and date archives were returning errors, so I fixed that.

I’m going to leave this as is for a while to see what other problems shake out.

Once I’m happy, I need to remove the old models, and I would love to rename FeedItem to Post.

I need to take a closer look at the new, generic FeedItem admin to make it usable. As it is now, there are some fields that are required that don’t need to be, and it can probably be organized a little bit better. That’s the interface I need to convert posts from one type to another, which I don’t expect to be common.

The unified code has really highlighted how squirrelly it is, so some code clean up would be nice, too.

Happy to have this live and a giant deployment no longer weighing on my mind.