Skip to main content

Opinionated collection of Django and DRF tools that came in handy time and again.

Project description

build-status pypi-version

… wait, why isn’t this part of Django already?

A focused collection of Django and Django REST framework utilities that solve real, recurring problems — the kind you keep re-inventing or copy&pasting in every new project.

$ pip install django-seriously

What’s in the box

Features

PydanticJSONField / ValidatedJSONField

Django’s JSONField is a black box — anything goes in, no guarantees come out. These fields bring schema validation and automatic deserialization using Pydantic v2.

  • PydanticJSONField — validates and deserializes to a typed Pydantic model instance

  • ValidatedJSONField — validates structure but keeps the raw Python object

from pydantic import BaseModel
from django_seriously.pydantic.fields import PydanticJSONField

class Address(BaseModel):
    street: str
    city: str
    zip_code: str

class Contact(models.Model):
    address = PydanticJSONField(structure=Address)

# Validated and deserialized automatically — no manual parsing needed
contact = Contact.objects.get(pk=pk)
print(contact.address.city)  # 'Berlin'

Works seamlessly in:

  • Django admin (pretty-printed JSON editor)

  • DRF serializers (auto-detected)

  • Form validation

  • Seamless integration with drf-spectacular. OpenAPI3-compliant schemas for those validated JSON fields.

TokenAuthentication

When dealing with OAuth2 is overkill, DRF’s built-in token is just horrible, and django-rest-knox just doesn’t fit your permissioning model.

Security:

  • Tokens are never stored in plain text — only a PBKDF2 hash is saved

  • Bearer token format: base64(uuid + random_secret) — UUID for fast DB lookup, secret for verification

  • Optional: Scopes

Simple usage (just authentication, no scopes):

from django_seriously.authtoken.authentication import TokenAuthentication

class ReportsViewSet(viewsets.ModelViewSet):
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticated]

With scope-based permissions:

# settings.py
SERIOUSLY_SETTINGS = {
    "AUTH_TOKEN_SCOPES": ["read", "write", "admin"]
}

# views.py
from django_seriously.authtoken.authentication import TokenAuthentication, TokenHasScope

class ReportsViewSet(viewsets.ModelViewSet):
    authentication_classes = [TokenAuthentication]
    permission_classes = [TokenHasScope]
    required_scopes = ['read']

Admin integration: tokens are shown once at creation (copy/paste), then stored only as hashes — just like a good secret manager.

BaseModel

The abstract base model you define in every new project, done properly.

  • UUID primary key (uuid4) — no sequential ID leakage

  • Automatic timestamps — immutable created_at, auto-updating updated_at

  • Validation always runs — overrides save() to call full_clean() every time, not just through forms/admin

That last point matters: Django only runs model validation in forms and the admin by default. Direct .save() calls silently skip your clean() logic. BaseModel closes that gap.

from django_seriously.utils.models import BaseModel

class Article(BaseModel):
    title = models.CharField(max_length=200)
    # uuid pk, created_at, updated_at included
    # full_clean() called automatically on every save()

MinimalUser

Django’s AbstractUser ships with username, first_name, last_name, and other fields that most modern apps don’t need. MinimalAbstractUser strips that down to what you actually use.

  • Email-based authentication (no username field)

  • Supports passwordless user creation (magic links, SSO)

  • Drop-in MinimalUserAdmin included

# models.py
from django_seriously.minimaluser.models import MinimalAbstractUser
from django_seriously.utils.models import BaseModel

class User(BaseModel, MinimalAbstractUser):
    # Add your fields here
    pass

# admin.py
from django_seriously.minimaluser.admin import MinimalUserAdmin

@admin.register(User)
class UserAdmin(MinimalUserAdmin):
    pass

AdminItemAction

Django admin’s built-in actions apply to a selected batch of rows. AdminItemAction puts a context-aware action button directly on each row — and only shows it when the action makes sense for that item.

# admin.py
from django_seriously.utils.admin import AdminItemAction

class UserAdminAction(AdminItemAction[User]):
    model_cls = User
    actions = [("reset_invitation", "Reset Invitation")]

    @classmethod
    def is_actionable(cls, obj: User, action: str) -> bool:
        if action == "reset_invitation":
            return obj.invitation_pending
        return False

    def perform_action(self, obj: User, action: str) -> None:
        if action == "reset_invitation":
            send_invitation(obj)  # your code

@admin.register(User)
class UserAdmin(ModelAdmin):
    list_display = (..., "admin_actions")

    def admin_actions(self, obj: User):
        return UserAdminAction.action_markup(obj)
# urls.py — item actions must precede regular admin endpoints
urlpatterns = [
    path("admin/", AdminItemAction.urls()),
    path("admin/", admin.site.urls),
]

Demo

AdminItemAction, admin_navigation_link, MinimalUser, and TokenAuthentication in action:

https://github.com/tfranzel/django-seriously/blob/master/docs/demo.gif

Requirements

  • Python >= 3.12

  • Django >= 4.2

  • Pydantic >= 2.0

  • Django REST Framework (optional)

License

Provided by T. Franzel, Licensed under 3-Clause BSD.

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_seriously-0.5.0.tar.gz (15.2 kB view details)

Uploaded Source

Built Distribution

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

django_seriously-0.5.0-py3-none-any.whl (23.5 kB view details)

Uploaded Python 3

File details

Details for the file django_seriously-0.5.0.tar.gz.

File metadata

  • Download URL: django_seriously-0.5.0.tar.gz
  • Upload date:
  • Size: 15.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.10 {"installer":{"name":"uv","version":"0.10.10","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 django_seriously-0.5.0.tar.gz
Algorithm Hash digest
SHA256 a4c972a18f3c062613e03696324697e822b642644160a4248efef47571879fb7
MD5 258391c7dd6aa2e6c929be0d1b2cb53d
BLAKE2b-256 b3cd582269584bad52711f99380419fbba7cc460fdd6dddce883a2a0f8035dc6

See more details on using hashes here.

File details

Details for the file django_seriously-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: django_seriously-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 23.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.10 {"installer":{"name":"uv","version":"0.10.10","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 django_seriously-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 43629187adf9eed6e5071ae8822922f2e178ad364c3c3047f701463aaea4c479
MD5 4a548017014b255625c96641b2e73acc
BLAKE2b-256 daf8df8b0b4d6be19d499e97def76b4c14e17326005c1db1bb90233ecb71d654

See more details on using hashes here.

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