Author management module for Bazis framework.
Project description
Bazis Author
An extension package for Bazis that provides automatic authorship tracking for models (who created and who updated records).
Quick Start
# Install the package
uv add bazis-author
# Create model with authorship tracking
from bazis.contrib.author.models_abstract import AuthorMixin
from bazis.core.models_abstract import DtMixin, UuidMixin, JsonApiMixin
from django.db import models
class Article(AuthorMixin, DtMixin, UuidMixin, JsonApiMixin):
"""Article with author tracking"""
title = models.CharField(max_length=255)
content = models.TextField()
class Meta:
verbose_name = 'Article'
verbose_name_plural = 'Articles'
# Create route with automatic author population
from bazis.contrib.author.routes_abstract import AuthorRouteBase
from django.apps import apps
class ArticleRouteSet(AuthorRouteBase):
model = apps.get_model('myapp.Article')
Table of Contents
Description
Bazis Author is an extension package for the Bazis framework that provides automatic authorship tracking for records. The package includes:
- AuthorMixin — mixin for models adding
author(who created) andauthor_updated(who updated) fields - AuthorRouteBase and AuthorRequiredRouteBase — base route classes with automatic author field population
- AuthorAdminMixin — admin mixin with convenient filters and readonly fields
The main advantage of the package is fully automatic author field population without the need to write additional code.
This package requires bazis and bazis-users packages to be installed.
Requirements
- Python: 3.12+
- bazis: latest version
- bazis-users: latest version
- PostgreSQL: 12+
- Redis: For caching
Installation
Using uv (recommended)
uv add bazis-author
Using pip
pip install bazis-author
Core Components
Model Mixins
AuthorMixin
Mixin for models adding authorship tracking fields.
Location: bazis.contrib.author.models_abstract.AuthorMixin
Added fields:
author— Foreign Key to user who created the record (nullable)author_updated— Foreign Key to user who last updated the record (nullable)
Features:
- Inherits from
UserMixin, has access to user context viaCTX_USER_REQUEST - Automatically populates
authorwhen creating record - Automatically updates
author_updatedon every save - Fields are optional (nullable), so records can be created without a user
Example usage:
from bazis.contrib.author.models_abstract import AuthorMixin
from bazis.core.models_abstract import DtMixin, UuidMixin, JsonApiMixin
from django.db import models
class Document(AuthorMixin, DtMixin, UuidMixin, JsonApiMixin):
"""Document with authorship tracking"""
title = models.CharField('Title', max_length=255)
content = models.TextField('Content')
class Meta:
verbose_name = 'Document'
verbose_name_plural = 'Documents'
Base Routes
AuthorRouteMixin
Base mixin for routes that automatically populate author fields.
Location: bazis.contrib.author.routes_abstract.AuthorRouteMixin
Features:
- Excludes
authorandauthor_updatedfields from CREATE and UPDATE schemas (they are populated automatically) - Uses
hook_before_createandhook_before_updatehooks to set author - Sets author only for authenticated users
Methods:
hook_before_create(item)— setsauthorbefore creating recordhook_before_update(item)— setsauthor_updatedbefore updating record
AuthorRouteBase
Base route class with authorship support for optional authentication.
Location: bazis.contrib.author.routes_abstract.AuthorRouteBase
Inheritance: AuthorRouteMixin + UserRouteBase
Use this class when endpoints are accessible to both authenticated and anonymous users.
Example usage:
from bazis.contrib.author.routes_abstract import AuthorRouteBase
from django.apps import apps
class ArticleRouteSet(AuthorRouteBase):
"""Route for articles with automatic authorship"""
model = apps.get_model('myapp.Article')
AuthorRequiredRouteBase
Base route class with authorship support for required authentication.
Location: bazis.contrib.author.routes_abstract.AuthorRequiredRouteBase
Inheritance: AuthorRouteMixin + UserRequiredRouteBase
Use this class when endpoints require mandatory authentication (return 401 for unauthenticated requests).
Example usage:
from bazis.contrib.author.routes_abstract import AuthorRequiredRouteBase
from django.apps import apps
class PrivateDocumentRouteSet(AuthorRequiredRouteBase):
"""Route for private documents (authenticated only)"""
model = apps.get_model('myapp.PrivateDocument')
Admin Mixins
AuthorAdminMixin
Mixin for Django admin classes adding convenient work with author fields.
Location: bazis.contrib.author.admin_abstract.AuthorAdminMixin
Capabilities:
- Adds
author__usernameto search fields - Makes
authorandauthor_updatedfields readonly - Adds autocomplete filter by author to list view
- Automatically populates author fields when saving through admin
Example usage:
from django.contrib import admin
from bazis.contrib.author.admin_abstract import AuthorAdminMixin
from bazis.core.admin_abstract import DtAdminMixin
from .models import Article
@admin.register(Article)
class ArticleAdmin(AuthorAdminMixin, DtAdminMixin, admin.ModelAdmin):
list_display = ('id', 'title', 'author', 'author_updated', 'dt_created')
search_fields = ('title', 'content')
Usage
Creating Model with Authorship
from bazis.contrib.author.models_abstract import AuthorMixin
from bazis.core.models_abstract import DtMixin, UuidMixin, JsonApiMixin
from django.db import models
class Article(AuthorMixin, DtMixin, UuidMixin, JsonApiMixin):
"""Article with automatic author tracking"""
title = models.CharField('Title', max_length=255)
content = models.TextField('Content')
is_published = models.BooleanField('Published', default=False)
class Meta:
verbose_name = 'Article'
verbose_name_plural = 'Articles'
def __str__(self):
return self.title
Creating Route with Authorship
For public endpoints (optional authentication)
from bazis.contrib.author.routes_abstract import AuthorRouteBase
from django.apps import apps
class ArticleRouteSet(AuthorRouteBase):
"""
Articles can be created by anonymous users.
If user is authenticated - author fields will be populated automatically.
"""
model = apps.get_model('myapp.Article')
Behavior:
- Anonymous user creates record →
authorandauthor_updatedremainnull - Authenticated user creates record →
authoris automatically populated - Any user updates record →
author_updatedis populated if user is authenticated
For private endpoints (required authentication)
from bazis.contrib.author.routes_abstract import AuthorRequiredRouteBase
from django.apps import apps
class PrivateDocumentRouteSet(AuthorRequiredRouteBase):
"""
Documents are accessible only to authenticated users.
Anonymous users will get 401.
"""
model = apps.get_model('myapp.PrivateDocument')
Behavior:
- Anonymous user tries to create/update record → 401 Unauthorized
- Authenticated user creates record →
authoris automatically populated - Authenticated user updates record →
author_updatedis automatically updated
Admin Setup
from django.contrib import admin
from bazis.contrib.author.admin_abstract import AuthorAdminMixin
from bazis.core.admin_abstract import DtAdminMixin
from .models import Article
@admin.register(Article)
class ArticleAdmin(AuthorAdminMixin, DtAdminMixin, admin.ModelAdmin):
list_display = ('id', 'title', 'author', 'author_updated', 'is_published', 'dt_created')
search_fields = ('title', 'content')
list_filter = ('is_published',)
readonly_fields = ('dt_created', 'dt_updated')
What AuthorAdminMixin provides:
- Autocomplete filter by author in list view
- Search by author username (
author__username) - Readonly fields
authorandauthor_updated(cannot be edited) - Automatic population when saving through admin
How It Works
In Models
When calling save() on a model with AuthorMixin:
- Check for user presence in
CTX_USER_REQUESTcontext - If user exists:
- When creating record (
authoris empty) → setauthor - When updating record → update
author_updated
- When creating record (
- Call
super().save()to save
def save(self, *args, **kwargs):
if author := self.CTX_USER_REQUEST.get():
if not self.author:
self.author = author
self.author_updated = author
super().save(*args, **kwargs)
In Routes
When creating/updating record via API:
-
In CREATE:
hook_before_create(item)is triggered- If user is NOT anonymous → set
item.author
- If user is NOT anonymous → set
-
In UPDATE:
hook_before_update(item)is triggered- If user is NOT anonymous → set
item.author_updated
- If user is NOT anonymous → set
-
Fields
authorandauthor_updatedare excluded from CREATE/UPDATE schemas, so client cannot pass them
fields: dict[ApiAction, SchemaFields] = {
CrudApiAction.CREATE: SchemaFields(
exclude={'author': None, 'author_updated': None},
),
CrudApiAction.UPDATE: SchemaFields(
exclude={'author': None, 'author_updated': None},
),
}
In Admin
When saving through Django Admin:
save_model()— sets author fromrequest.usersave_formset()— sets author for inline models
Examples
Complete Application Example
models.py:
from bazis.contrib.author.models_abstract import AuthorMixin
from bazis.core.models_abstract import DtMixin, UuidMixin, JsonApiMixin
from django.db import models
class Article(AuthorMixin, DtMixin, UuidMixin, JsonApiMixin):
"""Public article"""
title = models.CharField('Title', max_length=255)
content = models.TextField('Content')
is_published = models.BooleanField('Published', default=False)
class Meta:
verbose_name = 'Article'
verbose_name_plural = 'Articles'
class PrivateNote(AuthorMixin, DtMixin, UuidMixin, JsonApiMixin):
"""Private note (authenticated only)"""
title = models.CharField('Title', max_length=255)
content = models.TextField('Content')
class Meta:
verbose_name = 'Note'
verbose_name_plural = 'Notes'
routes.py:
from bazis.contrib.author.routes_abstract import AuthorRouteBase, AuthorRequiredRouteBase
from django.apps import apps
class ArticleRouteSet(AuthorRouteBase):
"""Articles accessible to everyone, but author is tracked if user is authenticated"""
model = apps.get_model('myapp.Article')
class PrivateNoteRouteSet(AuthorRequiredRouteBase):
"""Notes accessible only to authenticated users"""
model = apps.get_model('myapp.PrivateNote')
router.py:
from bazis.core.routing import BazisRouter
from . import routes
router = BazisRouter(tags=['Content'])
router.register(routes.ArticleRouteSet.as_router())
router.register(routes.PrivateNoteRouteSet.as_router())
admin.py:
from django.contrib import admin
from bazis.contrib.author.admin_abstract import AuthorAdminMixin
from bazis.core.admin_abstract import DtAdminMixin
from .models import Article, PrivateNote
@admin.register(Article)
class ArticleAdmin(AuthorAdminMixin, DtAdminMixin, admin.ModelAdmin):
list_display = ('id', 'title', 'author', 'is_published', 'dt_created')
search_fields = ('title', 'content')
list_filter = ('is_published',)
@admin.register(PrivateNote)
class PrivateNoteAdmin(AuthorAdminMixin, DtAdminMixin, admin.ModelAdmin):
list_display = ('id', 'title', 'author', 'dt_created')
search_fields = ('title', 'content')
API Usage Examples
Creating article by anonymous user:
POST /api/v1/content/article/
Content-Type: application/json
{
"data": {
"type": "myapp.article",
"attributes": {
"title": "My Article",
"content": "Article content",
"is_published": true
}
}
}
# Response: 201 Created
# author and author_updated will be null
Creating article by authenticated user:
POST /api/v1/content/article/
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"data": {
"type": "myapp.article",
"attributes": {
"title": "My Article",
"content": "Article content",
"is_published": true
}
}
}
# Response: 201 Created
# author is automatically set to current user
Attempting to create private note without authentication:
POST /api/v1/content/private_note/
Content-Type: application/json
{
"data": {
"type": "myapp.privatenote",
"attributes": {
"title": "Secret Note",
"content": "Secret content"
}
}
}
# Response: 401 Unauthorized
Creating private note with authentication:
POST /api/v1/content/private_note/
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"data": {
"type": "myapp.privatenote",
"attributes": {
"title": "Secret Note",
"content": "Secret content"
}
}
}
# Response: 201 Created
# author is automatically set to current user
Authorship Check in Responses
Fields author and author_updated are returned in the relationships block:
{
"data": {
"type": "myapp.article",
"id": "123e4567-e89b-12d3-a456-426614174000",
"attributes": {
"title": "My Article",
"content": "Article content",
"is_published": true
},
"relationships": {
"author": {
"data": {
"type": "users.user",
"id": "456e7890-e89b-12d3-a456-426614174000"
}
},
"author_updated": {
"data": {
"type": "users.user",
"id": "456e7890-e89b-12d3-a456-426614174000"
}
}
}
}
}
License
Apache License 2.0
See LICENSE file for details.
Links
- Bazis Documentation — main repository
- Bazis Users — user management package
- Bazis Author Repository — package repository
- Issue Tracker — report bugs or request features
Support
If you have questions or issues:
- Check the Bazis documentation
- Search existing issues
- Create a new issue with detailed information
Made with ❤️ by the Bazis team
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