Skip to main content

plain.redirection

Database-driven URL redirects with logging and admin interface.

Overview

You can manage URL redirects through the database instead of hardcoding them in your URL configuration. When a request results in a 404, the RedirectionMiddleware checks for a matching redirect rule and sends the user to the new location.

from plain.redirection.models import Redirect

# Create a simple redirect
Redirect.query.create(
    from_pattern="/old-page/",
    to_pattern="/new-page/",
    http_status=301,  # Permanent redirect (default)
)

All redirects are logged automatically, and 404s that don't match any redirect are logged separately. You can view and manage everything through the built-in admin interface.

Creating redirects

The Redirect model stores your redirect rules. Each redirect has a from_pattern to match against and a to_pattern to redirect to.

# Temporary redirect (302)
Redirect.query.create(
    from_pattern="/sale/",
    to_pattern="/promotions/summer-sale/",
    http_status=302,
)

# Disable a redirect without deleting it
redirect = Redirect.query.get(from_pattern="/old-page/")
redirect.enabled = False
redirect.update()

When multiple redirects could match, you can control priority with the order field. Lower values are checked first.

# This redirect is checked first
Redirect.query.create(
    from_pattern="/blog/featured/",
    to_pattern="/featured-posts/",
    order=10,
)

# This more general pattern is checked later
Redirect.query.create(
    from_pattern="/blog/",
    to_pattern="/articles/",
    order=20,
)

Regex patterns

For dynamic URL patterns, set is_regex=True and use regex groups in your patterns.

# Redirect /blog/2024/01/my-post/ to /posts/2024-01-my-post/
Redirect.query.create(
    from_pattern=r"^/blog/(\d{4})/(\d{2})/(.+)/$",
    to_pattern=r"/posts/\1-\2-\3/",
    is_regex=True,
)

The to_pattern can reference captured groups from the from_pattern using \1, \2, etc.

Full URL matching

By default, patterns match against the request path (e.g., /old-page/). If your pattern starts with http, it matches against the full URL including the domain and query string.

# Redirect requests from a specific domain
Redirect.query.create(
    from_pattern="https://old-domain.com/page/",
    to_pattern="/page/",
)

Logs

Every redirect is recorded in RedirectLog, capturing the original URL, destination, and request metadata like IP address, user agent, and referrer.

from plain.redirection.models import RedirectLog

# Recent redirects
recent = RedirectLog.query.all()[:10]

for log in recent:
    print(f"{log.from_url} -> {log.to_url} ({log.ip_address})")

Requests that result in 404s (and don't match any redirect) are logged in NotFoundLog.

from plain.redirection.models import NotFoundLog

# Find 404s from a specific referrer
broken_links = NotFoundLog.query.filter(referrer__contains="external-site.com")

Automatic cleanup

Logs are automatically cleaned up by the DeleteLogs chore. See Settings for configuring retention.

Admin interface

The package includes admin views for managing redirects and viewing logs. Once installed, you will find three sections under "Redirection" in your admin:

  • Redirects - Create, edit, and delete redirect rules
  • Redirect logs - View successful redirects with request details
  • 404 logs - Monitor URLs that resulted in 404s

The 404 logs are useful for discovering broken links on your site. You can search the logs to find patterns and create redirects to fix them.

Settings

Setting Default Env var
REDIRECTION_LOG_RETENTION_TIMEDELTA timedelta(days=30) -

See default_settings.py for more details.

FAQs

How does the middleware decide when to redirect?

The middleware only checks for redirects when a request results in a 404. It iterates through enabled redirects (ordered by the order field, then by creation date) and returns the first match. If no redirect matches, the 404 is logged and the original response is returned.

Can I redirect to external URLs?

Yes. The to_pattern can be any URL, including external sites:

Redirect.query.create(
    from_pattern="/partner/",
    to_pattern="https://partner-site.com/landing/",
)

What HTTP status codes can I use?

Any valid HTTP redirect status code works. The most common are:

  • 301 - Permanent redirect (default). Search engines update their index.
  • 302 - Temporary redirect. Search engines keep the original URL.
  • 307 - Temporary redirect that preserves the request method.
  • 308 - Permanent redirect that preserves the request method.

Installation

Install the plain.redirection package from PyPI:

uv add plain.redirection

Add plain.redirection to your INSTALLED_PACKAGES in app/settings.py:

# app/settings.py
INSTALLED_PACKAGES = [
    # ...other packages
    "plain.redirection",
]

Add the middleware to your MIDDLEWARE setting. It should be near the end so other middleware can process requests first:

# app/settings.py
MIDDLEWARE = [
    # ...other middleware
    "plain.redirection.RedirectionMiddleware",
]

Sync the database to create the tables:

plain postgres sync

You can now create redirects through the admin interface or programmatically using the Redirect model.

Download files

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

Source Distribution

plain_redirection-0.36.2.tar.gz (14.4 kB view details)

Uploaded Source

Built Distribution

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

plain_redirection-0.36.2-py3-none-any.whl (17.3 kB view details)

Uploaded Python 3

File details

Details for the file plain_redirection-0.36.2.tar.gz.

File metadata

  • Download URL: plain_redirection-0.36.2.tar.gz
  • Upload date:
  • Size: 14.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for plain_redirection-0.36.2.tar.gz
Algorithm Hash digest
SHA256 e576155f03cc972045498b18e0d495ffe3ec4e2adef8b6d3e50e236c4f90b09d
MD5 9174d62296a3a917e4bca53bf406bb13
BLAKE2b-256 87d9110c54d9e28bcba1e76cd8415112615f1c727abe684a8320518b931d845d

See more details on using hashes here.

File details

Details for the file plain_redirection-0.36.2-py3-none-any.whl.

File metadata

  • Download URL: plain_redirection-0.36.2-py3-none-any.whl
  • Upload date:
  • Size: 17.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for plain_redirection-0.36.2-py3-none-any.whl
Algorithm Hash digest
SHA256 ea30b564fa18f16bd91f3e5fea8d7b5cd3fa61bde1e4bfedd4192b5b83ad900a
MD5 b3b5f1580a286d357feaf78dc72c1d7e
BLAKE2b-256 40cac44f76a6b9668fabf83066ff7d1f3c6bed84ddbd98834698ca2acc017fef

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.36.2 This release

2 files

0.36.1

2 files

0.36.0

2 files

0.35.7

2 files

0.35.6

2 files

0.35.5

2 files

0.35.4

2 files

0.35.3

2 files

0.35.2

2 files

0.35.1

2 files

0.35.0

2 files

0.34.0

2 files

0.33.1

2 files

0.33.0

2 files

0.32.1

2 files

0.32.0

2 files

0.31.5

2 files

0.31.4

2 files

0.31.3

2 files

0.31.2

2 files

0.31.1

2 files

0.31.0

2 files

0.30.0

2 files

0.29.0

2 files

0.28.0

2 files

0.27.0

2 files

0.26.1

2 files

0.26.0

2 files

0.25.0

2 files

0.24.1

2 files

0.24.0

2 files

0.23.0

2 files

0.22.0

2 files

0.21.1

2 files

0.21.0

2 files

0.20.1

2 files

0.20.0

2 files

0.19.0

2 files

0.18.0

2 files

0.17.0

2 files

0.16.2

2 files

0.16.1

2 files

0.16.0

2 files

0.15.0

2 files

0.14.0

2 files

0.13.0

2 files

0.12.0

2 files

0.11.0

2 files

0.10.0

2 files

0.9.0

2 files

0.8.0

2 files

0.7.0

2 files

0.6.0

2 files

0.5.0

2 files

0.4.1

2 files

0.4.0

2 files

0.3.2

2 files

0.3.1

2 files

0.3.0

2 files

0.2.4

2 files

0.2.3

2 files

0.2.2

2 files

0.2.1

2 files

0.2.0

2 files

0.1.0

2 files

Supported by

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