ICHEC Django Core
ichec-django-core is the base for ICHEC's Django web apps. It gives your app
secure default settings, member and organisation models, sign-in through
Keycloak, and a REST API for all of these. You write the parts that are specific
to your app.
The reference app
is a complete app built on it, deployed with the ichec.platform Ansible
collection.
Try the example app
The app directory is a minimal portal built on the library. Run it
locally to see what you get before you write any code. You need
uv (brew install uv).
git clone https://git.ichec.ie/platform-engineering/modules/web/ichec-django-core.git
cd ichec-django-core
uv run tox exec -e dev -- python manage.py migrate
uv run tox exec -e dev -- python manage.py createsuperuser
uv run tox -e dev
Sign in at http://localhost:8000/accounts/login/ as the admin you created,
then open http://localhost:8000/api/ to browse the API. The dev env in
pyproject.toml holds the development settings.
Use it in your app
This guide assumes you can build a basic Django app. If not, work through the Django tutorial first.
Add the library to your dependencies:
uv add ichec-django-core
Settings
Import the library's settings, then set the few values that depend on your project layout. Your settings file then looks like app/settings.py:
from pathlib import Path
from ichec_django_core import settings
from ichec_django_core.settings import *
BASE_DIR = Path(__file__).resolve().parent.parent
ROOT_URLCONF = "app.urls"
WSGI_APPLICATION = "app.wsgi.application"
ASGI_APPLICATION = "app.asgi.application"
TEMPLATES = settings.get_templates(BASE_DIR)
DATABASES = settings.get_databases(BASE_DIR)
STATIC_ROOT = settings.get_static_root(BASE_DIR)
MEDIA_ROOT = settings.get_media_root(BASE_DIR)
Override any other setting below the import.
The library reads its settings from environment variables, which
settings.py lists with their defaults.
Django won't start without DJANGO_SECRET_KEY and DJANGO_ALLOWED_HOSTS. For
local work, set them in a tox env, as the dev env in
pyproject.toml does. In production, also set
DJANGO_PORTAL_URL and DJANGO_DEFAULT_FROM_EMAIL: a deploy check fails
without them.
Scheduled commands
Run these on a timer. The webapp_django role of the ICHEC Ansible collection
does it for you.
| Command | When |
|---|---|
retry_emails |
every 5 minutes |
run_workflow_schedule |
every 5 minutes, with workflows |
send_notification_digests |
daily |
purge_uploads |
daily |
URLs
Register the library's API views on your own router, and include its other views, as in app/urls.py:
from django.urls import include, path
from rest_framework import routers
from ichec_django_core.urls import register_drf_views
router = routers.DefaultRouter()
register_drf_views(router)
urlpatterns = [
path("api/", include(router.urls)),
path("", include("ichec_django_core.urls")),
]
handler404 = "ichec_django_core.views.errors.not_found"
handler500 = "ichec_django_core.views.errors.server_error"
Register your own viewsets on the same router, so the whole API sits under one
/api/ root. If Django serves your Angular app, add spa_route() from
views/spa.py last.
Say who may do what
Each model declares who may do what with it, once:
from django.db import models
from ichec_django_core.access import HasPermission, Owner, Policy
class Application(models.Model):
class Access(Policy):
applicant = Owner("applicant")
manager = HasPermission("change_application")
view = applicant | manager
change = applicant.when(status="draft")
A viewset built on SearchableModelViewSet then lists only the rows the
caller may see, and refuses what the policy doesn't allow.
docs/access.md covers status changes, roles and hidden
fields, and docs/members.md who sees whom.
Generate your app's client
Your API's schema, schema.yaml, and a typed Python client come from your
code. Install the codegen extra, give your app a client package, and run:
python manage.py generate_client myapp.client
Commit what it writes. In CI, add --check to fail when it's out of date.
Sign in with Keycloak
The example app uses Django's own login, which lets anyone register. Real deployments sign users in through Keycloak with OpenID Connect (OIDC).
Register your app as a client in the Keycloak realm, then set:
WITH_OIDC=1
OIDC_RP_CLIENT_ID=my-app
OIDC_RP_CLIENT_SECRET=...
OIDC_OP_AUTHORIZATION_ENDPOINT=https://keycloak.example.org/realms/my-realm/protocol/openid-connect/auth
OIDC_OP_TOKEN_ENDPOINT=https://keycloak.example.org/realms/my-realm/protocol/openid-connect/token
OIDC_OP_USER_ENDPOINT=https://keycloak.example.org/realms/my-realm/protocol/openid-connect/userinfo
OIDC_OP_JWKS_ENDPOINT=https://keycloak.example.org/realms/my-realm/protocol/openid-connect/certs
OIDC_OP_LOGOUT_ENDPOINT=https://keycloak.example.org/realms/my-realm/protocol/openid-connect/logout
Leave DJANGO_WITH_USER_LOGIN unset in production. The built-in login locks out
repeated failed sign-ins, and Django must only be reachable through nginx.
To sign in through a Keycloak on your machine, with a Test realm on port 8080,
run uv run tox -e dev-oidc.
mozilla-django-oidc
does the OIDC work, and its docs explain each setting.
Optional features
Each of these is off until you turn it on. Each link says how.
- Forms defined by JSON Schema, with drafts and file answers: docs/forms.md.
- Workflows, review flows held as data, so each call configures its own: docs/workflows.md.
- Background tasks on Celery: app/celery.py and tasks.
- Facet counts beside a list's results: views/facets.py.
- File storage through nginx or S3, and resumable uploads through tusd: docs/files.md.
- Email delivery records, retries and metrics: docs/email.md.
- Error reporting from the server and the browser app: set
SENTRY_DSNandDJANGO_ERROR_REPORTING_DSN. - Keycloak attributes copied onto each member's Keycloak user: sso/__init__.py.
Test your app
ichec_django_core.test has helpers for your app's tests:
AuthAPITestCasecalls your API as each test user.PolicyEnforcementTestCasechecks your API against each model's policy.AuthorizationSweepTestCasecalls every endpoint as each role.ListQueryBudgetTestCasefails any list that asks the database once per row.test/privacy.pychecks that erasing a member removes their personal data.
docs/e2e.md runs a user journey both in-process and against a deployed stack.
Work on this library
Install the development tools and run the checks that CI runs:
uv sync --group dev --extra async
uv run pytest
uv run ruff format --check .
uv run ruff check .
uv run mypy src
If you use direnv, run direnv allow once and the
environment activates when you enter the directory.
If you change a model or serializer, regenerate the API schema and client, and commit them:
uv run python manage.py generate_client ichec_django_core.client
Licence
Copyright of the Irish Centre for High End Computing (ICHEC), released under the MIT License. See LICENSE.
Metadata
Release files for ichec-django-core 7.5.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 | |
|---|---|---|---|
| ichec_django_core-7.5.0.tar.gz | 348.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ichec_django_core-7.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 653.0 kB
Release files / ichec_django_core-7.5.0.tar.gz
| Download URL | ichec_django_core-7.5.0.tar.gz |
|---|---|
| Size | 348.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4463f228ee73e089416f5dbb62b6bebb0aea995476019470fa900bb0be0f3781
|
|
BLAKE2b-256 checksum How to use checksums |
f7713ab74ccdc665f9b4227673aa71d83fc8668aa18b4c418e29ce153e999433
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Release files / ichec_django_core-7.5.0-py3-none-any.whl
| Download URL | ichec_django_core-7.5.0-py3-none-any.whl |
|---|---|
| Size | 304.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ab93615ea877bf97541e5821910939f40359272a44a58871699c47675409de40
|
|
BLAKE2b-256 checksum How to use checksums |
d833b6d335ba7468cb9884bd7f42220c3b09e725cb9259fee37dfaff49a3cfbc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|