bazis-users 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.
- bazis_users-2.2.0/.dockerignore +3 -0
- bazis_users-2.2.0/.github/workflows/build-publish.yml +16 -0
- bazis_users-2.2.0/.github/workflows/update-licenses.yml +18 -0
- bazis_users-2.2.0/.gitignore +44 -0
- bazis_users-2.2.0/PKG-INFO +594 -0
- bazis_users-2.2.0/README.md +568 -0
- bazis_users-2.2.0/bazis/contrib/users/__init__.py +31 -0
- bazis_users-2.2.0/bazis/contrib/users/_version.py +34 -0
- bazis_users-2.2.0/bazis/contrib/users/admin_abstract.py +40 -0
- bazis_users-2.2.0/bazis/contrib/users/apps.py +28 -0
- bazis_users-2.2.0/bazis/contrib/users/conf.py +41 -0
- bazis_users-2.2.0/bazis/contrib/users/locale/en/LC_MESSAGES/django.mo +0 -0
- bazis_users-2.2.0/bazis/contrib/users/locale/en/LC_MESSAGES/django.po +63 -0
- bazis_users-2.2.0/bazis/contrib/users/locale/ru/LC_MESSAGES/django.mo +0 -0
- bazis_users-2.2.0/bazis/contrib/users/locale/ru/LC_MESSAGES/django.po +81 -0
- bazis_users-2.2.0/bazis/contrib/users/middleware.py +37 -0
- bazis_users-2.2.0/bazis/contrib/users/migrations/0001_initial.py +63 -0
- bazis_users-2.2.0/bazis/contrib/users/migrations/0002_user_dt_first_login.py +34 -0
- bazis_users-2.2.0/bazis/contrib/users/migrations/__init__.py +14 -0
- bazis_users-2.2.0/bazis/contrib/users/models.py +34 -0
- bazis_users-2.2.0/bazis/contrib/users/models_abstract.py +156 -0
- bazis_users-2.2.0/bazis/contrib/users/router.py +23 -0
- bazis_users-2.2.0/bazis/contrib/users/routes.py +97 -0
- bazis_users-2.2.0/bazis/contrib/users/routes_abstract.py +110 -0
- bazis_users-2.2.0/bazis/contrib/users/schemas.py +25 -0
- bazis_users-2.2.0/bazis/contrib/users/service.py +92 -0
- bazis_users-2.2.0/bazis_users.egg-info/PKG-INFO +594 -0
- bazis_users-2.2.0/bazis_users.egg-info/SOURCES.txt +82 -0
- bazis_users-2.2.0/bazis_users.egg-info/dependency_links.txt +1 -0
- bazis_users-2.2.0/bazis_users.egg-info/requires.txt +8 -0
- bazis_users-2.2.0/bazis_users.egg-info/top_level.txt +1 -0
- bazis_users-2.2.0/docs/Makefile +24 -0
- bazis_users-2.2.0/docs/README.md +31 -0
- bazis_users-2.2.0/docs/_static/pydata-custom.css +10 -0
- bazis_users-2.2.0/docs/_static/readthedocs-custom.css +30 -0
- bazis_users-2.2.0/docs/_templates/custom-class-template.rst +35 -0
- bazis_users-2.2.0/docs/_templates/custom-module-template.rst +66 -0
- bazis_users-2.2.0/docs/conf.py +253 -0
- bazis_users-2.2.0/docs/index.rst +46 -0
- bazis_users-2.2.0/docs/make.bat +39 -0
- bazis_users-2.2.0/docs/manage.py +36 -0
- bazis_users-2.2.0/docs/project.env +2 -0
- bazis_users-2.2.0/docs/requirements.txt +7 -0
- bazis_users-2.2.0/docs/settings.py +15 -0
- bazis_users-2.2.0/docs/structure.rst +10 -0
- bazis_users-2.2.0/pyproject.toml +94 -0
- bazis_users-2.2.0/pytest.ini +4 -0
- bazis_users-2.2.0/sample/deploy/admin.sh +4 -0
- bazis_users-2.2.0/sample/deploy/app.sh +5 -0
- bazis_users-2.2.0/sample/deploy/config/admin.py +30 -0
- bazis_users-2.2.0/sample/deploy/config/app.py +32 -0
- bazis_users-2.2.0/sample/deploy/db.sh +78 -0
- bazis_users-2.2.0/sample/deploy/init.sh +40 -0
- bazis_users-2.2.0/sample/entity/__init__.py +14 -0
- bazis_users-2.2.0/sample/entity/admin.py +97 -0
- bazis_users-2.2.0/sample/entity/migrations/0001_initial.py +114 -0
- bazis_users-2.2.0/sample/entity/migrations/0002_alter_childentity_dt_created_and_more.py +67 -0
- bazis_users-2.2.0/sample/entity/migrations/__init__.py +14 -0
- bazis_users-2.2.0/sample/entity/models.py +105 -0
- bazis_users-2.2.0/sample/entity/router.py +24 -0
- bazis_users-2.2.0/sample/entity/routes.py +67 -0
- bazis_users-2.2.0/sample/manage.py +40 -0
- bazis_users-2.2.0/sample/project.env +19 -0
- bazis_users-2.2.0/sample/pyproject.toml +14 -0
- bazis_users-2.2.0/sample/sample/__init__.py +14 -0
- bazis_users-2.2.0/sample/sample/main.py +24 -0
- bazis_users-2.2.0/sample/sample/router.py +21 -0
- bazis_users-2.2.0/sample/sample/settings.py +15 -0
- bazis_users-2.2.0/sample/sample/urls.py +29 -0
- bazis_users-2.2.0/sample/sample/wsgi.py +22 -0
- bazis_users-2.2.0/sample/users/__init__.py +14 -0
- bazis_users-2.2.0/sample/users/admin.py +24 -0
- bazis_users-2.2.0/sample/users/apps.py +20 -0
- bazis_users-2.2.0/sample/users/migrations/0001_initial.py +63 -0
- bazis_users-2.2.0/sample/users/migrations/0002_user_dt_first_login.py +34 -0
- bazis_users-2.2.0/sample/users/migrations/__init__.py +14 -0
- bazis_users-2.2.0/sample/users/models.py +34 -0
- bazis_users-2.2.0/sample/users/router.py +15 -0
- bazis_users-2.2.0/setup.cfg +4 -0
- bazis_users-2.2.0/tests/__init__.py +14 -0
- bazis_users-2.2.0/tests/conftest.py +39 -0
- bazis_users-2.2.0/tests/factories.py +37 -0
- bazis_users-2.2.0/tests/test_protected.py +159 -0
- bazis_users-2.2.0/tests/test_users.py +64 -0
|
@@ -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,594 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bazis-users
|
|
3
|
+
Version: 2.2.0
|
|
4
|
+
Summary: User 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-users
|
|
8
|
+
Keywords: bazis,django,fastapi,pydantic,framework,jsonapi,users
|
|
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: python-multipart
|
|
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 Users
|
|
28
|
+
|
|
29
|
+
[](https://pypi.org/project/bazis-users/)
|
|
30
|
+
[](https://pypi.org/project/bazis-users/)
|
|
31
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
32
|
+
|
|
33
|
+
An extension package for Bazis that provides advanced capabilities for user management and authentication.
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Install the package
|
|
39
|
+
uv add bazis-users
|
|
40
|
+
|
|
41
|
+
# Create user model
|
|
42
|
+
from bazis.contrib.users.models_abstract import UserAbstract
|
|
43
|
+
from bazis.core.models_abstract import DtMixin, UuidMixin, JsonApiMixin
|
|
44
|
+
|
|
45
|
+
class User(UserAbstract, DtMixin, UuidMixin, JsonApiMixin):
|
|
46
|
+
"""Custom user model"""
|
|
47
|
+
|
|
48
|
+
class Meta:
|
|
49
|
+
verbose_name = 'User'
|
|
50
|
+
verbose_name_plural = 'Users'
|
|
51
|
+
|
|
52
|
+
# Create user route
|
|
53
|
+
from bazis.contrib.users.routes import UserRouteSet
|
|
54
|
+
from django.apps import apps
|
|
55
|
+
|
|
56
|
+
class MyUserRouteSet(UserRouteSet):
|
|
57
|
+
model = apps.get_model('myapp.User')
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Table of Contents
|
|
61
|
+
|
|
62
|
+
- [Description](#description)
|
|
63
|
+
- [Requirements](#requirements)
|
|
64
|
+
- [Installation](#installation)
|
|
65
|
+
- [Core Components](#core-components)
|
|
66
|
+
- [Abstract Models](#abstract-models)
|
|
67
|
+
- [Mixins](#mixins)
|
|
68
|
+
- [Base Routes](#base-routes)
|
|
69
|
+
- [Built-in Routes](#built-in-routes)
|
|
70
|
+
- [Services](#services)
|
|
71
|
+
- [Usage](#usage)
|
|
72
|
+
- [Creating User Model](#creating-user-model)
|
|
73
|
+
- [Creating User Route](#creating-user-route)
|
|
74
|
+
- [Using Authentication Services](#using-authentication-services)
|
|
75
|
+
- [Linking Models to Users](#linking-models-to-users)
|
|
76
|
+
- [API Authentication](#api-authentication)
|
|
77
|
+
- [Examples](#examples)
|
|
78
|
+
- [License](#license)
|
|
79
|
+
- [Links](#links)
|
|
80
|
+
|
|
81
|
+
## Description
|
|
82
|
+
|
|
83
|
+
**Bazis Users** is an extension package for the Bazis framework that provides a complete toolkit for user management and authentication. The package includes:
|
|
84
|
+
|
|
85
|
+
- Abstract user models with JWT authentication support
|
|
86
|
+
- Ready-to-use routes for user operations
|
|
87
|
+
- Services for token and authentication management
|
|
88
|
+
- Mixins for linking models to users
|
|
89
|
+
- Integration with Swagger UI for API testing
|
|
90
|
+
|
|
91
|
+
**This package requires the base `bazis` package to be installed.**
|
|
92
|
+
|
|
93
|
+
## Requirements
|
|
94
|
+
|
|
95
|
+
- **Python**: 3.12+
|
|
96
|
+
- **bazis**: latest version
|
|
97
|
+
- **PostgreSQL**: 12+
|
|
98
|
+
- **Redis**: For caching
|
|
99
|
+
|
|
100
|
+
## Installation
|
|
101
|
+
|
|
102
|
+
### Using uv (recommended)
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
uv add bazis-users
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Using pip
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
pip install bazis-users
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Core Components
|
|
115
|
+
|
|
116
|
+
### Abstract Models
|
|
117
|
+
|
|
118
|
+
#### UserAbstract
|
|
119
|
+
|
|
120
|
+
Abstract user model implementing methods required for extended work with Bazis.
|
|
121
|
+
|
|
122
|
+
**Location**: `bazis.contrib.users.models_abstract.UserAbstract`
|
|
123
|
+
|
|
124
|
+
**Methods**:
|
|
125
|
+
|
|
126
|
+
- `get_full_name()` - returns user's full name
|
|
127
|
+
- `jwt_build()` - creates JWT token for the user
|
|
128
|
+
- `find_or_create()` - finds or creates a user
|
|
129
|
+
- `raw_password` - works with raw password
|
|
130
|
+
|
|
131
|
+
**Example usage**:
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from bazis.contrib.users.models_abstract import UserAbstract
|
|
135
|
+
from bazis.core.models_abstract import DtMixin, UuidMixin, JsonApiMixin
|
|
136
|
+
|
|
137
|
+
class User(UserAbstract, DtMixin, UuidMixin, JsonApiMixin):
|
|
138
|
+
"""Application user model"""
|
|
139
|
+
|
|
140
|
+
class Meta:
|
|
141
|
+
verbose_name = 'User'
|
|
142
|
+
verbose_name_plural = 'Users'
|
|
143
|
+
|
|
144
|
+
def __str__(self):
|
|
145
|
+
return self.get_full_name()
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
#### AnonymousUserAbstract
|
|
149
|
+
|
|
150
|
+
Abstract anonymous user model for handling unauthenticated requests.
|
|
151
|
+
|
|
152
|
+
**Location**: `bazis.contrib.users.models_abstract.AnonymousUserAbstract`
|
|
153
|
+
|
|
154
|
+
**Methods**:
|
|
155
|
+
|
|
156
|
+
- `get_full_name()` - returns name for anonymous user
|
|
157
|
+
|
|
158
|
+
### Mixins
|
|
159
|
+
|
|
160
|
+
#### UserMixin
|
|
161
|
+
|
|
162
|
+
Mixin for entity models that should be associated with a user.
|
|
163
|
+
|
|
164
|
+
**Location**: `bazis.contrib.users.models_abstract.UserMixin`
|
|
165
|
+
|
|
166
|
+
**Features**:
|
|
167
|
+
|
|
168
|
+
- User is stored in context variable `CTX_USER_REQUEST`
|
|
169
|
+
- Automatically links model to current user from request
|
|
170
|
+
|
|
171
|
+
**Example usage**:
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
from bazis.contrib.users.models_abstract import UserMixin
|
|
175
|
+
from bazis.core.models_abstract import DtMixin, UuidMixin, JsonApiMixin
|
|
176
|
+
from django.db import models
|
|
177
|
+
|
|
178
|
+
class Article(UserMixin, DtMixin, UuidMixin, JsonApiMixin):
|
|
179
|
+
"""Article linked to a user"""
|
|
180
|
+
title = models.CharField(max_length=255)
|
|
181
|
+
content = models.TextField()
|
|
182
|
+
|
|
183
|
+
class Meta:
|
|
184
|
+
verbose_name = 'Article'
|
|
185
|
+
verbose_name_plural = 'Articles'
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Base Routes
|
|
189
|
+
|
|
190
|
+
#### UserRouteBase
|
|
191
|
+
|
|
192
|
+
Base class for routes associated with users.
|
|
193
|
+
|
|
194
|
+
**Location**: `bazis.contrib.users.routes_abstract.UserRouteBase`
|
|
195
|
+
|
|
196
|
+
Provides base functionality for creating routes that work with user models.
|
|
197
|
+
|
|
198
|
+
### Built-in Routes
|
|
199
|
+
|
|
200
|
+
#### token_auth
|
|
201
|
+
|
|
202
|
+
Basic token authentication (JWT), applied in Swagger UI.
|
|
203
|
+
|
|
204
|
+
**Location**: `bazis.contrib.users.routes.token_auth`
|
|
205
|
+
|
|
206
|
+
Enables JWT token authentication in Swagger UI.
|
|
207
|
+
|
|
208
|
+
#### UserRouteSet
|
|
209
|
+
|
|
210
|
+
Base route class for working with users.
|
|
211
|
+
|
|
212
|
+
**Location**: `bazis.contrib.users.routes.UserRouteSet`
|
|
213
|
+
|
|
214
|
+
Provides ready-to-use endpoints for user operations:
|
|
215
|
+
- List users
|
|
216
|
+
- Get specific user information
|
|
217
|
+
- Create user
|
|
218
|
+
- Update user
|
|
219
|
+
- Delete user
|
|
220
|
+
|
|
221
|
+
**Example usage**:
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
from bazis.contrib.users.routes import UserRouteSet
|
|
225
|
+
from django.apps import apps
|
|
226
|
+
|
|
227
|
+
class MyUserRouteSet(UserRouteSet):
|
|
228
|
+
model = apps.get_model('myapp.User')
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Services
|
|
232
|
+
|
|
233
|
+
Services for working with users and tokens.
|
|
234
|
+
|
|
235
|
+
**Location**: `bazis.contrib.users.services`
|
|
236
|
+
|
|
237
|
+
#### get_token_data
|
|
238
|
+
|
|
239
|
+
Extracts data from JWT token.
|
|
240
|
+
|
|
241
|
+
```python
|
|
242
|
+
from bazis.contrib.users.services import get_token_data
|
|
243
|
+
|
|
244
|
+
def my_endpoint(token: str = Depends(get_token_data)):
|
|
245
|
+
user_id = token.get('user_id')
|
|
246
|
+
# Work with token data
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
#### get_user_from_token
|
|
250
|
+
|
|
251
|
+
Gets user from token.
|
|
252
|
+
|
|
253
|
+
```python
|
|
254
|
+
from bazis.contrib.users.services import get_user_from_token
|
|
255
|
+
|
|
256
|
+
def my_endpoint(user = Depends(get_user_from_token)):
|
|
257
|
+
# user - user object or AnonymousUser
|
|
258
|
+
print(user.get_full_name())
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
#### get_user_required
|
|
262
|
+
|
|
263
|
+
Gets user from request (authentication required).
|
|
264
|
+
|
|
265
|
+
```python
|
|
266
|
+
from bazis.contrib.users.services import get_user_required
|
|
267
|
+
|
|
268
|
+
def my_endpoint(user = Depends(get_user_required)):
|
|
269
|
+
# user - authenticated user
|
|
270
|
+
# Returns 401 if token is invalid
|
|
271
|
+
return {"user": user.get_full_name()}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
#### get_user_optional
|
|
275
|
+
|
|
276
|
+
Gets user from request (authentication optional).
|
|
277
|
+
|
|
278
|
+
```python
|
|
279
|
+
from bazis.contrib.users.services import get_user_optional
|
|
280
|
+
|
|
281
|
+
def my_endpoint(user = Depends(get_user_optional)):
|
|
282
|
+
# user - user or AnonymousUser
|
|
283
|
+
if user.is_authenticated:
|
|
284
|
+
return {"user": user.get_full_name()}
|
|
285
|
+
return {"user": "Anonymous"}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Usage
|
|
289
|
+
|
|
290
|
+
### Creating User Model
|
|
291
|
+
|
|
292
|
+
```python
|
|
293
|
+
from bazis.contrib.users.models_abstract import UserAbstract
|
|
294
|
+
from bazis.core.models_abstract import DtMixin, UuidMixin, JsonApiMixin
|
|
295
|
+
from django.db import models
|
|
296
|
+
|
|
297
|
+
class User(UserAbstract, DtMixin, UuidMixin, JsonApiMixin):
|
|
298
|
+
"""Custom user model"""
|
|
299
|
+
|
|
300
|
+
# Additional fields
|
|
301
|
+
phone = models.CharField('Phone', max_length=20, null=True, blank=True)
|
|
302
|
+
department = models.CharField('Department', max_length=100, null=True, blank=True)
|
|
303
|
+
|
|
304
|
+
class Meta:
|
|
305
|
+
verbose_name = 'User'
|
|
306
|
+
verbose_name_plural = 'Users'
|
|
307
|
+
|
|
308
|
+
def __str__(self):
|
|
309
|
+
return self.get_full_name()
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Creating User Route
|
|
313
|
+
|
|
314
|
+
```python
|
|
315
|
+
# routes.py
|
|
316
|
+
from bazis.contrib.users.routes import UserRouteSet
|
|
317
|
+
from bazis.core.schemas.fields import SchemaField, SchemaFields
|
|
318
|
+
from django.apps import apps
|
|
319
|
+
|
|
320
|
+
class MyUserRouteSet(UserRouteSet):
|
|
321
|
+
model = apps.get_model('myapp.User')
|
|
322
|
+
|
|
323
|
+
# Add additional fields to schema
|
|
324
|
+
fields = {
|
|
325
|
+
None: SchemaFields(
|
|
326
|
+
include={
|
|
327
|
+
'phone': None,
|
|
328
|
+
'department': None,
|
|
329
|
+
},
|
|
330
|
+
),
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
```python
|
|
335
|
+
# router.py
|
|
336
|
+
from bazis.core.routing import BazisRouter
|
|
337
|
+
from . import routes
|
|
338
|
+
|
|
339
|
+
router = BazisRouter(tags=['Users'])
|
|
340
|
+
router.register(routes.MyUserRouteSet.as_router())
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Using Authentication Services
|
|
344
|
+
|
|
345
|
+
#### Required Authentication
|
|
346
|
+
|
|
347
|
+
Use `get_user_required` for endpoints that require authentication:
|
|
348
|
+
|
|
349
|
+
```python
|
|
350
|
+
from bazis.contrib.users.services import get_user_required
|
|
351
|
+
from fastapi import APIRouter, Depends
|
|
352
|
+
from pydantic import BaseModel
|
|
353
|
+
|
|
354
|
+
router = APIRouter()
|
|
355
|
+
|
|
356
|
+
class NewPasswordRequest(BaseModel):
|
|
357
|
+
old_password: str
|
|
358
|
+
new_password: str
|
|
359
|
+
|
|
360
|
+
@router.post('/user/change_password/', response_model=dict)
|
|
361
|
+
def change_password(
|
|
362
|
+
data: NewPasswordRequest,
|
|
363
|
+
user: User = Depends(get_user_required)
|
|
364
|
+
):
|
|
365
|
+
# user - guaranteed authenticated user
|
|
366
|
+
# Returns 401 if token is invalid
|
|
367
|
+
return user.change_password(data)
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
#### Optional Authentication
|
|
371
|
+
|
|
372
|
+
Use `get_user_optional` for endpoints available to everyone:
|
|
373
|
+
|
|
374
|
+
```python
|
|
375
|
+
from bazis.contrib.users.services import get_user_optional, get_token_data
|
|
376
|
+
from fastapi import Depends
|
|
377
|
+
|
|
378
|
+
@router.get('/auth/', response_model=AuthResponse)
|
|
379
|
+
def auth(
|
|
380
|
+
auth_store: AuthStore = Depends(),
|
|
381
|
+
user: User = Depends(get_user_optional),
|
|
382
|
+
token_data: dict = Depends(get_token_data),
|
|
383
|
+
):
|
|
384
|
+
# user can be AnonymousUser
|
|
385
|
+
if user.is_anonymous:
|
|
386
|
+
if user_id := auth_store.user_id:
|
|
387
|
+
user = User.objects.filter(id=user_id).first()
|
|
388
|
+
|
|
389
|
+
# Logic for working with user or anonymous
|
|
390
|
+
return {"user": user.get_full_name() if not user.is_anonymous else "Anonymous"}
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
#### Using get_token_data
|
|
394
|
+
|
|
395
|
+
Getting data from JWT token without loading the user:
|
|
396
|
+
|
|
397
|
+
```python
|
|
398
|
+
from bazis.contrib.users.services import get_token_data
|
|
399
|
+
from fastapi import Depends
|
|
400
|
+
|
|
401
|
+
@router.get('/token-info/')
|
|
402
|
+
def token_info(token_data: dict = Depends(get_token_data)):
|
|
403
|
+
# token_data contains decoded data from JWT
|
|
404
|
+
return {
|
|
405
|
+
"user_id": token_data.get("user_id"),
|
|
406
|
+
"exp": token_data.get("exp"),
|
|
407
|
+
}
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### Linking Models to Users
|
|
411
|
+
|
|
412
|
+
```python
|
|
413
|
+
from bazis.contrib.users.models_abstract import UserMixin
|
|
414
|
+
from bazis.core.models_abstract import DtMixin, UuidMixin, JsonApiMixin
|
|
415
|
+
from django.db import models
|
|
416
|
+
|
|
417
|
+
class Document(UserMixin, DtMixin, UuidMixin, JsonApiMixin):
|
|
418
|
+
"""Document automatically linked to user"""
|
|
419
|
+
title = models.CharField(max_length=255)
|
|
420
|
+
content = models.TextField()
|
|
421
|
+
|
|
422
|
+
class Meta:
|
|
423
|
+
verbose_name = 'Document'
|
|
424
|
+
verbose_name_plural = 'Documents'
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
## API Authentication
|
|
428
|
+
|
|
429
|
+
### Getting Token
|
|
430
|
+
|
|
431
|
+
After setting up routes, users can obtain JWT tokens through the authentication endpoint.
|
|
432
|
+
|
|
433
|
+
**Example request**:
|
|
434
|
+
```bash
|
|
435
|
+
POST /api/v1/auth/login
|
|
436
|
+
Content-Type: application/json
|
|
437
|
+
|
|
438
|
+
{
|
|
439
|
+
"username": "user@example.com",
|
|
440
|
+
"password": "password123"
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
**Example response**:
|
|
445
|
+
```json
|
|
446
|
+
{
|
|
447
|
+
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
|
448
|
+
"token_type": "bearer"
|
|
449
|
+
}
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
### Using Token in Requests
|
|
453
|
+
|
|
454
|
+
```bash
|
|
455
|
+
GET /api/v1/protected-resource
|
|
456
|
+
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### Authentication in Swagger UI
|
|
460
|
+
|
|
461
|
+
1. Open `/api/swagger/`
|
|
462
|
+
2. Click "Authorize" button
|
|
463
|
+
3. Enter token in format: `Bearer <your_token>`
|
|
464
|
+
4. Click "Authorize"
|
|
465
|
+
5. Now all requests will be executed with authentication
|
|
466
|
+
|
|
467
|
+
## Examples
|
|
468
|
+
|
|
469
|
+
### Complete Application Example with Users
|
|
470
|
+
|
|
471
|
+
**models.py**:
|
|
472
|
+
```python
|
|
473
|
+
from bazis.contrib.users.models_abstract import UserAbstract, UserMixin
|
|
474
|
+
from bazis.core.models_abstract import DtMixin, UuidMixin, JsonApiMixin
|
|
475
|
+
from django.db import models
|
|
476
|
+
|
|
477
|
+
class User(UserAbstract, DtMixin, UuidMixin, JsonApiMixin):
|
|
478
|
+
"""System user"""
|
|
479
|
+
phone = models.CharField('Phone', max_length=20, null=True, blank=True)
|
|
480
|
+
|
|
481
|
+
class Meta:
|
|
482
|
+
verbose_name = 'User'
|
|
483
|
+
verbose_name_plural = 'Users'
|
|
484
|
+
|
|
485
|
+
class Task(UserMixin, DtMixin, UuidMixin, JsonApiMixin):
|
|
486
|
+
"""Task linked to user"""
|
|
487
|
+
title = models.CharField('Title', max_length=255)
|
|
488
|
+
description = models.TextField('Description')
|
|
489
|
+
is_completed = models.BooleanField('Completed', default=False)
|
|
490
|
+
assigned_to = models.ForeignKey(
|
|
491
|
+
User,
|
|
492
|
+
on_delete=models.CASCADE,
|
|
493
|
+
related_name='tasks',
|
|
494
|
+
verbose_name='Assigned To'
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
class Meta:
|
|
498
|
+
verbose_name = 'Task'
|
|
499
|
+
verbose_name_plural = 'Tasks'
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
**routes.py**:
|
|
503
|
+
```python
|
|
504
|
+
from bazis.contrib.users.routes import UserRouteSet
|
|
505
|
+
from bazis.contrib.users.services import get_user_required
|
|
506
|
+
from django.apps import apps
|
|
507
|
+
from fastapi import APIRouter, Depends
|
|
508
|
+
from pydantic import BaseModel
|
|
509
|
+
|
|
510
|
+
class MyUserRouteSet(UserRouteSet):
|
|
511
|
+
model = apps.get_model('myapp.User')
|
|
512
|
+
|
|
513
|
+
# Additional endpoints for working with tasks
|
|
514
|
+
router = APIRouter()
|
|
515
|
+
|
|
516
|
+
class TaskCreateRequest(BaseModel):
|
|
517
|
+
title: str
|
|
518
|
+
description: str
|
|
519
|
+
|
|
520
|
+
@router.get('/my-tasks/')
|
|
521
|
+
def get_my_tasks(user: User = Depends(get_user_required)):
|
|
522
|
+
# Return only current user's tasks
|
|
523
|
+
tasks = Task.objects.filter(assigned_to=user)
|
|
524
|
+
return {"tasks": [{"id": str(t.id), "title": t.title} for t in tasks]}
|
|
525
|
+
|
|
526
|
+
@router.post('/tasks/')
|
|
527
|
+
def create_task(
|
|
528
|
+
data: TaskCreateRequest,
|
|
529
|
+
user: User = Depends(get_user_required)
|
|
530
|
+
):
|
|
531
|
+
# Create task for current user
|
|
532
|
+
task = Task.objects.create(
|
|
533
|
+
title=data.title,
|
|
534
|
+
description=data.description,
|
|
535
|
+
assigned_to=user
|
|
536
|
+
)
|
|
537
|
+
return {"id": str(task.id), "title": task.title}
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
**router.py**:
|
|
541
|
+
```python
|
|
542
|
+
from bazis.core.routing import BazisRouter
|
|
543
|
+
from . import routes
|
|
544
|
+
|
|
545
|
+
# Register user routes
|
|
546
|
+
user_router = BazisRouter(tags=['Users'])
|
|
547
|
+
user_router.register(routes.MyUserRouteSet.as_router())
|
|
548
|
+
|
|
549
|
+
# Register additional task endpoints
|
|
550
|
+
user_router.include_router(routes.router, prefix='/api/v1')
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
**admin.py**:
|
|
554
|
+
```python
|
|
555
|
+
from django.contrib import admin
|
|
556
|
+
from bazis.core.admin_abstract import DtAdminMixin
|
|
557
|
+
from .models import User, Task
|
|
558
|
+
|
|
559
|
+
@admin.register(User)
|
|
560
|
+
class UserAdmin(DtAdminMixin, admin.ModelAdmin):
|
|
561
|
+
list_display = ('id', 'username', 'email', 'phone', 'is_active')
|
|
562
|
+
search_fields = ('username', 'email', 'phone')
|
|
563
|
+
list_filter = ('is_active', 'is_staff')
|
|
564
|
+
|
|
565
|
+
@admin.register(Task)
|
|
566
|
+
class TaskAdmin(DtAdminMixin, admin.ModelAdmin):
|
|
567
|
+
list_display = ('id', 'title', 'assigned_to', 'is_completed')
|
|
568
|
+
search_fields = ('title', 'description')
|
|
569
|
+
list_filter = ('is_completed',)
|
|
570
|
+
raw_id_fields = ('assigned_to',)
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
## License
|
|
574
|
+
|
|
575
|
+
Apache License 2.0
|
|
576
|
+
|
|
577
|
+
See [LICENSE](LICENSE) file for details.
|
|
578
|
+
|
|
579
|
+
## Links
|
|
580
|
+
|
|
581
|
+
- [Bazis Documentation](https://github.com/ecofuture-tech/bazis) — main repository
|
|
582
|
+
- [Bazis Users Repository](https://github.com/ecofuture-tech/bazis-users) — package repository
|
|
583
|
+
- [Issue Tracker](https://github.com/ecofuture-tech/bazis-users/issues) — report bugs or request features
|
|
584
|
+
|
|
585
|
+
## Support
|
|
586
|
+
|
|
587
|
+
If you have questions or issues:
|
|
588
|
+
- Check the [Bazis documentation](https://github.com/ecofuture-tech/bazis)
|
|
589
|
+
- Search [existing issues](https://github.com/ecofuture-tech/bazis-users/issues)
|
|
590
|
+
- Create a [new issue](https://github.com/ecofuture-tech/bazis-users/issues/new) with detailed information
|
|
591
|
+
|
|
592
|
+
---
|
|
593
|
+
|
|
594
|
+
Made with ❤️ by the Bazis team
|