django-seriously
… 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
PydanticJSONField — typed, validated JSON fields backed by Pydantic
TokenAuthentication — secure, scoped API tokens without the OAuth2 overhead
DjangoBaseModel — UUID PKs, timestamps, and guaranteed validation on every save
MinimalUser — email-only user model without the cruft
AdminItemAction — per-row action buttons in Django admin list views
admin_navigation_link — clickable links between related models in admin
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
Installation:
Basic: Just add “django_seriously.authtoken” to INSTALLED_APPS
Extend: Subclass and extend django_seriously.authtoken.model.Token, as well as django_seriously.authtoken.admin.TokenAdmin.
Simple usage (just authentication, no scopes):
from rest_framework.permissions import IsAuthenticated
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.
DjangoBaseModel
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. DjangoBaseModel closes that gap.
from django_seriously.utils.models import DjangoBaseModel
class Article(DjangoBaseModel):
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 DjangoBaseModel
class User(DjangoBaseModel, MinimalAbstractUser):
# Add your fields here
pass
# admin.py
from django_seriously.minimaluser.admin import MinimalUserAdmin
# settings.py
AUTH_USER_MODEL = "REPLACEYOURAPP.User"
@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:
Requirements
Python >= 3.12
Django >= 4.2
Pydantic >= 2.0
Django REST Framework (optional)
License
Provided by T. Franzel, Licensed under 3-Clause BSD.
Metadata
Release files for django-seriously 0.5.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| django_seriously-0.5.1.tar.gz | 15.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_seriously-0.5.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 39.2 kB
Release files / django_seriously-0.5.1.tar.gz
| Download URL | django_seriously-0.5.1.tar.gz |
|---|---|
| Size | 15.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f1af69033d648c0deb81ac447eafc931ca0484fa7730fee9d26d7b1b6ffdd178
|
|
BLAKE2b-256 checksum How to use checksums |
4dee0ed9487c9cef396f0b48deba3c90f1348faa32fff5c5dababc99798e0b5b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.27 {"installer":{"name":"uv","version":"0.11.27","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}
|
Release files / django_seriously-0.5.1-py3-none-any.whl
| Download URL | django_seriously-0.5.1-py3-none-any.whl |
|---|---|
| Size | 23.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e730b6c11b9d3dd3ae77164c716a35aeec22251da133c85035c3ca3c80555b20
|
|
BLAKE2b-256 checksum How to use checksums |
8fcf77e23aefda703f2b47971ffa36063802433cb6909651a60e5314202b9e74
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.27 {"installer":{"name":"uv","version":"0.11.27","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}
|