Skip to main content

Embed legal texts (Impressum, Datenschutz, AGB, Widerruf, Barrierefreiheit) from getLaw.de in Django projects.

Project description

django-getlaw

Embed legal texts (Impressum, Datenschutzerklärung, AGB, Widerrufsbelehrung, Barrierefreiheitserklärung) from getLaw.de in your Django project.

This is the Django counterpart to the official getLaw plugins for WordPress and Contao. It calls the same getLaw client API (/api/texts/{api_key} with the X-getLaw-API-Version: 1 header), caches the response in your Django cache backend, and refreshes it lazily every 24 hours (configurable). You do not need a scheduler for correctness: the next page render after TTL_SECONDS will fetch fresh HTML. Scheduling the bundled management command is still recommended—see Scheduling getlaw_refresh.

The package depends on Django only (no third-party HTTP client) and uses the standard library urllib so it works wherever Django works.

Installation

uv add django-getlaw          # or: pip install django-getlaw

Add the app to INSTALLED_APPS:

# settings.py
INSTALLED_APPS = [
    # ...
    "django_getlaw",
]

Configuration

All settings live under a single GETLAW dict. Only KEYS is required; every text type you want to render must have an API key here. Get the keys from your getLaw.de account (one key per text).

# settings.py
GETLAW = {
    "KEYS": {
        "impressum": os.environ["GETLAW_KEY_IMPRESSUM"],
        "datenschutz": os.environ["GETLAW_KEY_DATENSCHUTZ"],
        # "agb": "...",
        # "widerruf": "...",
        # "barrierefreiheit": "...",
    },

    # Optional, shown with their defaults:
    "TTL_SECONDS": 86400,                              # 24 hours
    "API_BASE_URL": "https://www.getlaw.de/api/texts/",
    "API_VERSION": "1",
    "TIMEOUT_SECONDS": 10,
    "CACHE_ALIAS": "default",                          # any django CACHES alias
    "CACHE_KEY_PREFIX": "getlaw:",
    "USER_AGENT": "django-getlaw/<version>",           # auto-filled with package version
}

Use a shared cache backend (Redis, Memcached, or Django's DatabaseCache) in multi-worker deployments. With LocMemCache, every worker maintains its own cache and will fetch independently.

The HTTP client honours the standard HTTP_PROXY / HTTPS_PROXY environment variables automatically.

Usage

Template tag

{% load getlaw %}

<section>
  {% getlaw "impressum" %}
</section>

The tag returns safe HTML. On GETLAW misconfiguration or fetch failure it returns an empty string in production (DEBUG=False) and a visible HTML comment when DEBUG=True, so problems are obvious in development without breaking pages in production.

Programmatic API

from django_getlaw import get_text, refresh_text

html = get_text("impressum")             # cached, lazy refresh after TTL
html = get_text("impressum", force=True) # bypass TTL, fetch now
html = refresh_text("impressum")         # alias for force-fetch

Management command

# Refresh every configured text:
uv run python manage.py getlaw_refresh

# Refresh specific texts only:
uv run python manage.py getlaw_refresh impressum datenschutz

The command exits with a non-zero status if any refresh fails — wire it into cron or your scheduler so failures alert you.

Scheduling getlaw_refresh

Lazy refresh is enough if traffic regularly hits every embedded text before the cache goes stale. A scheduled run is still recommended because:

  • Latency — After TTL_SECONDS, the first visitor pays the HTTP round-trip to getLaw.de; a scheduled job refreshes the cache in the background so normal page loads stay fast.
  • Low-traffic pages — Legal snippets on rarely opened URLs might otherwise stay cached longer than you expect until someone loads that page.
  • Failures you can alert ongetlaw_refresh exits non-zero when a refresh fails, so cron or your job runner can notify you. Lazy refresh alone only triggers fetches when a page is rendered (you still get the admin banner for staff if you enabled it, but no automatic ops alert unless you add one).

Pick one approach below.

Cron (system scheduler)

Run daily (or as often as you like) under the same Unix user and environment as the app. Example: every day at 03:00, log stdout/stderr:

0 3 * * * cd /path/to/your/project && /path/to/.venv/bin/python manage.py getlaw_refresh >> /var/log/getlaw_refresh.log 2>&1

Adjust paths; use uv run python manage.py getlaw_refresh if that is how you invoke Django in production. Point $MAILTO or your monitoring at non-zero exit codes from the cron job if you need alerts.

Django-Q (admin or code)

Django-Q can run the same command on a schedule. The qcluster worker process must be running continuously; otherwise queued schedules never execute.

Admin UI: Django admin → Django QScheduled tasksAdd.

Field Value
Func django.core.management.call_command
Args getlaw_refresh — or getlaw_refresh,impressum,datenschutz to refresh only those types (comma-separated, same as CLI arguments after the command name).
Schedule type Daily — repeats every day at the clock time taken from Next run (the first run sets that time). Use Weekly / Cron if you prefer.
Next run Next datetime the job should run (project timezone).
Repeats -1 for “forever”.

Leave Cluster blank unless you use named clusters.

Code:

from django_q.tasks import schedule
from django_q.models import Schedule

schedule(
    "django.core.management.call_command",
    "getlaw_refresh",
    name="getlaw-refresh-daily",
    schedule_type=Schedule.DAILY,
)

Admin banner on fetch failures

django-getlaw always falls back to the last known content when an API fetch fails — there is no upper bound on staleness, because a slightly outdated Impressum is virtually always better than a blank page. To make sure the failure doesn't go unnoticed, add the bundled middleware after Django's messages middleware:

# settings.py
MIDDLEWARE = [
    # ...
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django_getlaw.middleware.GetlawAdminBannerMiddleware",
]

Any staff user (request.user.is_staff) visiting a Django admin page will see a warning banner listing the affected text types and the most recent error. The banner re-renders on every admin request and disappears as soon as a successful fetch (template tag, refresh_text(...), or getlaw_refresh) clears the failure marker.

If you need the failure list programmatically (e.g. for a status endpoint or your own dashboard), call django_getlaw.fetch_failures().

Supported text types

impressum, datenschutz, agb, widerruf, barrierefreiheit — but the package does not hard-code that list. Any text type for which you put a key in GETLAW["KEYS"] is fetchable; the API decides what's valid. This means you don't have to wait for a package release if getLaw adds a new text type in the future.

How it works

  1. The template tag / get_text(...) looks up the API key by text type in GETLAW["KEYS"].
  2. It checks the configured Django cache for a fresh entry (newer than TTL_SECONDS). If fresh, the cached HTML is returned.
  3. Otherwise the package calls GET https://www.getlaw.de/api/texts/{api_key} with the X-getLaw-API-Version: 1 header, parses the JSON response, and caches the content field.
  4. On HTTP redirects (the way the getLaw API signals "invalid key"), HTTP errors, timeouts, or unparseable responses, the call raises a GetlawAPIError. If any cached content exists, it is returned as a fallback (regardless of age), a warning is logged, and a per-text-type failure marker is recorded so GetlawAdminBannerMiddleware can warn staff on the next admin page. The marker clears the next time a fetch succeeds. If nothing has ever been fetched successfully, the error propagates and the template tag renders empty (or an HTML comment in DEBUG).

The cache key includes a hash of the API key, so rotating a key in your settings transparently invalidates the corresponding entry.

Development

uv sync
uv run pytest
uv run ruff check

Licence

MIT — see LICENSE. The upstream WordPress and Contao plugins by getLaw.de are GPL-3.0; this package contains no code from them — it simply calls the same client HTTP API (endpoint and headers) as those plugins. The getLaw API page is only a short overview.

Acknowledgements

Thanks to getLaw.de for providing a clean API for clients (paid subscription; requires an API key).

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_getlaw-0.1.3.tar.gz (12.5 kB view details)

Uploaded Source

Built Distribution

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

django_getlaw-0.1.3-py3-none-any.whl (14.1 kB view details)

Uploaded Python 3

File details

Details for the file django_getlaw-0.1.3.tar.gz.

File metadata

  • Download URL: django_getlaw-0.1.3.tar.gz
  • Upload date:
  • Size: 12.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for django_getlaw-0.1.3.tar.gz
Algorithm Hash digest
SHA256 3ca6771dfd7c0c7df218dfa555938772c9d370fb7903e1b8b79de31548e893c1
MD5 0ca196583fdeea24112abaa374041993
BLAKE2b-256 c299358a17978bdbd30a570e1514a9a9474a167c820be85b6b2da929bca2aad8

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_getlaw-0.1.3.tar.gz:

Publisher: release.yml on ralfzosel/django-getlaw

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file django_getlaw-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: django_getlaw-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 14.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for django_getlaw-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 1d151edca68a0bc20c56d00f911892ff6ab344e5abddffa23d73fe422fbc3101
MD5 ea5341ec0120fea45700c19a458a1716
BLAKE2b-256 437a27f77e27e882300e2f4f3183ecc35d0b75f393935bca06abe9023984ec22

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_getlaw-0.1.3-py3-none-any.whl:

Publisher: release.yml on ralfzosel/django-getlaw

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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