Library / SDK
feincms/feincms avatar
feincms/feincms

FeinCMS, a Django CMS that admits it knows nothing about content

A Django-based CMS with a focus on extensibility and concise code

1,131 stars239 forksPythonBSD-3-Clause

At a glance

What is it?
FeinCMS is a Django-based content management system whose central claim is that it knows nothing about content, and gives you regions, blocks and a handful of helper functions instead. Adding a content type is a model class and a call to Page.create_content_type, and the branch policy is unusually explicit for a project of this size.
Who is it for?
FeinCMS fits a Django team that wants the admin interface, the media handling and the block ordering without a CMS that dictates how content is structured, and it fits sites with unusual editorial requirements such as several independently editable regions.
Can I use it commercially?
Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

An extremely stupid content management system

The readme describes FeinCMS as an extremely stupid content management system, and then explains what that means: it knows nothing about content, just enough to create an admin interface for your own page content types.

What it actually provides is a small set of primitives. Content blocks can be reordered with a drag and drop interface. As many content blocks as you like can be added to a region, where a region is a named area of a page such as the sidebar, the main content region, or something else the author has not thought of yet. And there are helper functions that return ordered lists of page content blocks for those regions.

That is the whole system. A page is a set of regions, a region is an ordered list of blocks, and a block is an object of a type you defined. Everything else is Django.

The argument for the design is made at the top of the readme with a question. When was the last time that a pre-built software package you wanted to use got many things right, but in the end you still needed to modify the core parts of the code just because it was not easily possible to customize the way a certain part of the system behaved? The answer given is that Django rescued its users from having to do everything on their own, or from customizing another package until updating it was impossible. The readme then describes the CMS it wants to avoid: one with very strong views about how content should be structured, a rich text area, a media library and some templates.

The guiding principle is stated in one line: the biggest strength of a framework-like design is that it tries not to have too strong a view of what the user should do, so it should make some things easy but get out of the way most of the time.

A content type is ten lines of your own model

The example in the readme is the clearest statement of the extensibility model. The premise is that you would rather die than use a rich text editor, so here is a markdown content type in your own project:

python
from markdown2 import markdown
from feincms.module.page.models import Page
from django.db import models

class MarkdownPageContent(models.Model):
    content = models.TextField()

    class Meta:
        abstract = True

    def render(self, **kwargs):
        return markdown(self.content)

Page.create_content_type(MarkdownPageContent)

Three things are happening. The model is abstract, so it is a mixin rather than a table of its own, which is how a content type becomes a block that can be placed in any region. It defines a render method that returns the rendered output, and the method takes keyword arguments so that the template context can be passed in. And the final line registers the class with the page model through create_content_type.

The comment in the readme is that this is not even ten code lines for your own page content type, and the point is less about the line count than about the direction of the dependency. You import FeinCMS's page model into your project and call a method on it, rather than subclassing FeinCMS's content classes and overriding behaviour. The readme puts it plainly: you can go on using the CMS without being forced to use whatever the developers deemed best.

That is the same argument as the region model, only at a finer grain. Both exist so that the opinionated parts are optional.

The questions the readme keeps asking

The readme lists a series of what ifs, and they are worth reading as a specification of the design rather than as a sales pitch.

What if we want to be able to add custom content? That is the content type mechanism above. What if the user cannot be trusted to resize images before uploading them? That is the media library handling images on the way in, so the editor never has to. What if you would like to add a gallery somewhere in between other content? That is the region and block model, because a gallery is just another block type, and a block can sit between two others. And what if the user should be able to administer not only the main content, but also a sidebar and the footer? That is regions, which are arbitrary in number and named by the developer.

Each of those questions is a case where a conventional CMS has a fixed answer and FeinCMS has a default that you can replace. That is also the source of its weakness, and the readme is honest enough to imply it: a system that knows nothing about content makes no promises about what your content should look like, which means some of that work lands on you.

The Django comparison is the model here too. The readme says that Django came to rescue people who were not happy with either doing everything on their own or customizing another package until it was impossible to update, and FeinCMS is the CMS built on that lesson. The test of a good framework in this framing is not what it gives you, but how little of it you have to fight when your requirements go beyond the expected.

The branch policy, which is the upgrade contract

Most projects do not document their branch semantics, and this one does, in a short list that is easy to miss and useful to know.

There is a maint branch, which is the maintenance branch for the second-newest version of FeinCMS, and a main branch, which is the stable version. Both are never rebased or rewound, so a commit that exists in main today will still be reachable later, and the history of a released line is not rewritten underneath you.

Then there is next, the upcoming version. It is rarely rebased, if ever, but it might happen, and a note is sent to the official mailing list whenever it is rebased. That is a small commitment with a real consequence: a developer working on next may have to rebase, and will hear about it by mail.

Finally there are pu or feature branches for short-lived projects. Those are not guaranteed to stay around and are explicitly not meant to be deployed into production environments.

The policy in practice is a small piece of release engineering that protects the people running sites. A stable line that is never rebased means an upgrade is a merge rather than a replay, and a feature branch that is not for production means nobody ends up depending on a branch that disappears. The mailing list is the official channel, which is another reason the Google Group page, the django-feincms group, is listed among the places to visit.

feincms3 is where the new work is

The first line of the readme is a note, and it is the most important sentence in the file for anyone deciding what to use.

If you are starting a new project, you may want to take a look at feincms3, at feincms3.readthedocs.io. FeinCMS is still maintained and works well, but feincms3 is where current development is happening.

That is a two-speed situation. The mature line continues to receive maintenance, and the readme says plainly that it works well, which is what you want to hear about a project you are putting a production site on. The new development happens elsewhere, in a project with a different name and its own documentation site.

For a reader, this reframes the rest of the repository. FeinCMS is the version you adopt when you want a stable, well-worn system, and feincms3 is the version you adopt when you want the current design. The two are not interchangeable, and the readme does not attempt a migration guide, so a project that starts on one and wants the other is starting over.

The rest of the pointer list follows the same pattern. There is the FeinCMS website at feincms.org, the documentation on readthedocs under the feincms-django-cms name, the Google Groups page for the django-feincms group, and the repository itself. Four ways in, with the documentation being the real reference and the readme being the philosophy.

The tooling: ruff, tox, pre-commit and translation files

The repository layout is that of a long-lived Python project that has kept its house in order. There is a feincms/ package directory, a docs/ directory, a tests/ directory, a short setup.py that defers to setup.cfg, a pyproject.toml that carries the tool configuration rather than the package metadata, a tox.ini for test environments, a CHANGELOG.rst, a CONTRIBUTING.rst, an AUTHORS file and a.mailmap.

The pyproject.toml is almost entirely a ruff configuration, and it is a long one. The target version is py310. The lint rules are the usual collection of flake8 plugins brought in as a set, from bugbear and comprehensions through flake8-django, boolean trap, logging format, gettext, pyupgrade and the 2020 rules. The configuration is opinionated in the small ways that show care: isort combines as-imports and puts two lines after imports, and there is a per-file ignore for migration files that allows PascalCase model names and invalid module names, which are exactly the two things migrations legitimately break.

There is a per-file ignore for the tests directory too, allowing the camelCase assertion naming that the unittest style uses. The mccabe complexity limit is set to 15, which is a real limit rather than a decorative one.

Around Python there is a JavaScript side that is minimal but present, with a biome.json, an.eslintrc.js and a.prettierignore, and a.pre-commit-config.yaml that runs the checks before a commit lands. The.tx/ directory is the Transifex configuration, which is how the translations are managed, and mise.toml is a task runner configuration that pins the tools a contributor is expected to use. The last push was on 2026-10-13, and the repository has no GitHub releases.

Where FeinCMS is the wrong choice

The first limit is that a FeinCMS site is something you build rather than something you install. The CMS supplies regions, blocks, an admin interface and the media handling, and everything that makes the site what it is, the content model, the templates, the views, is yours to write. A team expecting a ready-made product with configurable page types will find the readme's central claim to be an obstacle rather than a feature.

The second is the framework's age relative to Django's own direction. The lint configuration targets Python 3.10, which is not the newest Python, and the CMS is a Django application, so it moves at the pace of the Django version it supports. A team on the latest Django with strict typing across the project may find the code style of an older generation.

The third is documentation drift. The documentation is on readthedocs, the discussion is on a Google Group, and the project site is a separate site again. A Google Group is an ageing communication channel, and a reader who prefers to ask in a repository issue tracker will find that the readme points them elsewhere for support.

The fourth is the fork in the road. FeinCMS and feincms3 are two projects with two documentation sites and no stated migration path, so choosing one is a decision about which line of development you want to be on. The readme's advice is to start new work on feincms3, and it is worth taking that advice seriously rather than treating the mature line as the default.

Editorial conclusion

FeinCMS fits a Django team that wants the admin interface, the media handling and the block ordering without a CMS that dictates how content is structured, and it fits sites with unusual editorial requirements such as several independently editable regions. It does not fit a project that wants a product to install and configure, because a FeinCMS site is something you build, and it does not fit a team starting fresh, because the readme points new projects at feincms3, where the current development is happening. Before choosing it, read the branch policy, since main and maint are never rebased while next occasionally is, and that difference determines what an upgrade costs you. The licence is BSD-3-Clause, the last push was on 2026-10-13, and the repository publishes no GitHub releases.

Frequently asked questions

What is FeinCMS and what does it give me?

It is a Django-based CMS with a focus on extensibility and concise code. It provides regions, ordered content blocks with drag and drop reordering, helper functions returning ordered lists of blocks, and an admin interface for content types you define yourself, while knowing nothing about the structure of content.

How do I add a custom content type to FeinCMS?

Define a model with an abstract Meta class and a render method that returns the output, then register it with Page.create_content_type. The readme's markdown example does exactly that in fewer than ten lines, using markdown2 for the rendering.

Which branch of FeinCMS should I use?

main is the stable version and maint is for the second-newest version, and both are never rebased or rewound. next is the upcoming version, is rarely rebased but might be, with a note sent to the mailing list when it is. pu and feature branches are short-lived and are not meant for production.

Is FeinCMS still being developed, and should a new project use it?

The readme says FeinCMS is still maintained and works well, but that feincms3 is where current development is happening, and it suggests looking at feincms3 if you are starting a new project. The two have separate documentation sites and no stated migration path.

Under which licence is FeinCMS released?

BSD-3-Clause, with the licence text in the LICENSE file at the repository root. The repository publishes no GitHub releases, and the branches, main, maint and next, are the versioning mechanism.

Official sources

  1. feincms/feincms on GitHub
  2. Issues
  3. License: BSD-3-Clause
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/feincms-feincms.svg)](https://hysenlabs.com/projects/feincms-feincms)