Make Your Django Application Editable
The CMS doesn't need to own your data to make it editable.
In "The CMS Is Not the Application", I argued for a simple separation of responsibilities: The application should own the architecture. The CMS should make it editable.
That sounds straightforward. But there is a practical question hiding behind it.
What happens when something that clearly belongs to your application also needs to become editable content?
Consider the conference application from the previous article. It might already have perfectly ordinary Django models for:
- speakers,
- conferences,
- venues,
- sessions,
- sponsors.
These are domain objects. They have relationships, business rules, permissions and perhaps APIs. They belong to the application.
Then the content team arrives.
They want speaker biographies in several languages. They want to prepare changes as drafts and publish them later. They want previews. They want to feature selected speakers on editorial pages. And ideally they would like to edit a speaker while looking at the website rather than hunting through an administration interface.
Suddenly we have a CMS problem.
But we don't necessarily need a new CMS model.
Content management is a capability
I think we tend to conflate three things when talking about CMS architecture.
There is domain data:
- a speaker's name,
- their company,
- the sessions they speak at,
- their relationship to a conference.
There is editorial content:
- their biography,
- an introduction,
- a quote,
- perhaps an SEO description.
And then there are content-management capabilities:
- localization,
- versioning,
- draft and publish,
- preview,
- permissions,
- frontend editing,
- composition.
Those are different concerns.
A speaker does not become a different kind of object just because their biography needs a publishing workflow. And yet this is where CMS architectures often start becoming awkward.
We already have a perfectly good representation of the real-world thing in our application. Then we create another representation inside the CMS because that is where editable content is supposed to live.
Now we have two models of the same thing. And sooner or later, we have to keep them in sync.
Don't model the same thing twice
This is one of the reasons I care about the distinction between the CMS and the application. If the application owns the domain, then the domain model should remain the source of truth. The CMS shouldn't require us to recreate the application's architecture in its own image before editors can work with it.
Instead, I want to ask a different question: What content-management capabilities does this application object need?
Perhaps a Speaker needs multilingual editorial content and versioning.
A Venue might only need a rich description.
A Conference may need a publishing workflow and placeholders for editorial composition.
A Sponsor may not need CMS functionality at all.
These should be choices we can make deliberately.
The architecture was possible. The ergonomics weren't good enough.
django CMS has been moving in this direction for several years.
django CMS 4 introduced a much more general concept of content than pages alone. Applications can provide their own content models and integrate them with CMS features such as versioning, language handling and frontend editing.
Architecturally, that was an important step.
Practically, however, building custom content still requires quite a lot of knowledge and plumbing.
You need to understand groupers and content models. You need to connect versioning. You need the appropriate admin integration. You need frontend editing. You may need URLs and apphooks. Relationships between versioned objects introduce another set of questions.
None of these problems is individually unreasonable.
Together, however, they create enough friction that the easiest solution can once again become: just put it in a page or build a special-purpose CMS application.
That's the wrong incentive.
If we really believe that the application should own the architecture, adding editorial capabilities to an application object needs to become much easier.
What if adding CMS capabilities were boring?
This is the motivation behind djangocms-custom-content (see its repo). It is an experiment in making custom django CMS content much less special.
Start with an application concept.
For example:
class Article(models.Model):
slug = models.SlugField()
Then give it the editorial content it needs:
class ArticleContent(models.Model):
article = models.ForeignKey(Article, on_delete=models.CASCADE)
language = models.CharField(max_length=15)
title = models.CharField(max_length=200)
body = PlaceholderRelationField()
From there, djangocms-custom-content provides the infrastructure for integrating that content with django CMS: multilingual content, versioning, frontend editing, administration and application URLs.
The important part isn't how many lines of code it saves. It is what did not happen.
We did not turn the application into a CMS application. We did not move the domain into a separate content repository.
We added content-management capabilities to an application concept.
The application still owns the architecture.
Relationships matter
This becomes particularly important once applications become more interesting than a single Article model.
Imagine an article with:
- authors that are
Personobjects, - a
Category, - related
Serviceobjects, - perhaps a
Conferenceit refers to.
Some of those objects may themselves have multilingual, versioned editorial content. Others may be plain Django models. That's normal. It is what an application looks like.
The CMS should not require all of these objects to become the same kind of "content" before they can relate to one another.
This is another area djangocms-custom-content explores: relationships that continue to work across versioned content while still allowing application models and CMS-enabled content to coexist.
The goal isn't to build a universal content graph.
The goal is much less ambitious — and, I think, much more useful:
Let Django model the application. Add CMS capabilities where they are actually needed.
From custom content to reusable building blocks
There is another consequence of making custom content less expensive to build: agencies can stop solving the same content problems from scratch for every customer.
Most agencies don't actually build completely unique systems every time.
Across projects, the same concepts appear again and again:
- people and team members,
- locations and offices,
- events,
- jobs,
- products,
- services,
- case studies,
- testimonials,
- downloads,
- FAQs.
These are more than simple CMS plugins, but they don't necessarily justify becoming standalone products either. They sit somewhere in between: reusable application concepts with a well-defined data model and useful editorial capabilities.
This is where djangocms-custom-content could become particularly interesting.
An agency could build its own library of sophisticated content applications — something like a project-specific or agency-specific contrib collection.
A People application might provide people, roles and organizations, together with multilingual biographies, portraits and frontend editing.
An Events application might provide events, venues, dates and registrations, while making descriptions and other editorial content versioned and multilingual.
A Locations application might combine structured addresses, coordinates and opening hours with rich editorial content managed through django CMS.
A Services application could model the actual service catalogue while allowing editors to compose rich presentations of each service using placeholders.
These wouldn't be generic content types pretending that every problem can be represented by a configurable schema.
They would be real Django applications.
They can have migrations, constraints, managers, permissions, APIs, integrations and whatever business logic their domain requires. And because their editorial parts integrate with django CMS, they can still provide the editing experience customers expect from a CMS.
Combine what the customer needs
That changes how an agency can approach a new project.
Instead of starting with an empty CMS and recreating familiar structures, the agency can assemble a project from proven building blocks:
customer_site/
people/
services/
locations/
events/
case_studies/
One customer might need People, Services and Locations. Another might need Events, People and Downloads. A third might need all of them, plus a genuinely custom application for the part of their business that is actually unique.
The important point is that these applications remain composable.
They don't need to know about one particular website architecture, and they don't all need to implement the same CMS capabilities. An Events application may require versioning. Locations might use multilingual content without a complex publishing workflow. A small reference-data application may not need CMS integration at all.
The agency chooses the pieces appropriate to the project and then adds the customer-specific layer on top.
In that sense, this could become a more sophisticated version of the traditional contrib idea: not a collection of generic plugins, but a toolbox of reusable Django applications that happen to be excellent CMS citizens.
And because they are Django applications first, they can evolve independently of the individual customer projects that use them.
Improvements to frontend editing can benefit every project using the component. A better Events application can be rolled into several customer sites. Integrations and APIs can be reused. Lessons learned on one project become improvements to the building block rather than disappearing into another project's codebase.
That is potentially a much more valuable kind of reuse than copying templates and CMS configuration from the last website. It also preserves the distinction that started this discussion.
The agency isn't building a larger CMS that tries to anticipate every future customer requirement.
It is building a collection of good Django applications and giving each of them exactly the content-management capabilities it needs.
Build the domain once. Make it editable. Then combine the building blocks differently for each customer.
A CMS for Django applications
There is a larger idea here.
What if adding content management to a Django application became as ordinary as adding authentication, an API or background jobs?
You would start with your domain:
class Speaker(models.Model):
...
And then ask:
-
Does this need to be multilingual?
-
Does it need drafts and publishing?
-
Should editors be able to preview it?
-
Should it be editable from the frontend?
-
Does it need editorial composition?
-
Does it participate in a workflow?
Those are much better questions than: How do I get my Speaker model into the CMS?
Because the Speaker doesn't need to go into the CMS. It already has a home.
We need CMSs that are comfortable not owning things
This is also why I don't think the future of CMS architecture is primarily about putting more things into increasingly sophisticated content platforms.
Sometimes we need the opposite. We need CMSs that are comfortable not owning things.
A Django application should be able to own its domain, its business rules, its data relationships and its APIs. The CMS should bring the things it is good at: editorial interfaces, localization, versioning, publishing, preview and composition. And the boundary between those two worlds should be cheap enough that developers don't have to choose between good application architecture and good editorial experience.
djangocms-custom-content is my attempt to explore what that could look like in django CMS. It is still an experiment, and I expect the interesting part to be discovering where the abstraction breaks.
If you have an application with domain models that you've struggled to make properly editable, I would be particularly interested in hearing about it.
Because ultimately the principle is simpler than the implementation:
The CMS doesn't need to own something to make it editable.