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. WithLocMemCache, every worker maintains its own cache and will fetch independently.
The HTTP client honours the standard
HTTP_PROXY/HTTPS_PROXYenvironment 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 on —
getlaw_refreshexits 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 Q → Scheduled tasks → Add.
| 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
- The template tag /
get_text(...)looks up the API key by text type inGETLAW["KEYS"]. - It checks the configured Django cache for a fresh entry (newer than
TTL_SECONDS). If fresh, the cached HTML is returned. - Otherwise the package calls
GET https://www.getlaw.de/api/texts/{api_key}with theX-getLaw-API-Version: 1header, parses the JSON response, and caches thecontentfield. - 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 soGetlawAdminBannerMiddlewarecan 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 inDEBUG).
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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3ca6771dfd7c0c7df218dfa555938772c9d370fb7903e1b8b79de31548e893c1
|
|
| MD5 |
0ca196583fdeea24112abaa374041993
|
|
| BLAKE2b-256 |
c299358a17978bdbd30a570e1514a9a9474a167c820be85b6b2da929bca2aad8
|
Provenance
The following attestation bundles were made for django_getlaw-0.1.3.tar.gz:
Publisher:
release.yml on ralfzosel/django-getlaw
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_getlaw-0.1.3.tar.gz -
Subject digest:
3ca6771dfd7c0c7df218dfa555938772c9d370fb7903e1b8b79de31548e893c1 - Sigstore transparency entry: 1523226635
- Sigstore integration time:
-
Permalink:
ralfzosel/django-getlaw@78a1eba4d76a00c52ae452610a1fa3eb3310ce0d -
Branch / Tag:
refs/tags/v0.1.3 - Owner: https://github.com/ralfzosel
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@78a1eba4d76a00c52ae452610a1fa3eb3310ce0d -
Trigger Event:
push
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1d151edca68a0bc20c56d00f911892ff6ab344e5abddffa23d73fe422fbc3101
|
|
| MD5 |
ea5341ec0120fea45700c19a458a1716
|
|
| BLAKE2b-256 |
437a27f77e27e882300e2f4f3183ecc35d0b75f393935bca06abe9023984ec22
|
Provenance
The following attestation bundles were made for django_getlaw-0.1.3-py3-none-any.whl:
Publisher:
release.yml on ralfzosel/django-getlaw
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_getlaw-0.1.3-py3-none-any.whl -
Subject digest:
1d151edca68a0bc20c56d00f911892ff6ab344e5abddffa23d73fe422fbc3101 - Sigstore transparency entry: 1523226684
- Sigstore integration time:
-
Permalink:
ralfzosel/django-getlaw@78a1eba4d76a00c52ae452610a1fa3eb3310ce0d -
Branch / Tag:
refs/tags/v0.1.3 - Owner: https://github.com/ralfzosel
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@78a1eba4d76a00c52ae452610a1fa3eb3310ce0d -
Trigger Event:
push
-
Statement type: