Skip to main content

Django Unfold admin integration for django-fobi

Project description

django-unfold-fobi

PyPI - Version Build

Unfold integration for django-fobi: Unfold-styled admin, Unfold theme for the form builder UI, DRF compatibility shims, and a few Unfold-friendly admin views. The optional Sites extension lives in unfold_fobi.contrib.sites.

This README shows the integration steps as they are used in djangocms_test/settings.py and djangocms_test/urls.py.

Quick Start

  1. Install dependencies.
pip install django-unfold-fobi
  1. Follow the standard django-fobi installation and plugin setup first, then add the unfold_fobi integration apps to INSTALLED_APPS (order matters).
INSTALLED_APPS = [
    "unfold",  # must be before django.contrib.admin
    "crispy_forms",  # required by unfold_fobi layouts/templates
    "fobi.contrib.themes.simple",  # required, Unfold theme extends it
    "unfold_fobi",
]

Add your normal django-fobi apps, plugins, and optional DRF integration exactly as documented by django-fobi.

If you want site-aware form bindings, add the optional app too:

INSTALLED_APPS += [
    "django.contrib.sites",
    "unfold_fobi.contrib.sites",
]

SITE_ID = 1
  1. Add template context processors and builtins.
TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.request",
                "fobi.context_processors.theme",
                "unfold_fobi.context_processors.admin_site",
            ],
            "builtins": [
                "unfold_fobi.templatetags.unfold_fobi_tags",
            ],
        },
    },
]
  1. Enable the Unfold Fobi theme and crispy forms pack.
FOBI_THEME = "unfold"
FOBI_DEFAULT_THEME = "unfold"

CRISPY_TEMPLATE_PACK = "unfold_crispy"
CRISPY_ALLOWED_TEMPLATE_PACKS = ["unfold_crispy"]
  1. Add URLs from djangocms_test/urls.py.
from django.urls import include, path

urlpatterns = [
    # DRF integration endpoints (optional)
    path("api/", include("fobi.contrib.apps.drf_integration.urls")),
    path("api/", include("unfold_fobi.api.urls")),

    # Public Fobi routes
    path("fobi/", include("unfold_fobi.urls.public")),

    # Admin/Fobi edit + legacy compatibility routes
    # Place before admin.site.urls to avoid admin catch_all_view shadowing.
    path("admin/fobi/", include("unfold_fobi.urls.admin")),
]
  1. Run migrations.
python manage.py migrate

Settings Reference (Unfold Sidebar)

This is the "Forms" navigation block used in djangocms_test/settings.py. It links to unfold_fobi admin views and Fobi routes. Include the reverse_lazy/gettext_lazy imports.

from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _

UNFOLD = {
    "SIDEBAR": {
        "navigation": [
            {
                "title": _("Forms"),
                "separator": True,
                "collapsible": True,
                "items": [
                    {
                        "title": _("All Forms"),
                        "icon": "list",
                        "link": reverse_lazy("admin:unfold_fobi_formentryproxy_changelist"),
                        "active": lambda request: (
                            getattr(getattr(request, "resolver_match", None), "view_name", "")
                            in {
                                "admin:unfold_fobi_formentryproxy_changelist",
                                "admin:unfold_fobi_formentryproxy_add",
                                "admin:unfold_fobi_formentryproxy_change",
                                "fobi.edit_form_entry",
                            }
                            or request.path.startswith("/admin/unfold_fobi/formentryproxy/")
                            or request.path.startswith("/admin/fobi/forms/")
                        ),
                    },
                    {
                        "title": _("Import Form"),
                        "icon": "upload",
                        "link": reverse_lazy("admin:unfold_fobi_formentryproxy_import_form_entry_action"),
                        "active": lambda request: getattr(
                            getattr(request, "resolver_match", None), "view_name", ""
                        )
                        in {
                            "admin:unfold_fobi_formentryproxy_import_form_entry_action",
                            "fobi.import_form_entry",
                        }
                        or request.path.startswith("/admin/fobi/forms/import/")
                        or request.path.startswith("/admin/unfold_fobi/formentryproxy/import-json/"),
                    },
                    {
                        "title": _("Wizards"),
                        "icon": "auto_awesome",
                        "link": reverse_lazy("fobi.form_wizards_dashboard"),
                        "active": lambda request: getattr(
                            getattr(request, "resolver_match", None), "view_name", ""
                        )
                        in {
                            "fobi.form_wizards_dashboard",
                        }
                        or request.path.startswith("/admin/fobi/wizards/")
                    },
                ],
            },
        ],
    },
}

Optional Sites Integration

The base package does not require django.contrib.sites. Enable the Sites extension only if your project needs form-to-site bindings.

  1. Add the optional apps:
INSTALLED_APPS += [
    "django.contrib.sites",
    "unfold_fobi.contrib.sites",
]

SITE_ID = 1
UNFOLD_FOBI_SITES_FOR_USER = "myproject.site_permissions.sites_for_user"
  1. Run migrations:
python manage.py migrate
  1. Re-register your admin classes with the mixins from unfold_fobi.contrib.sites.admin:
from django.contrib import admin
from fobi.contrib.plugins.form_handlers.db_store.models import SavedFormDataEntry

from unfold_fobi.admin import FormEntryProxyAdmin as BaseFormEntryProxyAdmin
from unfold_fobi.contrib.sites.admin import (
    RelationSiteScopeAdminMixin,
    SiteAwareFormEntryMixin,
)
from unfold_fobi.fobi_admin import SavedFormDataEntryAdmin as BaseSavedEntryAdmin
from unfold_fobi.models import FormEntryProxy


admin.site.unregister(FormEntryProxy)


@admin.register(FormEntryProxy)
class FormEntryProxyAdmin(
    SiteAwareFormEntryMixin,
    RelationSiteScopeAdminMixin,
    BaseFormEntryProxyAdmin,
):
    site_relation_lookup = "site_binding__sites"

    def has_view_permission(self, request, obj=None):
        return self._has_site_scoped_permission(request, "view", obj)

    def has_change_permission(self, request, obj=None):
        return self._has_site_scoped_permission(request, "change", obj)

    def has_delete_permission(self, request, obj=None):
        return self._has_site_scoped_permission(request, "delete", obj)


admin.site.unregister(SavedFormDataEntry)


@admin.register(SavedFormDataEntry)
class SavedFormDataEntryAdmin(RelationSiteScopeAdminMixin, BaseSavedEntryAdmin):
    site_relation_lookup = "form_entry__site_binding__sites"

    def has_view_permission(self, request, obj=None):
        return self._has_site_scoped_permission(request, "view", obj)

What the package provides:

  • FobiFormSiteBinding to persist form-to-site relations.
  • SiteAwareFormEntryMixin to add the synthetic sites field to the form admin.
  • RelationSiteScopeAdminMixin to filter querysets by a relation path such as site_binding__sites or form_entry__site_binding__sites.
  • Binding helpers in unfold_fobi.contrib.sites.services.

What stays in the consuming project:

  • Mapping users to allowed sites.
  • Any fallback/default assignment logic when no sites are selected.
  • Project-specific has_*_permission() policy.
  • Project-specific clone/import wiring beyond the provided binding helpers.

Development & Testing

Prerequisites

Install Poetry for dependency management.

Setup

# Install all dependencies (runtime + dev)
poetry install

# Run database migrations for the test server
poetry run python tests/server/manage.py migrate

Running Tests

# Run the full pytest suite
poetry run pytest -q

# Run with verbose output
poetry run pytest -v

# Run a specific test file
poetry run pytest tests/test_smoke.py -v

Manual Test Server

A local Django test server is included for visual inspection and manual testing.

# Apply migrations (creates tests/server/db.sqlite3)
poetry run python tests/server/manage.py migrate

# Create a superuser for admin login
poetry run python tests/server/manage.py createsuperuser

# Optionally seed a test form with sample fields
poetry run python tests/server/manage.py create_test_form

# Start the server
poetry run python tests/server/manage.py runserver 8080

Then open:

Test Server Structure

tests/
├── conftest.py                 # Shared fixtures (admin_user, form_entry)
├── test_smoke.py               # Smoke tests (setup, URLs, views, seed data)
├── e2e/                        # Playwright browser tests (T04)
│   ├── conftest.py             # Browser fixtures (admin_login)
│   └── test_placeholder.py    # Scaffold placeholder
└── server/
    ├── manage.py               # Django management entry point
    ├── db.sqlite3              # SQLite DB (created by migrate, gitignored)
    └── testapp/
        ├── settings.py         # Full Unfold + Fobi + unfold_fobi config
        ├── urls.py             # Admin + Fobi + DRF URL wiring
        ├── wsgi.py
        └── asgi.py

Notes

  • unfold_fobi.apps.UnfoldFobiConfig.ready() automatically loads the DRF compatibility shim and re-registers Fobi admin classes with Unfold, so you do not need manual imports.
  • The Unfold Fobi theme is registered as uid="unfold" in unfold_fobi/fobi_themes.py.
  • If you are using DRF integration, ensure the db_store handler is enabled for a form so REST submissions are stored. New forms get it automatically, but existing ones may need the handler attached.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_unfold_fobi-0.1.2.tar.gz (10.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_unfold_fobi-0.1.2-py3-none-any.whl (66.8 kB view details)

Uploaded Python 3

File details

Details for the file django_unfold_fobi-0.1.2.tar.gz.

File metadata

  • Download URL: django_unfold_fobi-0.1.2.tar.gz
  • Upload date:
  • Size: 10.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: python-httpx/0.28.1

File hashes

Hashes for django_unfold_fobi-0.1.2.tar.gz
Algorithm Hash digest
SHA256 49aaa0bd770613fa21330e217fe7251648d302547ce82ee445e1d57b6fb1cf74
MD5 2b8454df1384560d47a704ae066c7dda
BLAKE2b-256 1514efea41dcb7e8747bbb3b79fa93036a5c3492f616f686237cf301c4ac82e2

See more details on using hashes here.

File details

Details for the file django_unfold_fobi-0.1.2-py3-none-any.whl.

File metadata

File hashes

Hashes for django_unfold_fobi-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 76505cc3ebdbb13e5b9fbdc5337bc30a27494f395a4196298c1b19fa1d3bb924
MD5 b5bd09836dd18dec63595164e5061ea3
BLAKE2b-256 0e58834650c52e3567d4800e61984748e17c1fa64147d950c41925eb66b9ec8f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page