django-sphinx-hosting
Current version is 1.10.1.
This reusable Django application provides models, views, permissions, REST API endpoints and management commands for making a private Sphinx documentation hosting platform.
This is useful for when you want Sphinx documentation for your internal software projects, but you do not want that documentation do be shared with a third-party.
Installation
To install from PyPI:
pip install django-sphinx-hosting
If you want, you can run the tests:
python -m unittest discover
Features
Users must be authenticated to view docs
Multiple levels of privileges within the system based on Django authentication
Manage multiple versions of your docs per project
Automatically build and display navigation for each version of your documentaion
Renders all documentation published within with a consistent theme
Tag projects with classifiers to refine searching and filtering
Search across all projects
Use REST API to programmatically interact with the system. Useful for integrating into a CI/CD system
Configuration
Update INSTALLED_APPS
As with most Django applications, you should add django-sphinx-hosting and
its key dependencies to the INSTALLED_APPS within your settings file
(usually settings.py).
Example:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.messages',
'django.contrib.sessions',
'django.contrib.sites',
# ---------- add these ----------------
'rest_framework',
'rest_framework.authtoken',
'django_filters',
'drf_spectacular',
'crispy_forms',
'crispy_bootstrap5',
'haystack',
'academy_theme',
'wildewidgets',
'sphinx_hosting',
'sphinx_hosting.api'
# -------- done add these -------------
# Then your usual apps...
'blog',
]
Configure django-sphinx-hosting itself
For django-sphinx-hosting itself, you’ll typically want to add a
SPHINX_HOSTING_SETTINGS dict to localize django-sphinx-hosting to your
organization.
Full example:
SPHINX_HOSTING_SETTINGS = {
'LOGO_IMAGE': 'core/images/my-org-logo.png',
'LOGO_WIDTH': '75%',
'LOGO_URL': 'https://www.example.com',
'SITE_NAME': 'MyOrg Documentation',
'EXCLUDE_FROM_LATEST': ['*.dev*', '*.beta*'],
'EXTRA_MENU_ITEMS': [
{
'text': 'REST API',
'icon': 'diagram-3',
'url': '/api/v1/schema/swagger-ui/'
}
],
'MENU_ITEM_BUILDERS': ['myproject.core.navigation.build_menu_items'],
'NAVBAR_CLASS': 'myproject.core.wildewidgets.MainMenu',
'PROJECT_DETAIL_LAYOUT_BUILDERS': [
'myproject.core.wildewidgets.extend_project_detail_layout',
],
'SEARCH_RESULT_RENDERERS': {
'myproject.SearchNote': 'myproject.core.search.render_search_note_result',
},
}
LOGO_IMAGEA Django filesystem path to the image you want to use for the logo at the top of the navigation sidebar.
LOGO_WIDTHAny valid CSS width specifier. This will be applied to the
LOGO_IMAGE.LOGO_URLWhen a user clicks the
LOGO_IMAGE, they’ll be sent to this URL.SITE_NAMEThis will be used in the HTML title tags for each page, and wil be used as the
alttag for theLOGO_IMAGE.EXCLUDE_FROM_LATESTThis is a list of glob patterns to apply to version numbers. If the version number matches any of these patterns, it will not be marked as latest in the database and will not be indexed in the search engine. The default list of patterns is
['*.dev*', '*.alpha*', '*.beta*', '*.rc*'].EXTRA_MENU_ITEMSAdd static top-level menu items to the primary sidebar menu.
Each list entry must be either:
a
wildewidgets.MenuIteminstancea dict with keys
text(required),url,icon,items, andactive
iconvalues must use Bootstrap Icons names such asdiagram-3orlife-preserver.Unknown keys raise an error at request time.
MENU_ITEM_BUILDERSAdd conditional top-level menu items at request time via dotted-path callables.
Builder contract:
def build_items(*, request, user, menu): return None | MenuItem | dict | Iterable[MenuItem | dict]
Builders are evaluated in list order after built-in conditional items. Exceptions from builders are not swallowed and will propagate.
NAVBAR_CLASSOverride the sidebar class used by all
sphinx_hostingviews. This must resolve to awildewidgets.Navbarsubclass.PROJECT_DETAIL_LAYOUT_BUILDERSAdd request-time project detail customizations via dotted-path callables.
Builder contract:
def extend_project_detail_layout(*, request, user, project, layout, view): return None
Builders run only on
ProjectDetailViewafter the built-in widgets and sidebar actions are added. Any widgets appended throughlayout.add_widgetappear after the package’s default project-detail sections.SEARCH_RESULT_RENDERERSAdd host-model result renderers to unified global search.
Each key must be a Django model label such as
core.SearchNoteand each value must be a dotted-path callable that renders one HaystackSearchResultfor that model.Renderer contract:
def render_result(*, result, request, user, view): return Widget(...)
Host models only participate in unified search when they have both a registered Haystack
SearchIndexand a renderer entry here. See Unified Search for the complete setup.
Upgrade note
Navigation item configuration is now strict. Invalid menu item schemas, unknown keys, or invalid builder return types will raise explicit request-time errors. This keeps configuration bugs visible and easy to diagnose.
Configure django-wildewidgets
All HTML in django-sphinx-hosting is generated by django-wildewidgets widgets. We use the
django-theme-academy to
provide the HTML boilerplate to house the widget output, and
django-crispy-forms for our form
rendering.
Add this code to your settings.py to configure them properly:
# crispy-forms
# ------------------------------------------------------------------------------
CRISPY_ALLOWED_TEMPLATE_PACKS = 'bootstrap5'
CRISPY_TEMPLATE_PACK = 'bootstrap5'
# django-theme-academy
# ------------------------------------------------------------------------------
ACADEMY_THEME_SETTINGS = {
# Header
'APPLE_TOUCH_ICON': 'core/images/apple-touch-icon.png',
'FAVICON_32': 'core/images/favicon-32x32.png',
'FAVICON_16': 'core/images/favicon-16x16.png',
'FAVICON': 'core/images/favicon.ico',
'SITE_WEBMANIFEST': 'core/images/site.webmanifest',
# Footer
'ORGANIZATION_LINK': 'https://github.com/caltechads/django-sphinx-hosting',
'ORGANIZATION_NAME': 'Sphinx Hosting',
'ORGANIZATION_ADDRESS': '123 Main Street, Everytown, ST',
'COPYRIGHT_ORGANIZATION': 'Sphinx Hosting',
'FOOTER_LINKS': [
('https://example.com', 'Organization Home'),
('https://example.com/documents/privacy.pdf', "Privacy Policy")
]
}
# django-wildewidgets
# ------------------------------------------------------------------------------
WILDEWIDGETS_DATETIME_FORMAT = "%Y-%m-%d %H:%M %Z"
For ACADEMY_THEME_SETTINGS, localize to your organization by updating all the settings
appropriately.
FAVICON_*,APPLE_TOUCH_ICONandSITE_WEBMANIFESTSet these to the Django filesystem path to the files you want to use.
FOOTER_LINKSAdd links to the footer by listing 2-tuples of
("__URL__", "__LABEL__")
Configure Haystack
We use django-haystack to support our documentation search feature, but it is up to you what specific backend you want to configure. See Haystack: Configuration for instructions on how to configure Haystack for different backends.
Here is example settings.py code for using OpenSearch as our search backend:
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'django_haystack_opensearch.haystack.OpenSearchSearchEngine',
'URL': 'http://sphinx-hosting-search.example.com:9200/',
'INDEX_NAME': 'sphinx_hosting',
},
}
Configure Django REST Framework
To make the django-sphinx-hosting API work, add this code to your settings.py:
# djangorestframework
# ------------------------------------------------------------------------------
REST_FRAMEWORK = {
# https://www.django-rest-framework.org/api-guide/parsers/#setting-the-parsers
'DEFAULT_PARSER_CLASSES': ('rest_framework.parsers.JSONParser',),
# https://www.django-rest-framework.org/api-guide/authentication/#tokenauthentication
'DEFAULT_AUTHENTICATION_CLASSES': ('rest_framework.authentication.TokenAuthentication',),
# https://django-filter.readthedocs.io/en/master/guide/rest_framework.html
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
# https://www.django-rest-framework.org/api-guide/pagination/#limitoffsetpagination
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
# https://github.com/tfranzel/drf-spectacular
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
'PAGE_SIZE': 100,
}
# drf-spectacular
# ------------------------------------------------------------------------------
# https://drf-spectacular.readthedocs.io/en/latest/settings.html
SPECTACULAR_SETTINGS = {
'SCHEMA_PATH_PREFIX': r'/api/v1',
'SERVERS': [
{
'url': 'https://localhost',
'description': 'Django Sphinx Hosting'
}
],
'TITLE': 'YOUR_SITE_NAME'
'VERSION': __version__,
'DESCRIPTION': """__YOUR_DESCRIPTION__HERE"""
}
For DEFAULT_AUTHENTICATION_CLASSES, use whatever authentication class you
want – we’re just using TokenAuthentication here as an example. For the rest
of the settings, you’ll need at least what we’ve detailed above, but feel free to add
to them if you have additional API views you need to support in your own application
code.
Update your top-level urlconf
from django.urls import path, include
from sphinx_hosting import urls as sphinx_hosting_urls
from sphinx_hosting.api import urls as sphinx_hosting_api_urls
from wildewidgets import WildewidgetDispatch
urlpatterns = [
path('/docs/', include(sphinx_hosting_urls, namespace='sphinx_hosting')),
path('/docs/wildewidgets_json', WildewidgetDispatch.as_view(), name='wildewidgets_json'),
path('api/v1/', include(sphinx_hosting_api_urls, namespace='sphinx_hosting_api')),
]