Skip to main content

Full-featured dynamic QR code package for Django — 14 types, analytics, access control, REST API

Project description

django-dynamic-qr

A full-featured, self-hosted dynamic QR code package for Django — a QRFY-equivalent you own completely.

14 QR types · short-URL redirects · per-scan analytics · password/schedule/geo access control · bulk CSV/ZIP · REST API · Django admin with live previews.

Features

  • 14 QR types: URL, vCard, vCard+ (rich landing page), PDF, Restaurant Menu, Social Links hub, WiFi, Email, SMS, WhatsApp, Image Gallery, Plain Text, Link List, App Store (smart iOS/Android redirect)
  • Dynamic short URLs — edit destination after printing, no reprint needed
  • Design engine — custom colors, logo overlay, frame styles, 4 error-correction levels
  • Export formats — PNG, SVG, PDF, EPS
  • Analytics — scan count, device/OS/browser, country/city geolocation, daily trend, CSV/XLSX export
  • Access control — password protection, daily scan limits, scheduled activation/expiry, country allow-list
  • Bulk operations — CSV import, ZIP export of multiple QR images
  • REST API — full DRF viewsets with token auth (optional)
  • Django admin — inline scan logs, live QR thumbnail preview, bulk actions
  • Async-ready — optional Celery task for non-blocking scan logging

Installation

pip install django-dynamic-qr[api]
# or, from local source:
pip install -e /path/to/django-dynamic-qr

1. Add to INSTALLED_APPS

INSTALLED_APPS = [
    ...
    "django_dynamic_qr",
    "rest_framework",   # only if using the REST API
]

2. Include URLs

# project/urls.py
from django.urls import path, include

urlpatterns = [
    ...
    path("", include("django_dynamic_qr.urls")),
]

This mounts:

  • /qr/<slug>/ — the redirect / landing-page endpoint (this is what your QR images encode)
  • /qr/<slug>/export/ — download the QR image
  • /api/qr/... — REST API (if DRF installed)

3. Configure settings (all optional — sane defaults provided)

DJANGO_DYNAMIC_QR = {
    "SHORT_URL_BASE": "https://yourdomain.com",   # IMPORTANT: set this in production
    "DEFAULT_ERROR_CORRECTION": "H",
    "DEFAULT_BOX_SIZE": 10,
    "DEFAULT_BORDER": 4,
    "LOGO_MAX_RATIO": 0.25,
    "SCAN_LOG_ASYNC": False,        # True if Celery is configured
    "GEO_BACKEND": "ip-api",        # "ip-api" | "maxmind" | None
    "MAXMIND_DB_PATH": None,
    "ENABLE_REST_API": True,
    "UPLOAD_TO": "qr_codes/",
    "LOGO_UPLOAD_TO": "qr_logos/",
}

4. Migrate

python manage.py migrate django_dynamic_qr

5. Make sure MEDIA_URL / MEDIA_ROOT are configured (for logo uploads) and your urls.py serves media in development.

Usage

Create a QR code programmatically

from django_dynamic_qr.models import QRCode

qr = QRCode.objects.create(
    user=request.user,
    name="My Website",
    qr_type="url",
    type_data={"url": "https://example.com"},
    fg_color="#111111",
    bg_color="#FFFFFF",
)

print(qr.get_redirect_url())   # https://yourdomain.com/qr/AbC123Xy/

Export the QR image

from django_dynamic_qr.engine.exporter import export_qr_code

png_bytes = export_qr_code(qr, fmt="png")
svg_bytes = export_qr_code(qr, fmt="svg")

Or via the built-in view: GET /qr/<slug>/export/?fmt=svg

Update the destination — no reprint needed

qr.type_data = {"url": "https://example.com/new-page"}
qr.save()
# The printed QR code, slug, and image stay identical.
# Anyone scanning it is now redirected to the new URL.

Add access control

from django.utils import timezone
import datetime

qr.password = "secret123"
qr.scan_limit_day = 500
qr.active_from = timezone.now()
qr.active_until = timezone.now() + datetime.timedelta(days=30)
qr.allowed_countries = ["IN", "US", "GB"]
qr.save()

Bulk import from CSV

CSV format:

name,qr_type,data
My Website,url,"{""url"": ""https://example.com""}"
Office WiFi,wifi,"{""ssid"": ""OfficeNet"", ""password"": ""pass123"", ""security"": ""WPA""}"
from django_dynamic_qr.bulk import import_from_csv

created, errors = import_from_csv(csv_file, user=request.user)

Management command

python manage.py generate_qr <slug> --fmt png --output qr.png

REST API (optional, requires djangorestframework)

GET    /api/qr/                          list QR codes
POST   /api/qr/                          create
GET    /api/qr/<id>/                     retrieve
PATCH  /api/qr/<id>/                     update destination (dynamic!)
DELETE /api/qr/<id>/
GET    /api/qr/<id>/export/?fmt=png      download image
GET    /api/qr/<id>/analytics/           scan stats summary
GET    /api/qr/<id>/analytics/export/    download scan logs CSV/XLSX
POST   /api/qr/bulk-import/              CSV bulk create
GET    /api/qr/bulk-export/?fmt=svg      ZIP export

Type payload reference

Type type_data keys
url url
vcard first_name, last_name, phone, email, company, title, address, website
vcard_plus same as vcard + photo_url, bio, socials: {platform: url}
pdf pdf_url, title
menu restaurant_name, logo_url, categories: [{name, items: [{name, description, price, image_url}]}]
social title, bio, avatar_url, links: [{platform, url, label}]
wifi ssid, password, security, hidden
email to, subject, body
sms phone, message
whatsapp phone, message
image title, images: [{url, caption}]
text text
link_list title, description, links: [{label, url, icon}]
app_store ios_url, android_url, fallback_url

Extending with a custom QR type

# myapp/qr_types.py
from django_dynamic_qr.types.base import BaseQRType
from django_dynamic_qr.types import registry

class CalendarEventType(BaseQRType):
    type_id = "calendar_event"
    is_static = True

    def get_destination(self):
        d = self.data
        return f"BEGIN:VEVENT\nSUMMARY:{d['title']}\nDTSTART:{d['start']}\nEND:VEVENT"

registry["calendar_event"] = CalendarEventType

Import this module in your app's ready() to register it at startup.

License

MIT

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_dynamic_qr-1.0.0.tar.gz (32.4 kB view details)

Uploaded Source

Built Distribution

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

django_dynamic_qr-1.0.0-py3-none-any.whl (48.5 kB view details)

Uploaded Python 3

File details

Details for the file django_dynamic_qr-1.0.0.tar.gz.

File metadata

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

File hashes

Hashes for django_dynamic_qr-1.0.0.tar.gz
Algorithm Hash digest
SHA256 d797c2bb9643aa0550a543e7454ac64b55e3530523ce2825c7e9a6923b933d2f
MD5 89f42f2d670c640c7d3422ea1113c60b
BLAKE2b-256 e6ac67ff595d7bc628559988ec16ab58d2c39ff9f8447139999b41290d6e83ef

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_dynamic_qr-1.0.0.tar.gz:

Publisher: release.yml on rahul-baberwal/django-dynamic-qr

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_dynamic_qr-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for django_dynamic_qr-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 89bf69b618064f58be1dbfcec504c464e75a97a1ab04cd3e5693d3fb43947dbd
MD5 480e3267b96ff200c791668631a41764
BLAKE2b-256 67e4e1a36b6df6fa7527e1a740de289d7b07593299a4e31bc98424bfcf7c81d8

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_dynamic_qr-1.0.0-py3-none-any.whl:

Publisher: release.yml on rahul-baberwal/django-dynamic-qr

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