Skip to main content

Author management module for Bazis framework.

Project description

Bazis Author

PyPI version Python Versions License

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) and author_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 via CTX_USER_REQUEST
  • Automatically populates author when creating record
  • Automatically updates author_updated on 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 author and author_updated fields from CREATE and UPDATE schemas (they are populated automatically)
  • Uses hook_before_create and hook_before_update hooks to set author
  • Sets author only for authenticated users

Methods:

  • hook_before_create(item) — sets author before creating record
  • hook_before_update(item) — sets author_updated before 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__username to search fields
  • Makes author and author_updated fields 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 → author and author_updated remain null
  • Authenticated user creates record → author is automatically populated
  • Any user updates record → author_updated is 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 → author is automatically populated
  • Authenticated user updates record → author_updated is 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:

  1. Autocomplete filter by author in list view
  2. Search by author username (author__username)
  3. Readonly fields author and author_updated (cannot be edited)
  4. Automatic population when saving through admin

How It Works

In Models

When calling save() on a model with AuthorMixin:

  1. Check for user presence in CTX_USER_REQUEST context
  2. If user exists:
    • When creating record (author is empty) → set author
    • When updating record → update author_updated
  3. 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:

  1. In CREATE: hook_before_create(item) is triggered

    • If user is NOT anonymous → set item.author
  2. In UPDATE: hook_before_update(item) is triggered

    • If user is NOT anonymous → set item.author_updated
  3. Fields author and author_updated are 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:

  1. save_model() — sets author from request.user
  2. save_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

Support

If you have questions or issues:


Made with ❤️ by the Bazis team

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

bazis_author-2.2.0.tar.gz (73.7 kB view details)

Uploaded Source

Built Distribution

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

bazis_author-2.2.0-py3-none-any.whl (12.4 kB view details)

Uploaded Python 3

File details

Details for the file bazis_author-2.2.0.tar.gz.

File metadata

  • Download URL: bazis_author-2.2.0.tar.gz
  • Upload date:
  • Size: 73.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","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 bazis_author-2.2.0.tar.gz
Algorithm Hash digest
SHA256 c7c65896c874a3f0060da27c51d55876b84b06888fb7e4dc10c3a0914fb430e8
MD5 f32e00abaa1d6c0427f372928537172c
BLAKE2b-256 595f4c4c1df90198154435aefbd2358eec87bc09552f7346069e8b7ed6fa0f36

See more details on using hashes here.

File details

Details for the file bazis_author-2.2.0-py3-none-any.whl.

File metadata

  • Download URL: bazis_author-2.2.0-py3-none-any.whl
  • Upload date:
  • Size: 12.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","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 bazis_author-2.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8a2496564ab12eb2531800df008579f4d1fe52eaaff62a4e2fe4c45177a58048
MD5 59040c6f4dbeeec691c821bbfec867a3
BLAKE2b-256 a8ed0e1c9912826c9257c875e35f646721884e60a78632c1489343024d852af3

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