django-pfx 1.4.dev64__tar.gz → 1.4.dev68__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.
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/PKG-INFO +1 -1
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/django_pfx.egg-info/PKG-INFO +1 -1
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/django_pfx.egg-info/SOURCES.txt +3 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/doc/source/authentication.md +27 -3
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/manage.py +2 -1
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.po +6 -6
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/models/abstract_pfx_base_user.py +3 -1
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/models/pfx_user.py +1 -2
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/shortcuts.py +15 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/views/__init__.py +3 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/views/rest_views.py +76 -1
- django_pfx-1.4.dev68/tests/migrations/0001_initial.py +132 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/models.py +16 -57
- django_pfx-1.4.dev68/tests/settings/__init__.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/tests/__init__.py +1 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/tests/test_client.py +1 -16
- django_pfx-1.4.dev68/tests/tests/test_permissions.py +239 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/urls.py +2 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/views.py +21 -3
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/.gitignore +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/.gitlab-ci.yml +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/.pre-commit-config.yaml +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/LICENSE +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/MANIFEST.in +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/README.md +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/django_pfx.egg-info/dependency_links.txt +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/django_pfx.egg-info/requires.txt +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/django_pfx.egg-info/top_level.txt +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/doc/Makefile +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/doc/conf.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/doc/index.rst +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/doc/source/api.views.rst +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/doc/source/decorator.md +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/doc/source/generate_openapi.md +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/doc/source/getting_started.md +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/doc/source/internationalisation.md +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/doc/source/model.md +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/doc/source/pfx_views.md +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/doc/source/profiling.md +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/doc/source/settings.md +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/doc/source/testing.md +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/img/pfx.png +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/img/pfx.svg +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/make_messages +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/__init__.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/__init__.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/apidoc/__init__.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/apidoc/parameters.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/apidoc/schema.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/apidoc/tags.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/apps.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/decorator/__init__.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/decorator/rest.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/default_settings.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/exceptions.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/fields.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/http/__init__.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/http/json_response.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.mo +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/management/__init__.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/management/commands/__init__.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/management/commands/makeapidoc.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/management/commands/profile.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/middleware/__init__.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/middleware/authentication.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/middleware/locale.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/middleware/profiling.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/migrations/0001_initial.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/migrations/0002_pfxpermissionsuser.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/migrations/0003_delete_pfxpermissionsuser.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/migrations/__init__.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/models/__init__.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/models/cache_mixins.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/models/login_ban.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/models/not_null_fields.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/models/otp_user_mixin.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/models/pfx_models.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/models/user_filtered_queryset_mixin.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/serializers/__init__.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/serializers/json.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/settings.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/storage/__init__.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/storage/s3_storage.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/templates/registration/otp_code_email.txt +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/templates/registration/otp_code_subject.txt +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/templates/registration/password_reset_email.txt +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/templates/registration/password_reset_subject.txt +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/templates/registration/welcome_email.txt +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/templates/registration/welcome_subject.txt +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/test.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/urls.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/views/authentication_views.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/views/fields.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/views/filters_views.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/views/locale_views.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/views/parameters/__init__.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/views/parameters/date_format.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/views/parameters/groups.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/views/parameters/list_count.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/views/parameters/list_items.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/views/parameters/list_mode.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/views/parameters/list_order.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/views/parameters/list_search.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/views/parameters/media_redirect.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/views/parameters/meta_fields.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/views/parameters/meta_filters.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/views/parameters/meta_orders.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/views/parameters/subset.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/views/parameters/subset_limit.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/views/parameters/subset_offset.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/views/parameters/subset_page.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/views/parameters/subset_page_size.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/pfxcore/views/parameters/subset_page_subset.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/settings/__init__.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pfx/settings/dev.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/pyproject.toml +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/requirements.txt +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/serve-doc +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/setup.cfg +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/setup.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/__init__.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/locale/fr/LC_MESSAGES/django.po +0 -0
- {django_pfx-1.4.dev64/tests/settings → django_pfx-1.4.dev68/tests/migrations}/__init__.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/settings/ci.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/settings/common.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/settings/dev.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/settings/dev_custom_example.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/settings/dev_default.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/tests/basic_api_errors.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/tests/basic_api_test.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/tests/test_api_doc.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/tests/test_api_doc_search.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/tests/test_auth_api.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/tests/test_body_mixin.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/tests/test_cache.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/tests/test_fields.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/tests/test_filters.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/tests/test_locale_api.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/tests/test_perm_tests.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/tests/test_perms_api.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/tests/test_profiling_middleware.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/tests/test_settings.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/tests/test_shortcuts.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/tests/test_timezone_middleware.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/tests/test_tools.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/tests/test_user_queryset.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/tests/test_view_decorators.py +0 -0
- {django_pfx-1.4.dev64 → django_pfx-1.4.dev68}/tests/tests/test_view_fields.py +0 -0
|
@@ -114,6 +114,8 @@ tests/models.py
|
|
|
114
114
|
tests/urls.py
|
|
115
115
|
tests/views.py
|
|
116
116
|
tests/locale/fr/LC_MESSAGES/django.po
|
|
117
|
+
tests/migrations/0001_initial.py
|
|
118
|
+
tests/migrations/__init__.py
|
|
117
119
|
tests/settings/__init__.py
|
|
118
120
|
tests/settings/ci.py
|
|
119
121
|
tests/settings/common.py
|
|
@@ -133,6 +135,7 @@ tests/tests/test_fields.py
|
|
|
133
135
|
tests/tests/test_filters.py
|
|
134
136
|
tests/tests/test_locale_api.py
|
|
135
137
|
tests/tests/test_perm_tests.py
|
|
138
|
+
tests/tests/test_permissions.py
|
|
136
139
|
tests/tests/test_perms_api.py
|
|
137
140
|
tests/tests/test_profiling_middleware.py
|
|
138
141
|
tests/tests/test_settings.py
|
|
@@ -9,20 +9,44 @@ including password validation and hashing.
|
|
|
9
9
|
## User Model
|
|
10
10
|
|
|
11
11
|
You have the option to use the standard Django User with {class}`pfx.pfxcore.models.PFXUser`
|
|
12
|
-
(which is a {class}`django.contrib.auth.models.User` with PFX required mixins)
|
|
13
|
-
|
|
12
|
+
(which is a {class}`django.contrib.auth.models.User` with PFX required mixins):
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
AUTH_USER_MODEL = 'pfxcore.PFXUser'
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
But you may prefer to use your own model. To do this, create your own user class.
|
|
19
|
+
|
|
20
|
+
You have 2 options:
|
|
14
21
|
|
|
15
22
|
```python
|
|
16
23
|
from pfx.pfxcore.models import AbstractPFXBaseUser
|
|
17
24
|
|
|
18
25
|
class MyUser(AbstractPFXBaseUser):
|
|
26
|
+
# Equivalent of django.contrib.auth.models.AbstractBaseUser for PFX.
|
|
27
|
+
#
|
|
28
|
+
# Minimal user, you have to manage the USERNAME_FIELD by yourself,
|
|
29
|
+
# and you have to add django.contrib.auth.models.PermissionsMixin
|
|
30
|
+
# if you want to use the permission system.
|
|
19
31
|
pass
|
|
20
32
|
```
|
|
21
33
|
|
|
34
|
+
```python
|
|
35
|
+
from pfx.pfxcore.models import AbstractPFXUser
|
|
36
|
+
|
|
37
|
+
class MyUser(AbstractPFXUser):
|
|
38
|
+
# Equivalent of django.contrib.auth.models.AbstractUser for PFX.
|
|
39
|
+
#
|
|
40
|
+
# This is the same as using pfxcore.PFXUser, but you can add your
|
|
41
|
+
# custom fields.
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
|
|
22
46
|
Then, define the model in your settings:
|
|
23
47
|
|
|
24
48
|
```python
|
|
25
|
-
AUTH_USER_MODEL =
|
|
49
|
+
AUTH_USER_MODEL = 'myapp.MyUser'
|
|
26
50
|
```
|
|
27
51
|
|
|
28
52
|
## Authentication Modes
|
|
@@ -21,7 +21,8 @@ def main():
|
|
|
21
21
|
cmd = len(sys.argv) > 1 and sys.argv[1]
|
|
22
22
|
os.environ.setdefault(
|
|
23
23
|
"DJANGO_SETTINGS_MODULE",
|
|
24
|
-
cmd
|
|
24
|
+
cmd in ('test', 'makemigrations') and
|
|
25
|
+
"tests.settings.dev" or "pfx.settings.dev")
|
|
25
26
|
try:
|
|
26
27
|
from django.core.management import execute_from_command_line
|
|
27
28
|
except ImportError as exc:
|
|
@@ -7,7 +7,7 @@ msgid ""
|
|
|
7
7
|
msgstr ""
|
|
8
8
|
"Project-Id-Version: \n"
|
|
9
9
|
"Report-Msgid-Bugs-To: \n"
|
|
10
|
-
"POT-Creation-Date:
|
|
10
|
+
"POT-Creation-Date: 2025-01-21 14:22+0100\n"
|
|
11
11
|
"PO-Revision-Date: 2021-06-22 23:31+0200\n"
|
|
12
12
|
"Last-Translator: \n"
|
|
13
13
|
"Language-Team: \n"
|
|
@@ -259,27 +259,27 @@ msgstr "Un nouveau code d'authentification a été envoyé par e-mail."
|
|
|
259
259
|
msgid "Invalid value for {filter} filter"
|
|
260
260
|
msgstr "Valeur invalide pour le filtre {filter}"
|
|
261
261
|
|
|
262
|
-
#: views/rest_views.py:
|
|
262
|
+
#: views/rest_views.py:242
|
|
263
263
|
#, python-brace-format
|
|
264
264
|
msgid "{obj} cannot be deleted because it is referenced by other objects."
|
|
265
265
|
msgstr ""
|
|
266
266
|
"{obj} ne peut pas être supprimé car il est référencé par d’autres objets."
|
|
267
267
|
|
|
268
|
-
#: views/rest_views.py:
|
|
268
|
+
#: views/rest_views.py:342
|
|
269
269
|
#, python-brace-format
|
|
270
270
|
msgid "{model} {obj} created."
|
|
271
271
|
msgstr "{model} {obj} créé."
|
|
272
272
|
|
|
273
|
-
#: views/rest_views.py:
|
|
273
|
+
#: views/rest_views.py:343
|
|
274
274
|
#, python-brace-format
|
|
275
275
|
msgid "{model} {obj} updated."
|
|
276
276
|
msgstr "{model} {obj} modifié."
|
|
277
277
|
|
|
278
|
-
#: views/rest_views.py:
|
|
278
|
+
#: views/rest_views.py:1141
|
|
279
279
|
#, python-brace-format
|
|
280
280
|
msgid "{model} {obj} deleted."
|
|
281
281
|
msgstr "{model} {obj} supprimé."
|
|
282
282
|
|
|
283
|
-
#: views/rest_views.py:
|
|
283
|
+
#: views/rest_views.py:1240 views/rest_views.py:1280
|
|
284
284
|
msgid "Unexpected storage error"
|
|
285
285
|
msgstr "Erreur de stockage inattendue"
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from django.contrib.auth.models import AbstractBaseUser, AbstractUser
|
|
2
2
|
|
|
3
|
+
from .pfx_models import PFXModelMixin
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
|
|
6
|
+
class AbstractPFXBaseUser(PFXModelMixin, AbstractBaseUser):
|
|
5
7
|
"""The base abstract user for PFX."""
|
|
6
8
|
|
|
7
9
|
class Meta:
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
from django.contrib.auth.models import AbstractUser
|
|
2
2
|
|
|
3
3
|
from .abstract_pfx_base_user import AbstractPFXUser
|
|
4
|
-
from .pfx_models import PFXModelMixin
|
|
5
4
|
|
|
6
5
|
|
|
7
|
-
class PFXUser(
|
|
6
|
+
class PFXUser(AbstractPFXUser):
|
|
8
7
|
"""The Django User with PFX mixins.
|
|
9
8
|
"""
|
|
10
9
|
|
|
@@ -121,3 +121,18 @@ def register_views(*views):
|
|
|
121
121
|
|
|
122
122
|
def class_key(cls, *args):
|
|
123
123
|
return f"{cls.__module__}.{cls.__name__}{''.join(f'.{a}' for a in args)}"
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def permissions(*perms):
|
|
127
|
+
from django.contrib.auth.models import Permission
|
|
128
|
+
pks = set()
|
|
129
|
+
for perm in perms:
|
|
130
|
+
app_label, codename = perm.split('.')
|
|
131
|
+
pks.add(Permission.objects.get(
|
|
132
|
+
codename=codename, content_type__app_label=app_label).pk)
|
|
133
|
+
return Permission.objects.filter(pk__in=pks)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def model_permissions(model, *actions):
|
|
137
|
+
meta = model._meta
|
|
138
|
+
return {f"{meta.app_label}.{a}_{meta.model_name}" for a in actions}
|
|
@@ -15,13 +15,16 @@ from .rest_views import (
|
|
|
15
15
|
DeleteRestViewMixin,
|
|
16
16
|
DetailRestViewMixin,
|
|
17
17
|
ListRestViewMixin,
|
|
18
|
+
MediaPermsRestViewMixin,
|
|
18
19
|
MediaRestViewMixin,
|
|
19
20
|
ModelBodyMixin,
|
|
20
21
|
ModelMixin,
|
|
21
22
|
ModelResponseMixin,
|
|
23
|
+
PermsRestView,
|
|
22
24
|
RestView,
|
|
23
25
|
SecuredRestViewMixin,
|
|
24
26
|
SlugDetailRestViewMixin,
|
|
27
|
+
SlugPermsDetailRestViewMixin,
|
|
25
28
|
UpdateRestViewMixin,
|
|
26
29
|
resource_not_found,
|
|
27
30
|
)
|
|
@@ -34,7 +34,14 @@ from pfx.pfxcore.exceptions import (
|
|
|
34
34
|
from pfx.pfxcore.fields import MediaField
|
|
35
35
|
from pfx.pfxcore.http import JsonResponse
|
|
36
36
|
from pfx.pfxcore.models import JSONReprMixin, UserFilteredQuerySetMixin
|
|
37
|
-
from pfx.pfxcore.shortcuts import
|
|
37
|
+
from pfx.pfxcore.shortcuts import (
|
|
38
|
+
class_key,
|
|
39
|
+
f,
|
|
40
|
+
get_bool,
|
|
41
|
+
get_int,
|
|
42
|
+
get_object,
|
|
43
|
+
model_permissions,
|
|
44
|
+
)
|
|
38
45
|
from pfx.pfxcore.storage.s3_storage import StorageException
|
|
39
46
|
|
|
40
47
|
from . import parameters
|
|
@@ -242,6 +249,12 @@ class ModelMixin():
|
|
|
242
249
|
return cls.tags or [
|
|
243
250
|
Tag(str(cls.model._meta.verbose_name))]
|
|
244
251
|
|
|
252
|
+
@classmethod
|
|
253
|
+
def get_model_perms(cls, actions):
|
|
254
|
+
"""Get the model permission name for the action.
|
|
255
|
+
"""
|
|
256
|
+
return model_permissions(cls.model, actions)
|
|
257
|
+
|
|
245
258
|
|
|
246
259
|
class ModelResponseMixin(ModelMixin):
|
|
247
260
|
"""Extension of :class:`ModelMixin` to manage object responses."""
|
|
@@ -894,6 +907,13 @@ class ListRestViewMixin(ModelResponseMixin):
|
|
|
894
907
|
cls.meta_list_schema, cls.model_list_schema]
|
|
895
908
|
|
|
896
909
|
|
|
910
|
+
class ListPermsRestViewMixin(ListRestViewMixin):
|
|
911
|
+
"""Extension mixin to check permissions."""
|
|
912
|
+
|
|
913
|
+
def get_list_perm(self, *args, **kwargs):
|
|
914
|
+
return self.request.user.has_perm(*self.get_model_perms('view'))
|
|
915
|
+
|
|
916
|
+
|
|
897
917
|
class DetailRestViewMixin(ModelResponseMixin):
|
|
898
918
|
"""Extension mixin to add a get detail route."""
|
|
899
919
|
|
|
@@ -917,6 +937,13 @@ class DetailRestViewMixin(ModelResponseMixin):
|
|
|
917
937
|
return self.response(obj)
|
|
918
938
|
|
|
919
939
|
|
|
940
|
+
class DetailPermsRestViewMixin(DetailRestViewMixin):
|
|
941
|
+
"""Extension mixin to check permissions."""
|
|
942
|
+
|
|
943
|
+
def get_perm(self, *args, **kwargs):
|
|
944
|
+
return self.request.user.has_perm(*self.get_model_perms('view'))
|
|
945
|
+
|
|
946
|
+
|
|
920
947
|
class SlugDetailRestViewMixin(ModelResponseMixin):
|
|
921
948
|
"""Extension mixin to add a get detail by slug route."""
|
|
922
949
|
|
|
@@ -944,6 +971,13 @@ class SlugDetailRestViewMixin(ModelResponseMixin):
|
|
|
944
971
|
return self.response(obj)
|
|
945
972
|
|
|
946
973
|
|
|
974
|
+
class SlugPermsDetailRestViewMixin(SlugDetailRestViewMixin):
|
|
975
|
+
"""Extension mixin to check permissions."""
|
|
976
|
+
|
|
977
|
+
def get_by_slug_perm(self, *args, **kwargs):
|
|
978
|
+
return self.request.user.has_perm(*self.get_model_perms('view'))
|
|
979
|
+
|
|
980
|
+
|
|
947
981
|
class CreateRestViewMixin(ModelBodyMixin, ModelResponseMixin):
|
|
948
982
|
"""Extension mixin to add create route."""
|
|
949
983
|
|
|
@@ -1013,6 +1047,13 @@ class CreateRestViewMixin(ModelBodyMixin, ModelResponseMixin):
|
|
|
1013
1047
|
return self._post(*args, **kwargs)
|
|
1014
1048
|
|
|
1015
1049
|
|
|
1050
|
+
class CreatePermsRestViewMixin(CreateRestViewMixin):
|
|
1051
|
+
"""Extension mixin to check permissions."""
|
|
1052
|
+
|
|
1053
|
+
def post_perm(self, *args, **kwargs):
|
|
1054
|
+
return self.request.user.has_perm(*self.get_model_perms('add'))
|
|
1055
|
+
|
|
1056
|
+
|
|
1016
1057
|
class UpdateRestViewMixin(ModelBodyMixin, ModelResponseMixin):
|
|
1017
1058
|
"""Extension mixin to add create route."""
|
|
1018
1059
|
|
|
@@ -1068,6 +1109,13 @@ class UpdateRestViewMixin(ModelBodyMixin, ModelResponseMixin):
|
|
|
1068
1109
|
return self._put(id, *args, **kwargs)
|
|
1069
1110
|
|
|
1070
1111
|
|
|
1112
|
+
class UpdatePermsRestViewMixin(UpdateRestViewMixin):
|
|
1113
|
+
"""Extension mixin to check permissions."""
|
|
1114
|
+
|
|
1115
|
+
def put_perm(self, *args, **kwargs):
|
|
1116
|
+
return self.request.user.has_perm(*self.get_model_perms('change'))
|
|
1117
|
+
|
|
1118
|
+
|
|
1071
1119
|
class DeleteRestViewMixin(ModelMixin):
|
|
1072
1120
|
"""Extension mixin to add delete route."""
|
|
1073
1121
|
|
|
@@ -1111,6 +1159,13 @@ class DeleteRestViewMixin(ModelMixin):
|
|
|
1111
1159
|
return self._delete(id, *args, **kwargs)
|
|
1112
1160
|
|
|
1113
1161
|
|
|
1162
|
+
class DeletePermsRestViewMixin(DeleteRestViewMixin):
|
|
1163
|
+
"""Extension mixin to check permissions."""
|
|
1164
|
+
|
|
1165
|
+
def delete_perm(self, *args, **kwargs):
|
|
1166
|
+
return self.request.user.has_perm(*self.get_model_perms('delete'))
|
|
1167
|
+
|
|
1168
|
+
|
|
1114
1169
|
class MediaRestViewMixin(ModelMixin):
|
|
1115
1170
|
"""Extension mixin to manage media fields."""
|
|
1116
1171
|
|
|
@@ -1228,6 +1283,16 @@ class MediaRestViewMixin(ModelMixin):
|
|
|
1228
1283
|
return JsonResponse(dict(url=url))
|
|
1229
1284
|
|
|
1230
1285
|
|
|
1286
|
+
class MediaPermsRestViewMixin(MediaRestViewMixin):
|
|
1287
|
+
"""Extension mixin to check permissions."""
|
|
1288
|
+
|
|
1289
|
+
def field_media_upload_url_perm(self, *args, **kwargs):
|
|
1290
|
+
return self.request.user.has_perm(*self.get_model_perms('change'))
|
|
1291
|
+
|
|
1292
|
+
def field_media_get_perm(self, *args, **kwargs):
|
|
1293
|
+
return self.request.user.has_perm(*self.get_model_perms('view'))
|
|
1294
|
+
|
|
1295
|
+
|
|
1231
1296
|
class SecuredRestViewMixin(View):
|
|
1232
1297
|
"""A view mixin to manage service permissions.
|
|
1233
1298
|
|
|
@@ -1409,3 +1474,13 @@ class RestView(
|
|
|
1409
1474
|
DeleteRestViewMixin,
|
|
1410
1475
|
BaseRestView):
|
|
1411
1476
|
pass
|
|
1477
|
+
|
|
1478
|
+
|
|
1479
|
+
class PermsRestView(
|
|
1480
|
+
ListPermsRestViewMixin,
|
|
1481
|
+
DetailPermsRestViewMixin,
|
|
1482
|
+
CreatePermsRestViewMixin,
|
|
1483
|
+
UpdatePermsRestViewMixin,
|
|
1484
|
+
DeletePermsRestViewMixin,
|
|
1485
|
+
BaseRestView):
|
|
1486
|
+
pass
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Generated by Django 4.2.17 on 2025-01-21 04:24
|
|
2
|
+
# flake8: noqa
|
|
3
|
+
|
|
4
|
+
import django.contrib.auth.models
|
|
5
|
+
import django.contrib.auth.validators
|
|
6
|
+
import django.db.models.deletion
|
|
7
|
+
import django.utils.timezone
|
|
8
|
+
from django.db import migrations, models
|
|
9
|
+
|
|
10
|
+
import pfx.pfxcore.fields
|
|
11
|
+
import pfx.pfxcore.models.cache_mixins
|
|
12
|
+
import pfx.pfxcore.models.not_null_fields
|
|
13
|
+
import pfx.pfxcore.models.pfx_models
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Migration(migrations.Migration):
|
|
17
|
+
|
|
18
|
+
initial = True
|
|
19
|
+
|
|
20
|
+
dependencies = [
|
|
21
|
+
('auth', '0012_alter_user_first_name_max_length'),
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
operations = [
|
|
25
|
+
migrations.CreateModel(
|
|
26
|
+
name='Author',
|
|
27
|
+
fields=[
|
|
28
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
29
|
+
('first_name', models.CharField(max_length=30, verbose_name='First Name')),
|
|
30
|
+
('last_name', models.CharField(max_length=30, verbose_name='Last Name')),
|
|
31
|
+
('slug', models.SlugField(unique=True, verbose_name='Slug')),
|
|
32
|
+
('gender', models.CharField(choices=[('male', 'Male'), ('female', 'Female')], default='male', max_length=10, verbose_name='Gender')),
|
|
33
|
+
('science_fiction', models.BooleanField(default=False, verbose_name='Science Fiction')),
|
|
34
|
+
('created_at', models.DateField(auto_now_add=True, verbose_name='Created at')),
|
|
35
|
+
('create_comment', pfx.pfxcore.models.not_null_fields.NotNullCharField(blank=True, max_length=255, verbose_name='Create comment')),
|
|
36
|
+
('update_comment', pfx.pfxcore.models.not_null_fields.NotNullCharField(blank=True, max_length=255, verbose_name='Update comment')),
|
|
37
|
+
('website', pfx.pfxcore.models.not_null_fields.NotNullURLField(blank=True, max_length=255, verbose_name='Website')),
|
|
38
|
+
],
|
|
39
|
+
options={
|
|
40
|
+
'verbose_name': 'Author',
|
|
41
|
+
'verbose_name_plural': 'Authors',
|
|
42
|
+
'ordering': ['last_name', 'first_name', 'pk'],
|
|
43
|
+
},
|
|
44
|
+
bases=(pfx.pfxcore.models.cache_mixins.CacheableMixin, pfx.pfxcore.models.pfx_models.JSONReprMixin, models.Model),
|
|
45
|
+
),
|
|
46
|
+
migrations.CreateModel(
|
|
47
|
+
name='BookType',
|
|
48
|
+
fields=[
|
|
49
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
50
|
+
('name', models.CharField(max_length=30, verbose_name='Name')),
|
|
51
|
+
('slug', models.SlugField(verbose_name='Slug')),
|
|
52
|
+
],
|
|
53
|
+
options={
|
|
54
|
+
'verbose_name': 'Book Type',
|
|
55
|
+
'verbose_name_plural': 'Book Types',
|
|
56
|
+
},
|
|
57
|
+
bases=(pfx.pfxcore.models.cache_mixins.CacheDependsMixin, pfx.pfxcore.models.pfx_models.PFXModelMixin, models.Model),
|
|
58
|
+
),
|
|
59
|
+
migrations.CreateModel(
|
|
60
|
+
name='TestModel',
|
|
61
|
+
fields=[
|
|
62
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
63
|
+
('name', models.CharField(max_length=30, verbose_name='Name')),
|
|
64
|
+
],
|
|
65
|
+
options={
|
|
66
|
+
'verbose_name': 'TestModel',
|
|
67
|
+
'verbose_name_plural': 'TestModels',
|
|
68
|
+
'ordering': ['name', 'pk'],
|
|
69
|
+
},
|
|
70
|
+
bases=(pfx.pfxcore.models.pfx_models.JSONReprMixin, models.Model),
|
|
71
|
+
),
|
|
72
|
+
migrations.CreateModel(
|
|
73
|
+
name='Book',
|
|
74
|
+
fields=[
|
|
75
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
76
|
+
('name', models.CharField(max_length=100, verbose_name='Name')),
|
|
77
|
+
('pub_date', models.DateField(verbose_name='Pub Date')),
|
|
78
|
+
('created_at', models.DateField(auto_now_add=True, verbose_name='Created at')),
|
|
79
|
+
('pages', models.IntegerField(blank=True, null=True, verbose_name='Pages')),
|
|
80
|
+
('rating', models.FloatField(blank=True, null=True, verbose_name='Rating')),
|
|
81
|
+
('reference', models.CharField(blank=True, max_length=30, null=True, verbose_name='Reference')),
|
|
82
|
+
('cover', pfx.pfxcore.fields.MediaField(blank=True, default=dict, max_length=255, verbose_name='Cover')),
|
|
83
|
+
('read_time', pfx.pfxcore.fields.MinutesDurationField(blank=True, null=True, verbose_name='Read Time')),
|
|
84
|
+
('author', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, related_name='books', to='tests.author', verbose_name='Author')),
|
|
85
|
+
('type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='books', to='tests.booktype', verbose_name='Book Type')),
|
|
86
|
+
],
|
|
87
|
+
options={
|
|
88
|
+
'verbose_name': 'Book',
|
|
89
|
+
'verbose_name_plural': 'Books',
|
|
90
|
+
},
|
|
91
|
+
bases=(pfx.pfxcore.models.cache_mixins.CacheDependsMixin, pfx.pfxcore.models.pfx_models.PFXModelMixin, models.Model),
|
|
92
|
+
),
|
|
93
|
+
migrations.AddField(
|
|
94
|
+
model_name='author',
|
|
95
|
+
name='types',
|
|
96
|
+
field=models.ManyToManyField(related_name='authors', to='tests.booktype', verbose_name='Types'),
|
|
97
|
+
),
|
|
98
|
+
migrations.CreateModel(
|
|
99
|
+
name='User',
|
|
100
|
+
fields=[
|
|
101
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
102
|
+
('password', models.CharField(max_length=128, verbose_name='password')),
|
|
103
|
+
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
|
104
|
+
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
|
105
|
+
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
|
|
106
|
+
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
|
|
107
|
+
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
|
|
108
|
+
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
|
|
109
|
+
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
|
110
|
+
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
|
111
|
+
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
|
112
|
+
('otp_secret_token', models.CharField(blank=True, max_length=32, null=True, unique=True, verbose_name='OTP secret token')),
|
|
113
|
+
('otp_secret_token_tmp', models.CharField(blank=True, max_length=32, null=True, verbose_name='Temporary OTP secret token')),
|
|
114
|
+
('hotp_count', models.IntegerField(default=0, verbose_name='HOTP count')),
|
|
115
|
+
('hotp_expiry', models.DateTimeField(default=django.utils.timezone.now, verbose_name='HOTP expiry')),
|
|
116
|
+
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
|
|
117
|
+
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
|
|
118
|
+
],
|
|
119
|
+
options={
|
|
120
|
+
'verbose_name': 'user',
|
|
121
|
+
'verbose_name_plural': 'users',
|
|
122
|
+
},
|
|
123
|
+
bases=(pfx.pfxcore.models.cache_mixins.CacheableMixin, pfx.pfxcore.models.pfx_models.PFXModelMixin, models.Model),
|
|
124
|
+
managers=[
|
|
125
|
+
('objects', django.contrib.auth.models.UserManager()),
|
|
126
|
+
],
|
|
127
|
+
),
|
|
128
|
+
migrations.AddConstraint(
|
|
129
|
+
model_name='book',
|
|
130
|
+
constraint=pfx.pfxcore.models.pfx_models.UniqueConstraint(fields=('author', 'name'), message='%(name)s already exists for %(author)s', name='book_unique_author_and_name'),
|
|
131
|
+
),
|
|
132
|
+
]
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from django.contrib.auth.base_user import BaseUserManager
|
|
2
1
|
from django.core.mail import send_mail
|
|
3
2
|
from django.db import models
|
|
4
3
|
from django.db.models import Q
|
|
@@ -8,7 +7,7 @@ from django.utils.translation import gettext_lazy as _
|
|
|
8
7
|
from pfx.pfxcore.decorator import rest_property
|
|
9
8
|
from pfx.pfxcore.fields import MediaField, MinutesDurationField
|
|
10
9
|
from pfx.pfxcore.models import (
|
|
11
|
-
|
|
10
|
+
AbstractPFXUser,
|
|
12
11
|
CacheableMixin,
|
|
13
12
|
CacheDependsMixin,
|
|
14
13
|
JSONReprMixin,
|
|
@@ -21,61 +20,8 @@ from pfx.pfxcore.models import (
|
|
|
21
20
|
)
|
|
22
21
|
|
|
23
22
|
|
|
24
|
-
class
|
|
25
|
-
|
|
26
|
-
self, username, email, password, first_name, last_name,
|
|
27
|
-
is_superuser=False):
|
|
28
|
-
"""
|
|
29
|
-
Creates and saves a User with the given email and password.
|
|
30
|
-
"""
|
|
31
|
-
if not email:
|
|
32
|
-
raise ValueError('Users must have an email address')
|
|
33
|
-
|
|
34
|
-
user = self.model(
|
|
35
|
-
email=self.normalize_email(email),
|
|
36
|
-
username=username,
|
|
37
|
-
first_name=first_name,
|
|
38
|
-
last_name=last_name,
|
|
39
|
-
is_superuser=is_superuser
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
user.set_password(password)
|
|
43
|
-
user.save(using=self._db)
|
|
44
|
-
return user
|
|
45
|
-
|
|
46
|
-
def create_superuser(
|
|
47
|
-
self, username, email, password, first_name, last_name):
|
|
48
|
-
return self.create_user(
|
|
49
|
-
username, email, password, first_name, last_name,
|
|
50
|
-
is_superuser=True)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
class User(CacheableMixin, JSONReprMixin, OtpUserMixin, AbstractPFXBaseUser):
|
|
54
|
-
username = models.CharField(
|
|
55
|
-
'username',
|
|
56
|
-
max_length=150,
|
|
57
|
-
unique=True,
|
|
58
|
-
error_messages={
|
|
59
|
-
'unique': "A user with that username already exists.",
|
|
60
|
-
},
|
|
61
|
-
)
|
|
62
|
-
first_name = models.CharField('first name', max_length=150, blank=True)
|
|
63
|
-
last_name = models.CharField('last name', max_length=150, blank=True)
|
|
64
|
-
email = models.EmailField('email address', blank=True)
|
|
65
|
-
is_active = models.BooleanField(
|
|
66
|
-
'active',
|
|
67
|
-
default=True,
|
|
68
|
-
)
|
|
69
|
-
is_superuser = models.BooleanField(
|
|
70
|
-
'is_superuser',
|
|
71
|
-
default=False,
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
EMAIL_FIELD = 'email'
|
|
75
|
-
USERNAME_FIELD = 'username'
|
|
76
|
-
REQUIRED_FIELDS = ['email']
|
|
77
|
-
|
|
78
|
-
objects = UserManager()
|
|
23
|
+
class User(CacheableMixin, OtpUserMixin, AbstractPFXUser):
|
|
24
|
+
"""Default user for tests."""
|
|
79
25
|
|
|
80
26
|
class Meta:
|
|
81
27
|
verbose_name = 'user'
|
|
@@ -220,3 +166,16 @@ class Book(CacheDependsMixin, PFXModelMixin, models.Model):
|
|
|
220
166
|
type: string
|
|
221
167
|
"""
|
|
222
168
|
return super().json_repr(author_name=str(self.author), **values)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class TestModel(JSONReprMixin, models.Model):
|
|
172
|
+
|
|
173
|
+
name = models.CharField("Name", max_length=30)
|
|
174
|
+
|
|
175
|
+
class Meta:
|
|
176
|
+
verbose_name = "TestModel"
|
|
177
|
+
verbose_name_plural = "TestModels"
|
|
178
|
+
ordering = ['name', 'pk']
|
|
179
|
+
|
|
180
|
+
def __str__(self):
|
|
181
|
+
return self.name
|
|
File without changes
|
|
@@ -10,6 +10,7 @@ from .test_fields import FieldsTest
|
|
|
10
10
|
from .test_filters import FiltersTest
|
|
11
11
|
from .test_locale_api import LocaleAPITest
|
|
12
12
|
from .test_perm_tests import PermTestsTest
|
|
13
|
+
from .test_permissions import TestPermissions
|
|
13
14
|
from .test_perms_api import PermsAPITest
|
|
14
15
|
from .test_profiling_middleware import ProfilingMiddlewareTest
|
|
15
16
|
from .test_settings import TestSettings
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from django.db import models
|
|
2
1
|
from django.http import HttpResponse
|
|
3
2
|
from django.test import TestCase
|
|
4
3
|
from django.test.utils import override_settings
|
|
@@ -7,23 +6,9 @@ from django.urls import include, path
|
|
|
7
6
|
from pfx.pfxcore import register_views
|
|
8
7
|
from pfx.pfxcore.decorator import rest_api, rest_view
|
|
9
8
|
from pfx.pfxcore.http import JsonResponse
|
|
10
|
-
from pfx.pfxcore.models import JSONReprMixin
|
|
11
9
|
from pfx.pfxcore.test import APIClient, TestAssertMixin
|
|
12
10
|
from pfx.pfxcore.views import RestView
|
|
13
|
-
from tests.models import User
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class TestModel(JSONReprMixin, models.Model):
|
|
17
|
-
|
|
18
|
-
name = models.CharField("Name", max_length=30)
|
|
19
|
-
|
|
20
|
-
class Meta:
|
|
21
|
-
verbose_name = "TestModel"
|
|
22
|
-
verbose_name_plural = "TestModels"
|
|
23
|
-
ordering = ['name', 'pk']
|
|
24
|
-
|
|
25
|
-
def __str__(self):
|
|
26
|
-
return self.name
|
|
11
|
+
from tests.models import TestModel, User
|
|
27
12
|
|
|
28
13
|
|
|
29
14
|
@rest_view("/test")
|