django-oscar-blog
A blog app for django-oscar: posts, an Oscar dashboard section under Content, and posts that can link out to catalogue products they mention.
What this package does, and doesn't, do
- A
Postmodel: title/slug/excerpt/body/featured image/author/status (draft/published)/publish date, plusmeta_title/meta_descriptionfor SEO, arelated_productsmany-to-many ontocatalogue.Product, and tags (via django-taggit). - Storefront list/detail views (
oscar_blog.urls) and aPostSitemap. - An Oscar dashboard section (
oscar_blog.dashboard) for staff to create, edit, and delete posts — list/create/update/delete views following the exact same conventions as Oscar's owndashboard.pagesapp (flatpages), including its permission model (is_staffby default). - It does not ship a newsletter integration or a "featured products" CTA anywhere in the storefront — see "Adding a CTA" below. The related-products picker defaults to a plain multi-select but has an opt-in AJAX autocomplete mode — see "Related products" below.
Installation
uv add django-oscar-blog
Add to INSTALLED_APPS, after catalogue/partner (it references
catalogue.Product) and after oscar.config.Shop:
INSTALLED_APPS = [
...,
"taggit",
"oscar_blog.apps.OscarBlogConfig",
"oscar_blog.dashboard.apps.BlogDashboardConfig",
]
Run manage.py migrate — this package ships its own migration.
Wiring the storefront URLs
# urls.py
urlpatterns = [
path("blog/", include("oscar_blog.urls")),
...
]
Templates extend oscar/layout.html under oscar_blog/post_list.html and
oscar_blog/post_detail.html — override them the same way you'd override
any other Oscar template (a project-level templates/oscar_blog/...
directory).
Wiring the dashboard in ("Content" menu)
Oscar's own dashboard.apps.DashboardConfig.get_urls() is a hardcoded
list, not auto-discovered — every dashboard section Oscar ships is added
there directly, and third-party sections have to fork that config the same
way you'd fork any other Oscar app. This package's own test suite
(tests/dashboard.py) is a working example of exactly this fork — copy its
shape into your project, e.g. a dashboard/apps.py:
from django.apps import apps
from django.urls import include, path
from oscar.apps.dashboard.apps import DashboardConfig as OscarDashboardConfig
class DashboardConfig(OscarDashboardConfig):
def ready(self):
super().ready()
self.blog_app = apps.get_app_config("blog_dashboard")
def get_urls(self):
urls = super().get_urls()
urls.append(path("blog/", include(self.blog_app.urls[0])))
return urls
...and swap "oscar.apps.dashboard.apps.DashboardConfig" for your
project's own "yourproject.dashboard.apps.DashboardConfig" in
INSTALLED_APPS.
Then add the nav entry under Content, alongside Pages/Email
templates/Reviews, by overriding OSCAR_DASHBOARD_NAVIGATION (a plain
Django setting — copy Oscar's default from oscar.defaults and insert):
{
"label": _("Blog posts"),
"url_name": "dashboard:blog-post-list",
},
into the "Content" entry's "children" list.
Related products
Post.related_products is a plain (unordered) ManyToManyField onto
catalogue.Product. A published post's detail page lists them under
"Mentioned products", linking to each product's own page
(post_detail.html). Nothing on the Product side surfaces "posts that
mention me" yet — the reverse relation is available as
product.blog_posts.all() if a host project wants to add that to a
product page.
Dashboard picker widget — two modes, one setting:
OSCAR_BLOG_PRODUCT_AUTOCOMPLETE = True # default: False
- Off (default): a plain
<select multiple>with every product rendered as an<option>. Fine at a small catalogue's scale; at real scale every dashboard post-edit page load pulls in the whole catalogue. - On: the dashboard's related-products field switches to Oscar's own
oscar.apps.dashboard.catalogue.widgets.ProductSelectMultiple— a Select2 widget backed by Oscar's already-installeddashboard:catalogue-product-lookupendpoint (oscar.apps.dashboard.catalogue.views.ProductLookupView, a realtitle__icontainssearch, already staff-permission-gated). Only the currently selected products are pre-rendered; everything else loads as the user types. No new backend endpoint needed — this project'sdashboard/forms.pyjust wires Oscar's existing one in, plus one real fix Oscar itself doesn't apply consistently:ProductSelectMultipleneeds an explicitclass="select2 product-select"to actually get AJAX-backed search (confirmed againstoscar/static/oscar/js/oscar/dashboard.js'sinitSelects()— without it, the field would still render, just silently degraded to searching only the few options already in the page).
Requires oscar.apps.dashboard.catalogue installed (it almost
certainly already is — it's the products dashboard). If it isn't, turning
this setting on will fail with a NoReverseMatch on
dashboard:catalogue-product-lookup, not silently.
Tags
Uses django-taggit directly
(BSD-licensed) rather than a hand-rolled tag model — Post.tags is a
TaggableManager, editable in the dashboard as a plain comma-separated
field (taggit's own default widget, no extra code needed). Tags are
shared across posts (the same tag name is the same Tag row everywhere,
not per-post free text), and the storefront list view supports
?tag=<slug> filtering — both oscar_blog/post_list.html and
post_detail.html render each post's tags as links to that filtered
list. Run manage.py migrate after adding taggit to INSTALLED_APPS —
it ships its own migrations for the shared Tag/TaggedItem tables.
Adding a CTA (deliberately not built here)
This package intentionally ships no newsletter-subscribe or
"promote this product" call-to-action anywhere in its templates — that's
host-project UI/UX scope (placement, copy, branding), not something a
reusable blog app should assume. If your project adds one, the natural
places are oscar_blog/post_list.html/post_detail.html (override them)
or a custom template tag included from there.
Development
uv sync
uv run pytest
tests/settings.py boots a full (stock) Oscar app stack — this package
genuinely depends on Oscar's catalogue models, unlike a framework-agnostic
library, so its tests can't run against bare Django alone.
Metadata
Release files for django-oscar-blog 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| django_oscar_blog-0.4.0.tar.gz | 68.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_oscar_blog-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 87.2 kB
Release files / django_oscar_blog-0.4.0.tar.gz
| Download URL | django_oscar_blog-0.4.0.tar.gz |
|---|---|
| Size | 68.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7b89a66704f6e72b133ef46fd0c1de75c86ec4343c4479bdfce767829ea42954
|
|
BLAKE2b-256 checksum How to use checksums |
1c478a84643e80012804c47233cec7a6874e154dcd2494b0558b53996688eddc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / django_oscar_blog-0.4.0-py3-none-any.whl
| Download URL | django_oscar_blog-0.4.0-py3-none-any.whl |
|---|---|
| Size | 18.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
65c504df394f94acf16d2fed3836be30f6c51803f09bc9280eb4693816dbd5ab
|
|
BLAKE2b-256 checksum How to use checksums |
bf559e2f5c60432173e326b30c4d7753fa5ea1f432de114d39281059fbf63c67
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|