bazis-author 2.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. bazis_author-2.2.0/.dockerignore +3 -0
  2. bazis_author-2.2.0/.github/workflows/build-publish.yml +16 -0
  3. bazis_author-2.2.0/.github/workflows/update-licenses.yml +18 -0
  4. bazis_author-2.2.0/.gitignore +44 -0
  5. bazis_author-2.2.0/PKG-INFO +589 -0
  6. bazis_author-2.2.0/README.md +563 -0
  7. bazis_author-2.2.0/bazis/contrib/author/__init__.py +19 -0
  8. bazis_author-2.2.0/bazis/contrib/author/_version.py +34 -0
  9. bazis_author-2.2.0/bazis/contrib/author/admin_abstract.py +53 -0
  10. bazis_author-2.2.0/bazis/contrib/author/apps.py +22 -0
  11. bazis_author-2.2.0/bazis/contrib/author/locale/en/LC_MESSAGES/django.mo +0 -0
  12. bazis_author-2.2.0/bazis/contrib/author/locale/en/LC_MESSAGES/django.po +22 -0
  13. bazis_author-2.2.0/bazis/contrib/author/locale/ru/LC_MESSAGES/django.mo +0 -0
  14. bazis_author-2.2.0/bazis/contrib/author/locale/ru/LC_MESSAGES/django.po +24 -0
  15. bazis_author-2.2.0/bazis/contrib/author/models_abstract.py +49 -0
  16. bazis_author-2.2.0/bazis/contrib/author/routes_abstract.py +50 -0
  17. bazis_author-2.2.0/bazis_author.egg-info/PKG-INFO +589 -0
  18. bazis_author-2.2.0/bazis_author.egg-info/SOURCES.txt +56 -0
  19. bazis_author-2.2.0/bazis_author.egg-info/dependency_links.txt +1 -0
  20. bazis_author-2.2.0/bazis_author.egg-info/requires.txt +8 -0
  21. bazis_author-2.2.0/bazis_author.egg-info/top_level.txt +1 -0
  22. bazis_author-2.2.0/pyproject.toml +94 -0
  23. bazis_author-2.2.0/pytest.ini +4 -0
  24. bazis_author-2.2.0/sample/deploy/admin.sh +4 -0
  25. bazis_author-2.2.0/sample/deploy/app.sh +5 -0
  26. bazis_author-2.2.0/sample/deploy/config/admin.py +30 -0
  27. bazis_author-2.2.0/sample/deploy/config/app.py +32 -0
  28. bazis_author-2.2.0/sample/deploy/db.sh +78 -0
  29. bazis_author-2.2.0/sample/deploy/init.sh +40 -0
  30. bazis_author-2.2.0/sample/entity/__init__.py +14 -0
  31. bazis_author-2.2.0/sample/entity/admin.py +64 -0
  32. bazis_author-2.2.0/sample/entity/migrations/0001_initial.py +110 -0
  33. bazis_author-2.2.0/sample/entity/migrations/0002_initial.py +87 -0
  34. bazis_author-2.2.0/sample/entity/migrations/0003_alter_childentity_dt_created_and_more.py +67 -0
  35. bazis_author-2.2.0/sample/entity/migrations/__init__.py +14 -0
  36. bazis_author-2.2.0/sample/entity/models.py +64 -0
  37. bazis_author-2.2.0/sample/entity/router.py +24 -0
  38. bazis_author-2.2.0/sample/entity/routes.py +49 -0
  39. bazis_author-2.2.0/sample/manage.py +35 -0
  40. bazis_author-2.2.0/sample/project.env +11 -0
  41. bazis_author-2.2.0/sample/pyproject.toml +14 -0
  42. bazis_author-2.2.0/sample/sample/__init__.py +14 -0
  43. bazis_author-2.2.0/sample/sample/main.py +24 -0
  44. bazis_author-2.2.0/sample/sample/router.py +19 -0
  45. bazis_author-2.2.0/sample/sample/settings.py +15 -0
  46. bazis_author-2.2.0/sample/sample/urls.py +29 -0
  47. bazis_author-2.2.0/sample/sample/wsgi.py +22 -0
  48. bazis_author-2.2.0/sample/users/__init__.py +14 -0
  49. bazis_author-2.2.0/sample/users/migrations/0001_initial.py +63 -0
  50. bazis_author-2.2.0/sample/users/migrations/0002_user_dt_first_login.py +34 -0
  51. bazis_author-2.2.0/sample/users/migrations/__init__.py +14 -0
  52. bazis_author-2.2.0/sample/users/models.py +24 -0
  53. bazis_author-2.2.0/sample/uv.lock +1329 -0
  54. bazis_author-2.2.0/setup.cfg +4 -0
  55. bazis_author-2.2.0/tests/__init__.py +14 -0
  56. bazis_author-2.2.0/tests/conftest.py +30 -0
  57. bazis_author-2.2.0/tests/factories.py +49 -0
  58. bazis_author-2.2.0/tests/test_author.py +159 -0
@@ -0,0 +1,3 @@
1
+ .gitlab-ci.yml
2
+ .gitignore
3
+ .dockerignore
@@ -0,0 +1,16 @@
1
+ name: Build and Publish
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*.*.*'
7
+
8
+ permissions:
9
+ contents: write
10
+
11
+ jobs:
12
+ build:
13
+ uses: ecofuture-tech/.github/.github/workflows/reusable-build-publish.yml@main
14
+ secrets:
15
+ PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
16
+ TEST_PYPI_TOKEN: ${{ secrets.TEST_PYPI_TOKEN }}
@@ -0,0 +1,18 @@
1
+ name: Update Licenses
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ paths:
8
+ - '**.py'
9
+ - 'pyproject.toml'
10
+
11
+ permissions:
12
+ contents: write
13
+
14
+ jobs:
15
+ update-licenses:
16
+ uses: ecofuture-tech/.github/.github/workflows/reusable-update-licenses.yml@main
17
+ secrets:
18
+ WORKFLOW_LICENSE_TOKEN: ${{ secrets.WORKFLOW_LICENSE_TOKEN }}
@@ -0,0 +1,44 @@
1
+ .idea
2
+ .ipynb_checkpoints
3
+ .mypy_cache
4
+ .vscode
5
+ __pycache__
6
+ .pytest_cache
7
+ htmlcov
8
+ dist
9
+ site
10
+ .coverage
11
+ coverage.xml
12
+ .netlify
13
+ test.db
14
+ log.txt
15
+ Pipfile.lock
16
+ env3.*
17
+ env
18
+ docs_build
19
+ venv
20
+ .venv
21
+ docs.zip
22
+ archive.zip
23
+
24
+ .env
25
+ local.env
26
+ */.env*
27
+
28
+ # vim temporary files
29
+ *~
30
+ .*.sw?
31
+ *.DS_Store
32
+
33
+ *.egg-info
34
+ build
35
+ dist
36
+
37
+ docs/_autosummary
38
+ docs/_build
39
+ *.DS_Store
40
+ *.log
41
+ my-*
42
+ My*
43
+ */**/_version.py
44
+ /uv.lock
@@ -0,0 +1,589 @@
1
+ Metadata-Version: 2.4
2
+ Name: bazis-author
3
+ Version: 2.2.0
4
+ Summary: Author management module for Bazis framework.
5
+ Author-email: Ilya Kharyn <ilya.tt07@gmail.com>
6
+ Maintainer-email: Ilya Kharyn <ilya.tt07@gmail.com>
7
+ Project-URL: Home, https://github.com/ecofuture-tech/bazis-author
8
+ Keywords: bazis,django,fastapi,pydantic,framework,jsonapi,author
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Framework :: Django
17
+ Classifier: Framework :: FastAPI
18
+ Requires-Python: >=3.12
19
+ Description-Content-Type: text/markdown
20
+ Requires-Dist: bazis
21
+ Requires-Dist: bazis-users
22
+ Provides-Extra: test
23
+ Requires-Dist: bazis-test-utils; extra == "test"
24
+ Provides-Extra: dev
25
+ Requires-Dist: ruff; extra == "dev"
26
+
27
+ # Bazis Author
28
+
29
+ [![PyPI version](https://img.shields.io/pypi/v/bazis-author.svg)](https://pypi.org/project/bazis-author/)
30
+ [![Python Versions](https://img.shields.io/pypi/pyversions/bazis-author.svg)](https://pypi.org/project/bazis-author/)
31
+ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
32
+
33
+ An extension package for Bazis that provides automatic authorship tracking for models (who created and who updated records).
34
+
35
+ ## Quick Start
36
+
37
+ ```bash
38
+ # Install the package
39
+ uv add bazis-author
40
+
41
+ # Create model with authorship tracking
42
+ from bazis.contrib.author.models_abstract import AuthorMixin
43
+ from bazis.core.models_abstract import DtMixin, UuidMixin, JsonApiMixin
44
+ from django.db import models
45
+
46
+ class Article(AuthorMixin, DtMixin, UuidMixin, JsonApiMixin):
47
+ """Article with author tracking"""
48
+ title = models.CharField(max_length=255)
49
+ content = models.TextField()
50
+
51
+ class Meta:
52
+ verbose_name = 'Article'
53
+ verbose_name_plural = 'Articles'
54
+
55
+ # Create route with automatic author population
56
+ from bazis.contrib.author.routes_abstract import AuthorRouteBase
57
+ from django.apps import apps
58
+
59
+ class ArticleRouteSet(AuthorRouteBase):
60
+ model = apps.get_model('myapp.Article')
61
+ ```
62
+
63
+ ## Table of Contents
64
+
65
+ - [Description](#description)
66
+ - [Requirements](#requirements)
67
+ - [Installation](#installation)
68
+ - [Core Components](#core-components)
69
+ - [Model Mixins](#model-mixins)
70
+ - [Base Routes](#base-routes)
71
+ - [Admin Mixins](#admin-mixins)
72
+ - [Usage](#usage)
73
+ - [Creating Model with Authorship](#creating-model-with-authorship)
74
+ - [Creating Route with Authorship](#creating-route-with-authorship)
75
+ - [Admin Setup](#admin-setup)
76
+ - [How It Works](#how-it-works)
77
+ - [Examples](#examples)
78
+ - [License](#license)
79
+ - [Links](#links)
80
+
81
+ ## Description
82
+
83
+ **Bazis Author** is an extension package for the Bazis framework that provides automatic authorship tracking for records. The package includes:
84
+
85
+ - **AuthorMixin** — mixin for models adding `author` (who created) and `author_updated` (who updated) fields
86
+ - **AuthorRouteBase** and **AuthorRequiredRouteBase** — base route classes with automatic author field population
87
+ - **AuthorAdminMixin** — admin mixin with convenient filters and readonly fields
88
+
89
+ The main advantage of the package is fully automatic author field population without the need to write additional code.
90
+
91
+ **This package requires `bazis` and `bazis-users` packages to be installed.**
92
+
93
+ ## Requirements
94
+
95
+ - **Python**: 3.12+
96
+ - **bazis**: latest version
97
+ - **bazis-users**: latest version
98
+ - **PostgreSQL**: 12+
99
+ - **Redis**: For caching
100
+
101
+ ## Installation
102
+
103
+ ### Using uv (recommended)
104
+
105
+ ```bash
106
+ uv add bazis-author
107
+ ```
108
+
109
+ ### Using pip
110
+
111
+ ```bash
112
+ pip install bazis-author
113
+ ```
114
+
115
+ ## Core Components
116
+
117
+ ### Model Mixins
118
+
119
+ #### AuthorMixin
120
+
121
+ Mixin for models adding authorship tracking fields.
122
+
123
+ **Location**: `bazis.contrib.author.models_abstract.AuthorMixin`
124
+
125
+ **Added fields**:
126
+
127
+ - `author` — Foreign Key to user who created the record (nullable)
128
+ - `author_updated` — Foreign Key to user who last updated the record (nullable)
129
+
130
+ **Features**:
131
+
132
+ - Inherits from `UserMixin`, has access to user context via `CTX_USER_REQUEST`
133
+ - Automatically populates `author` when creating record
134
+ - Automatically updates `author_updated` on every save
135
+ - Fields are optional (nullable), so records can be created without a user
136
+
137
+ **Example usage**:
138
+
139
+ ```python
140
+ from bazis.contrib.author.models_abstract import AuthorMixin
141
+ from bazis.core.models_abstract import DtMixin, UuidMixin, JsonApiMixin
142
+ from django.db import models
143
+
144
+ class Document(AuthorMixin, DtMixin, UuidMixin, JsonApiMixin):
145
+ """Document with authorship tracking"""
146
+ title = models.CharField('Title', max_length=255)
147
+ content = models.TextField('Content')
148
+
149
+ class Meta:
150
+ verbose_name = 'Document'
151
+ verbose_name_plural = 'Documents'
152
+ ```
153
+
154
+ ### Base Routes
155
+
156
+ #### AuthorRouteMixin
157
+
158
+ Base mixin for routes that automatically populate author fields.
159
+
160
+ **Location**: `bazis.contrib.author.routes_abstract.AuthorRouteMixin`
161
+
162
+ **Features**:
163
+
164
+ - Excludes `author` and `author_updated` fields from CREATE and UPDATE schemas (they are populated automatically)
165
+ - Uses `hook_before_create` and `hook_before_update` hooks to set author
166
+ - Sets author only for authenticated users
167
+
168
+ **Methods**:
169
+
170
+ - `hook_before_create(item)` — sets `author` before creating record
171
+ - `hook_before_update(item)` — sets `author_updated` before updating record
172
+
173
+ #### AuthorRouteBase
174
+
175
+ Base route class with authorship support for optional authentication.
176
+
177
+ **Location**: `bazis.contrib.author.routes_abstract.AuthorRouteBase`
178
+
179
+ **Inheritance**: `AuthorRouteMixin` + `UserRouteBase`
180
+
181
+ Use this class when endpoints are accessible to both authenticated and anonymous users.
182
+
183
+ **Example usage**:
184
+
185
+ ```python
186
+ from bazis.contrib.author.routes_abstract import AuthorRouteBase
187
+ from django.apps import apps
188
+
189
+ class ArticleRouteSet(AuthorRouteBase):
190
+ """Route for articles with automatic authorship"""
191
+ model = apps.get_model('myapp.Article')
192
+ ```
193
+
194
+ #### AuthorRequiredRouteBase
195
+
196
+ Base route class with authorship support for required authentication.
197
+
198
+ **Location**: `bazis.contrib.author.routes_abstract.AuthorRequiredRouteBase`
199
+
200
+ **Inheritance**: `AuthorRouteMixin` + `UserRequiredRouteBase`
201
+
202
+ Use this class when endpoints require mandatory authentication (return 401 for unauthenticated requests).
203
+
204
+ **Example usage**:
205
+
206
+ ```python
207
+ from bazis.contrib.author.routes_abstract import AuthorRequiredRouteBase
208
+ from django.apps import apps
209
+
210
+ class PrivateDocumentRouteSet(AuthorRequiredRouteBase):
211
+ """Route for private documents (authenticated only)"""
212
+ model = apps.get_model('myapp.PrivateDocument')
213
+ ```
214
+
215
+ ### Admin Mixins
216
+
217
+ #### AuthorAdminMixin
218
+
219
+ Mixin for Django admin classes adding convenient work with author fields.
220
+
221
+ **Location**: `bazis.contrib.author.admin_abstract.AuthorAdminMixin`
222
+
223
+ **Capabilities**:
224
+
225
+ - Adds `author__username` to search fields
226
+ - Makes `author` and `author_updated` fields readonly
227
+ - Adds autocomplete filter by author to list view
228
+ - Automatically populates author fields when saving through admin
229
+
230
+ **Example usage**:
231
+
232
+ ```python
233
+ from django.contrib import admin
234
+ from bazis.contrib.author.admin_abstract import AuthorAdminMixin
235
+ from bazis.core.admin_abstract import DtAdminMixin
236
+ from .models import Article
237
+
238
+ @admin.register(Article)
239
+ class ArticleAdmin(AuthorAdminMixin, DtAdminMixin, admin.ModelAdmin):
240
+ list_display = ('id', 'title', 'author', 'author_updated', 'dt_created')
241
+ search_fields = ('title', 'content')
242
+ ```
243
+
244
+ ## Usage
245
+
246
+ ### Creating Model with Authorship
247
+
248
+ ```python
249
+ from bazis.contrib.author.models_abstract import AuthorMixin
250
+ from bazis.core.models_abstract import DtMixin, UuidMixin, JsonApiMixin
251
+ from django.db import models
252
+
253
+ class Article(AuthorMixin, DtMixin, UuidMixin, JsonApiMixin):
254
+ """Article with automatic author tracking"""
255
+ title = models.CharField('Title', max_length=255)
256
+ content = models.TextField('Content')
257
+ is_published = models.BooleanField('Published', default=False)
258
+
259
+ class Meta:
260
+ verbose_name = 'Article'
261
+ verbose_name_plural = 'Articles'
262
+
263
+ def __str__(self):
264
+ return self.title
265
+ ```
266
+
267
+ ### Creating Route with Authorship
268
+
269
+ #### For public endpoints (optional authentication)
270
+
271
+ ```python
272
+ from bazis.contrib.author.routes_abstract import AuthorRouteBase
273
+ from django.apps import apps
274
+
275
+ class ArticleRouteSet(AuthorRouteBase):
276
+ """
277
+ Articles can be created by anonymous users.
278
+ If user is authenticated - author fields will be populated automatically.
279
+ """
280
+ model = apps.get_model('myapp.Article')
281
+ ```
282
+
283
+ **Behavior**:
284
+ - Anonymous user creates record → `author` and `author_updated` remain `null`
285
+ - Authenticated user creates record → `author` is automatically populated
286
+ - Any user updates record → `author_updated` is populated if user is authenticated
287
+
288
+ #### For private endpoints (required authentication)
289
+
290
+ ```python
291
+ from bazis.contrib.author.routes_abstract import AuthorRequiredRouteBase
292
+ from django.apps import apps
293
+
294
+ class PrivateDocumentRouteSet(AuthorRequiredRouteBase):
295
+ """
296
+ Documents are accessible only to authenticated users.
297
+ Anonymous users will get 401.
298
+ """
299
+ model = apps.get_model('myapp.PrivateDocument')
300
+ ```
301
+
302
+ **Behavior**:
303
+ - Anonymous user tries to create/update record → 401 Unauthorized
304
+ - Authenticated user creates record → `author` is automatically populated
305
+ - Authenticated user updates record → `author_updated` is automatically updated
306
+
307
+ ### Admin Setup
308
+
309
+ ```python
310
+ from django.contrib import admin
311
+ from bazis.contrib.author.admin_abstract import AuthorAdminMixin
312
+ from bazis.core.admin_abstract import DtAdminMixin
313
+ from .models import Article
314
+
315
+ @admin.register(Article)
316
+ class ArticleAdmin(AuthorAdminMixin, DtAdminMixin, admin.ModelAdmin):
317
+ list_display = ('id', 'title', 'author', 'author_updated', 'is_published', 'dt_created')
318
+ search_fields = ('title', 'content')
319
+ list_filter = ('is_published',)
320
+ readonly_fields = ('dt_created', 'dt_updated')
321
+ ```
322
+
323
+ **What AuthorAdminMixin provides**:
324
+
325
+ 1. **Autocomplete filter by author** in list view
326
+ 2. **Search by author username** (`author__username`)
327
+ 3. **Readonly fields** `author` and `author_updated` (cannot be edited)
328
+ 4. **Automatic population** when saving through admin
329
+
330
+ ## How It Works
331
+
332
+ ### In Models
333
+
334
+ When calling `save()` on a model with `AuthorMixin`:
335
+
336
+ 1. Check for user presence in `CTX_USER_REQUEST` context
337
+ 2. If user exists:
338
+ - When creating record (`author` is empty) → set `author`
339
+ - When updating record → update `author_updated`
340
+ 3. Call `super().save()` to save
341
+
342
+ ```python
343
+ def save(self, *args, **kwargs):
344
+ if author := self.CTX_USER_REQUEST.get():
345
+ if not self.author:
346
+ self.author = author
347
+ self.author_updated = author
348
+ super().save(*args, **kwargs)
349
+ ```
350
+
351
+ ### In Routes
352
+
353
+ When creating/updating record via API:
354
+
355
+ 1. **In CREATE**: `hook_before_create(item)` is triggered
356
+ - If user is NOT anonymous → set `item.author`
357
+
358
+ 2. **In UPDATE**: `hook_before_update(item)` is triggered
359
+ - If user is NOT anonymous → set `item.author_updated`
360
+
361
+ 3. Fields `author` and `author_updated` are **excluded** from CREATE/UPDATE schemas, so client cannot pass them
362
+
363
+ ```python
364
+ fields: dict[ApiAction, SchemaFields] = {
365
+ CrudApiAction.CREATE: SchemaFields(
366
+ exclude={'author': None, 'author_updated': None},
367
+ ),
368
+ CrudApiAction.UPDATE: SchemaFields(
369
+ exclude={'author': None, 'author_updated': None},
370
+ ),
371
+ }
372
+ ```
373
+
374
+ ### In Admin
375
+
376
+ When saving through Django Admin:
377
+
378
+ 1. `save_model()` — sets author from `request.user`
379
+ 2. `save_formset()` — sets author for inline models
380
+
381
+ ## Examples
382
+
383
+ ### Complete Application Example
384
+
385
+ **models.py**:
386
+ ```python
387
+ from bazis.contrib.author.models_abstract import AuthorMixin
388
+ from bazis.core.models_abstract import DtMixin, UuidMixin, JsonApiMixin
389
+ from django.db import models
390
+
391
+ class Article(AuthorMixin, DtMixin, UuidMixin, JsonApiMixin):
392
+ """Public article"""
393
+ title = models.CharField('Title', max_length=255)
394
+ content = models.TextField('Content')
395
+ is_published = models.BooleanField('Published', default=False)
396
+
397
+ class Meta:
398
+ verbose_name = 'Article'
399
+ verbose_name_plural = 'Articles'
400
+
401
+ class PrivateNote(AuthorMixin, DtMixin, UuidMixin, JsonApiMixin):
402
+ """Private note (authenticated only)"""
403
+ title = models.CharField('Title', max_length=255)
404
+ content = models.TextField('Content')
405
+
406
+ class Meta:
407
+ verbose_name = 'Note'
408
+ verbose_name_plural = 'Notes'
409
+ ```
410
+
411
+ **routes.py**:
412
+ ```python
413
+ from bazis.contrib.author.routes_abstract import AuthorRouteBase, AuthorRequiredRouteBase
414
+ from django.apps import apps
415
+
416
+ class ArticleRouteSet(AuthorRouteBase):
417
+ """Articles accessible to everyone, but author is tracked if user is authenticated"""
418
+ model = apps.get_model('myapp.Article')
419
+
420
+ class PrivateNoteRouteSet(AuthorRequiredRouteBase):
421
+ """Notes accessible only to authenticated users"""
422
+ model = apps.get_model('myapp.PrivateNote')
423
+ ```
424
+
425
+ **router.py**:
426
+ ```python
427
+ from bazis.core.routing import BazisRouter
428
+ from . import routes
429
+
430
+ router = BazisRouter(tags=['Content'])
431
+ router.register(routes.ArticleRouteSet.as_router())
432
+ router.register(routes.PrivateNoteRouteSet.as_router())
433
+ ```
434
+
435
+ **admin.py**:
436
+ ```python
437
+ from django.contrib import admin
438
+ from bazis.contrib.author.admin_abstract import AuthorAdminMixin
439
+ from bazis.core.admin_abstract import DtAdminMixin
440
+ from .models import Article, PrivateNote
441
+
442
+ @admin.register(Article)
443
+ class ArticleAdmin(AuthorAdminMixin, DtAdminMixin, admin.ModelAdmin):
444
+ list_display = ('id', 'title', 'author', 'is_published', 'dt_created')
445
+ search_fields = ('title', 'content')
446
+ list_filter = ('is_published',)
447
+
448
+ @admin.register(PrivateNote)
449
+ class PrivateNoteAdmin(AuthorAdminMixin, DtAdminMixin, admin.ModelAdmin):
450
+ list_display = ('id', 'title', 'author', 'dt_created')
451
+ search_fields = ('title', 'content')
452
+ ```
453
+
454
+ ### API Usage Examples
455
+
456
+ **Creating article by anonymous user**:
457
+ ```bash
458
+ POST /api/v1/content/article/
459
+ Content-Type: application/json
460
+
461
+ {
462
+ "data": {
463
+ "type": "myapp.article",
464
+ "attributes": {
465
+ "title": "My Article",
466
+ "content": "Article content",
467
+ "is_published": true
468
+ }
469
+ }
470
+ }
471
+
472
+ # Response: 201 Created
473
+ # author and author_updated will be null
474
+ ```
475
+
476
+ **Creating article by authenticated user**:
477
+ ```bash
478
+ POST /api/v1/content/article/
479
+ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
480
+ Content-Type: application/json
481
+
482
+ {
483
+ "data": {
484
+ "type": "myapp.article",
485
+ "attributes": {
486
+ "title": "My Article",
487
+ "content": "Article content",
488
+ "is_published": true
489
+ }
490
+ }
491
+ }
492
+
493
+ # Response: 201 Created
494
+ # author is automatically set to current user
495
+ ```
496
+
497
+ **Attempting to create private note without authentication**:
498
+ ```bash
499
+ POST /api/v1/content/private_note/
500
+ Content-Type: application/json
501
+
502
+ {
503
+ "data": {
504
+ "type": "myapp.privatenote",
505
+ "attributes": {
506
+ "title": "Secret Note",
507
+ "content": "Secret content"
508
+ }
509
+ }
510
+ }
511
+
512
+ # Response: 401 Unauthorized
513
+ ```
514
+
515
+ **Creating private note with authentication**:
516
+ ```bash
517
+ POST /api/v1/content/private_note/
518
+ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
519
+ Content-Type: application/json
520
+
521
+ {
522
+ "data": {
523
+ "type": "myapp.privatenote",
524
+ "attributes": {
525
+ "title": "Secret Note",
526
+ "content": "Secret content"
527
+ }
528
+ }
529
+ }
530
+
531
+ # Response: 201 Created
532
+ # author is automatically set to current user
533
+ ```
534
+
535
+ ### Authorship Check in Responses
536
+
537
+ Fields `author` and `author_updated` are returned in the `relationships` block:
538
+
539
+ ```json
540
+ {
541
+ "data": {
542
+ "type": "myapp.article",
543
+ "id": "123e4567-e89b-12d3-a456-426614174000",
544
+ "attributes": {
545
+ "title": "My Article",
546
+ "content": "Article content",
547
+ "is_published": true
548
+ },
549
+ "relationships": {
550
+ "author": {
551
+ "data": {
552
+ "type": "users.user",
553
+ "id": "456e7890-e89b-12d3-a456-426614174000"
554
+ }
555
+ },
556
+ "author_updated": {
557
+ "data": {
558
+ "type": "users.user",
559
+ "id": "456e7890-e89b-12d3-a456-426614174000"
560
+ }
561
+ }
562
+ }
563
+ }
564
+ }
565
+ ```
566
+
567
+ ## License
568
+
569
+ Apache License 2.0
570
+
571
+ See [LICENSE](LICENSE) file for details.
572
+
573
+ ## Links
574
+
575
+ - [Bazis Documentation](https://github.com/ecofuture-tech/bazis) — main repository
576
+ - [Bazis Users](https://github.com/ecofuture-tech/bazis-users) — user management package
577
+ - [Bazis Author Repository](https://github.com/ecofuture-tech/bazis-author) — package repository
578
+ - [Issue Tracker](https://github.com/ecofuture-tech/bazis-author/issues) — report bugs or request features
579
+
580
+ ## Support
581
+
582
+ If you have questions or issues:
583
+ - Check the [Bazis documentation](https://github.com/ecofuture-tech/bazis)
584
+ - Search [existing issues](https://github.com/ecofuture-tech/bazis-author/issues)
585
+ - Create a [new issue](https://github.com/ecofuture-tech/bazis-author/issues/new) with detailed information
586
+
587
+ ---
588
+
589
+ Made with ❤️ by the Bazis team