Reusable app for a simple content feed: text posts (ContentPost) authored by a user / profile, each with any number of attached images (ContentPostImage). Reactions and @-mentions are layered in optionally through the plugin architecture, so the feed works on its own and gains those features automatically when the matching packages are installed.
Install the package with pip install baseapp-backend.
If you want to develop, install using this other guide.
The package registers itself as a plugin (see baseapp.content_feed.plugin:ContentFeedPlugin), so the GraphQL wiring happens through the plugin registry.
- Add
baseapp.content_feedtoINSTALLED_APPS:
INSTALLED_APPS = [
# ...
"baseapp.content_feed",
# ...
]-
Make sure your project's
graphql.pycomposes the schema viaplugin_registry.get_all_graphql_*()soContentFeedQueries(contentPost,contentPosts) andContentFeedMutations(contentPostCreate) are picked up automatically. -
Define the concrete models (see models) and point the swapper settings at them:
BASEAPP_CONTENT_FEED_CONTENTPOST_MODEL = "content_feed.ContentPost"
BASEAPP_CONTENT_FEED_CONTENTPOSTIMAGE_MODEL = "content_feed.ContentPostImage"Run ./manage.py makemigrations / ./manage.py migrate after defining them.
Both models are abstract + swappable, and the package ships no concrete models or migrations — your project must subclass the abstracts in a local app and point the swapper settings at them.
| Abstract | Concrete reference | Purpose |
|---|---|---|
AbstractContentPost |
ContentPost |
A feed post: user (author), content text, timestamps. When baseapp_profiles is installed, a profile FK (related_name content_posts) is mixed in. |
AbstractContentPostImage |
ContentPostImage |
An image attached to a post via the post FK (related_name images). |
Both inherit DocumentIdMixin, so any post / image can be the target of mentions, comments, reactions, etc. without extra wiring.
Minimal concrete definition:
# myproject/content_feed/models.py
from baseapp.content_feed.models import AbstractContentPost, AbstractContentPostImage
class ContentPost(AbstractContentPost):
class Meta(AbstractContentPost.Meta):
pass
class ContentPostImage(AbstractContentPostImage):
class Meta(AbstractContentPostImage.Meta):
pass| Field | Description |
|---|---|
contentPost(id) |
RelayNode fetch of a single post by relay id. |
contentPosts |
Filterable connection of posts. Supports orderBy: created via ContentPostFilter. |
| Field | Purpose |
|---|---|
contentPostCreate |
Create a post from the current user (and current profile, if baseapp_profiles is installed). Accepts content, isReactionsEnabled, optional mentionedProfileIds, and uploaded images files. |
contentPostCreate runs in a transaction: it persists the post, sets the reactions-enabled flag through the reactable_metadata service, creates each validated ContentPostImage, and records mentions through the mentions service.
ContentPostObjectType opts into shared interfaces by name (they degrade gracefully when the providing package is absent):
interfaces = graphql_shared_interfaces.get(
RelayNode, "MentionsInterface", "ReactionsInterface"
)MentionsInterface(frombaseapp_mentions) — exposes the post's mentions.ReactionsInterface(frombaseapp_reactions) — exposesreactionsCount/isReactionsEnabled/reactions.ContentPostObjectType.get_querysetannotates the reactable metadata up front so these resolvers don't N+1.
Content Feed consumes the following services lazily via shared_services.get(...) — they are all optional, behaviour degrades gracefully when absent:
reactable_metadata(frombaseapp_reactions) —set_is_reactions_enabledon create andannotate_querysetwhen resolving the feed.mentions(frombaseapp_mentions) —update_mentionsto track inline @-mentions in post content.
baseapp_profiles— when installed,ContentPostexposes the authoringprofileandcontentPostCreatesets it from the current profile.baseapp_reactions— per-post reactions count / enabled flag viaReactableMetadataServiceandReactionsInterface.baseapp_mentions— inline @-mention tracking oncontentPostCreate.
Clone the project inside your project's backend dir:
git clone git@github.com:silverlogic/baseapp-backend.git
And manually install the package:
pip install -e baseapp-backend
The -e flag means any change you make in the cloned repo files will be reflected in the project. Run the test suite from the backend root:
docker compose run --rm web pytest baseapp/content_feed/