django-pfx 1.4.dev50__tar.gz → 1.4.dev54__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.dev50 → django_pfx-1.4.dev54}/PKG-INFO +1 -1
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/django_pfx.egg-info/PKG-INFO +1 -1
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/django_pfx.egg-info/SOURCES.txt +1 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/doc/source/api.views.rst +83 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/doc/source/generate_openapi.md +49 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/apidoc/parameters.py +4 -4
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/decorator/rest.py +2 -1
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.po +3 -3
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/management/commands/makeapidoc.py +8 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/test.py +6 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/parameters/groups.py +2 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/rest_views.py +48 -5
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/tests/__init__.py +1 -0
- django_pfx-1.4.dev54/tests/tests/test_api_doc_search.py +110 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/.gitignore +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/.gitlab-ci.yml +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/.pre-commit-config.yaml +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/LICENSE +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/MANIFEST.in +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/README.md +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/django_pfx.egg-info/dependency_links.txt +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/django_pfx.egg-info/requires.txt +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/django_pfx.egg-info/top_level.txt +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/doc/Makefile +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/doc/conf.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/doc/index.rst +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/doc/source/authentication.md +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/doc/source/decorator.md +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/doc/source/getting_started.md +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/doc/source/internationalisation.md +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/doc/source/model.md +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/doc/source/pfx_views.md +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/doc/source/profiling.md +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/doc/source/settings.md +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/doc/source/testing.md +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/img/pfx.png +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/img/pfx.svg +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/make_messages +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/manage.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/__init__.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/__init__.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/apidoc/__init__.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/apidoc/schema.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/apidoc/tags.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/apps.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/decorator/__init__.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/default_settings.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/exceptions.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/fields.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/http/__init__.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/http/json_response.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.mo +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/management/__init__.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/management/commands/__init__.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/management/commands/profile.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/middleware/__init__.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/middleware/authentication.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/middleware/locale.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/middleware/profiling.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/migrations/0001_initial.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/migrations/__init__.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/models/__init__.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/models/abstract_pfx_base_user.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/models/cache_mixins.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/models/login_ban.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/models/not_null_fields.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/models/otp_user_mixin.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/models/pfx_models.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/models/pfx_user.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/models/user_filtered_queryset_mixin.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/serializers/__init__.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/serializers/json.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/settings.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/shortcuts.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/storage/__init__.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/storage/s3_storage.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/templates/registration/otp_code_email.txt +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/templates/registration/otp_code_subject.txt +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/templates/registration/password_reset_email.txt +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/templates/registration/password_reset_subject.txt +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/templates/registration/welcome_email.txt +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/templates/registration/welcome_subject.txt +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/urls.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/__init__.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/authentication_views.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/fields.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/filters_views.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/locale_views.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/parameters/__init__.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/parameters/date_format.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/parameters/list_count.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/parameters/list_items.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/parameters/list_mode.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/parameters/list_order.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/parameters/list_search.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/parameters/media_redirect.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/parameters/meta_fields.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/parameters/meta_filters.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/parameters/meta_orders.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/parameters/subset.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/parameters/subset_limit.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/parameters/subset_offset.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/parameters/subset_page.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/parameters/subset_page_size.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/parameters/subset_page_subset.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/settings/__init__.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/settings/dev.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pyproject.toml +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/requirements.txt +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/serve-doc +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/setup.cfg +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/setup.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/__init__.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/locale/fr/LC_MESSAGES/django.po +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/models.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/settings/__init__.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/settings/ci.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/settings/common.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/settings/dev.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/settings/dev_custom_example.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/settings/dev_default.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/tests/basic_api_errors.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/tests/basic_api_test.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/tests/test_api_doc.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/tests/test_auth_api.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/tests/test_body_mixin.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/tests/test_cache.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/tests/test_client.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/tests/test_fields.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/tests/test_filters.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/tests/test_locale_api.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/tests/test_perm_tests.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/tests/test_perms_api.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/tests/test_profiling_middleware.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/tests/test_settings.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/tests/test_shortcuts.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/tests/test_timezone_middleware.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/tests/test_tools.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/tests/test_user_queryset.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/tests/test_view_decorators.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/tests/test_view_fields.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/urls.py +0 -0
- {django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/tests/views.py +0 -0
|
@@ -122,6 +122,7 @@ tests/tests/__init__.py
|
|
|
122
122
|
tests/tests/basic_api_errors.py
|
|
123
123
|
tests/tests/basic_api_test.py
|
|
124
124
|
tests/tests/test_api_doc.py
|
|
125
|
+
tests/tests/test_api_doc_search.py
|
|
125
126
|
tests/tests/test_auth_api.py
|
|
126
127
|
tests/tests/test_body_mixin.py
|
|
127
128
|
tests/tests/test_cache.py
|
|
@@ -47,6 +47,89 @@ API Reference
|
|
|
47
47
|
``pfx.pfxcore.views``
|
|
48
48
|
*********************
|
|
49
49
|
|
|
50
|
+
Query parameters & groups
|
|
51
|
+
-------------------------
|
|
52
|
+
|
|
53
|
+
.. autoclass:: pfx.pfxcore.views.parameters.ListCount
|
|
54
|
+
:members:
|
|
55
|
+
:undoc-members:
|
|
56
|
+
:show-inheritance:
|
|
57
|
+
|
|
58
|
+
.. autoclass:: pfx.pfxcore.views.parameters.ListItems
|
|
59
|
+
:members:
|
|
60
|
+
:undoc-members:
|
|
61
|
+
:show-inheritance:
|
|
62
|
+
|
|
63
|
+
.. autoclass:: pfx.pfxcore.views.parameters.ListMode
|
|
64
|
+
:members:
|
|
65
|
+
:undoc-members:
|
|
66
|
+
:show-inheritance:
|
|
67
|
+
|
|
68
|
+
.. autoclass:: pfx.pfxcore.views.parameters.ListOrder
|
|
69
|
+
:members:
|
|
70
|
+
:undoc-members:
|
|
71
|
+
:show-inheritance:
|
|
72
|
+
|
|
73
|
+
.. autoclass:: pfx.pfxcore.views.parameters.ListSearch
|
|
74
|
+
:members:
|
|
75
|
+
:undoc-members:
|
|
76
|
+
:show-inheritance:
|
|
77
|
+
|
|
78
|
+
.. autoclass:: pfx.pfxcore.views.parameters.MediaRedirect
|
|
79
|
+
:members:
|
|
80
|
+
:undoc-members:
|
|
81
|
+
:show-inheritance:
|
|
82
|
+
|
|
83
|
+
.. autoclass:: pfx.pfxcore.views.parameters.MetaFields
|
|
84
|
+
:members:
|
|
85
|
+
:undoc-members:
|
|
86
|
+
:show-inheritance:
|
|
87
|
+
|
|
88
|
+
.. autoclass:: pfx.pfxcore.views.parameters.groups.MetaFilters
|
|
89
|
+
:members:
|
|
90
|
+
:undoc-members:
|
|
91
|
+
:show-inheritance:
|
|
92
|
+
|
|
93
|
+
.. autoclass:: pfx.pfxcore.views.parameters.groups.MetaOrders
|
|
94
|
+
:members:
|
|
95
|
+
:undoc-members:
|
|
96
|
+
:show-inheritance:
|
|
97
|
+
|
|
98
|
+
.. autoclass:: pfx.pfxcore.views.parameters.groups.SubsetLimit
|
|
99
|
+
:members:
|
|
100
|
+
:undoc-members:
|
|
101
|
+
:show-inheritance:
|
|
102
|
+
|
|
103
|
+
.. autoclass:: pfx.pfxcore.views.parameters.groups.SubsetOffset
|
|
104
|
+
:members:
|
|
105
|
+
:undoc-members:
|
|
106
|
+
:show-inheritance:
|
|
107
|
+
|
|
108
|
+
.. autoclass:: pfx.pfxcore.views.parameters.groups.SubsetPageSize
|
|
109
|
+
:members:
|
|
110
|
+
:undoc-members:
|
|
111
|
+
:show-inheritance:
|
|
112
|
+
|
|
113
|
+
.. autoclass:: pfx.pfxcore.views.parameters.groups.SubsetPageSubset
|
|
114
|
+
:members:
|
|
115
|
+
:undoc-members:
|
|
116
|
+
:show-inheritance:
|
|
117
|
+
|
|
118
|
+
.. autoclass:: pfx.pfxcore.views.parameters.groups.SubsetPage
|
|
119
|
+
:members:
|
|
120
|
+
:undoc-members:
|
|
121
|
+
:show-inheritance:
|
|
122
|
+
|
|
123
|
+
.. autoclass:: pfx.pfxcore.views.parameters.groups.MetaList
|
|
124
|
+
:members:
|
|
125
|
+
:undoc-members:
|
|
126
|
+
:show-inheritance:
|
|
127
|
+
|
|
128
|
+
.. autoclass:: pfx.pfxcore.views.parameters.groups.List
|
|
129
|
+
:members:
|
|
130
|
+
:undoc-members:
|
|
131
|
+
:show-inheritance:
|
|
132
|
+
|
|
50
133
|
Base services
|
|
51
134
|
-------------
|
|
52
135
|
|
|
@@ -158,6 +158,55 @@ from .parameters import ListGroup
|
|
|
158
158
|
@rest_api("/", method="get", parameters=[ListGroup])
|
|
159
159
|
```
|
|
160
160
|
|
|
161
|
+
#### Pre defined query parameters
|
|
162
|
+
|
|
163
|
+
A number of predefined parameters and groups are used by view mixins, and are reusable and inheritable:
|
|
164
|
+
See [API reference | Query parameters & groups](api.views.rst#query-parameters-groups) for details.
|
|
165
|
+
|
|
166
|
+
For {class}`pfx.pfxcore.views.parameters.ListSearch`, you can redefine the behaviour by annotation:
|
|
167
|
+
either with `@rest_doc` on the view (useful if the view inherits from {class}`pfx.pfxcore.views.ListRestViewMixin`),
|
|
168
|
+
or with `@rest_api` on the service (useful if you reuse the {class}`pfx.pfxcore.views.parameters.ListSearch` parameter
|
|
169
|
+
or the {class}`pfx.pfxcore.views.parameters.groups.List` group).
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
from pfx.pfxcore.decorator.rest import rest_doc, rest_view
|
|
173
|
+
from pfx.pfxcore.views import BaseRestView, ListRestViewMixin
|
|
174
|
+
|
|
175
|
+
# Do not add the search param in api doc:
|
|
176
|
+
@rest_doc("", "get", search=False)
|
|
177
|
+
@rest_view("/my-view")
|
|
178
|
+
class MyView(ListRestViewMixin, BaseRestView):
|
|
179
|
+
pass
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
from pfx.pfxcore.decorator.rest import rest_doc, rest_view
|
|
184
|
+
from pfx.pfxcore.views import BaseRestView, ListRestViewMixin
|
|
185
|
+
|
|
186
|
+
# Customize the search param description in api doc:
|
|
187
|
+
@rest_doc("", "get", search="Search the string in name and summary fields.")
|
|
188
|
+
@rest_view("/my-view")
|
|
189
|
+
class MyView(ListRestViewMixin, BaseRestView):
|
|
190
|
+
pass
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
from pfx.pfxcore.decorator.rest import rest_doc, rest_view
|
|
195
|
+
from pfx.pfxcore.views import BaseRestView
|
|
196
|
+
|
|
197
|
+
@rest_view("/custom")
|
|
198
|
+
class MyView(BaseRestView):
|
|
199
|
+
|
|
200
|
+
# Use parameters.groups.List with custom description for search param:
|
|
201
|
+
@rest_api(
|
|
202
|
+
"", method="get",
|
|
203
|
+
parameters=[parameters.groups.List],
|
|
204
|
+
search="A custom description.")
|
|
205
|
+
def get(self, *args, **kwargs):
|
|
206
|
+
return JsonResponse({})
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
|
|
161
210
|
### Body and response schema
|
|
162
211
|
|
|
163
212
|
When using standard mixins to provide basic Rest services, the body
|
|
@@ -21,12 +21,12 @@ class Parameter:
|
|
|
21
21
|
return cls.__name__
|
|
22
22
|
|
|
23
23
|
@classmethod
|
|
24
|
-
def as_parameter(cls):
|
|
24
|
+
def as_parameter(cls, doc=None):
|
|
25
25
|
res = dict(name=cls.name)
|
|
26
26
|
res['in'] = cls.location
|
|
27
|
-
|
|
28
|
-
if
|
|
29
|
-
res['description'] =
|
|
27
|
+
description = doc or inspect.getdoc(cls)
|
|
28
|
+
if description:
|
|
29
|
+
res['description'] = description
|
|
30
30
|
members = inspect.getmembers(
|
|
31
31
|
cls, predicate=lambda x: not (
|
|
32
32
|
inspect.ismethod(x)))
|
|
@@ -17,7 +17,7 @@ def rest_api(
|
|
|
17
17
|
path, method='get', public=None, priority=0, priority_doc=0,
|
|
18
18
|
parameters=None,
|
|
19
19
|
request_schema=None, response_schema=None, filters=False,
|
|
20
|
-
groups=None):
|
|
20
|
+
search=False, groups=None):
|
|
21
21
|
def decorator(func):
|
|
22
22
|
@wraps(func)
|
|
23
23
|
def wrapper(self, request, *args, **kwargs):
|
|
@@ -47,6 +47,7 @@ def rest_api(
|
|
|
47
47
|
wrapper.rest_api_request_schema = request_schema
|
|
48
48
|
wrapper.rest_api_response_schema = response_schema
|
|
49
49
|
wrapper.rest_api_filters = filters
|
|
50
|
+
wrapper.rest_api_search = search
|
|
50
51
|
wrapper.rest_api_groups = set(groups or [])
|
|
51
52
|
wrapper.rest_api_public = public
|
|
52
53
|
return wrapper
|
|
@@ -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: 2024-
|
|
10
|
+
"POT-Creation-Date: 2024-08-20 12:22+0200\n"
|
|
11
11
|
"PO-Revision-Date: 2021-06-22 23:31+0200\n"
|
|
12
12
|
"Last-Translator: \n"
|
|
13
13
|
"Language-Team: \n"
|
|
@@ -275,11 +275,11 @@ msgstr "{model} {obj} créé."
|
|
|
275
275
|
msgid "{model} {obj} updated."
|
|
276
276
|
msgstr "{model} {obj} modifié."
|
|
277
277
|
|
|
278
|
-
#: views/rest_views.py:
|
|
278
|
+
#: views/rest_views.py:1077
|
|
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:1169 views/rest_views.py:1209
|
|
284
284
|
msgid "Unexpected storage error"
|
|
285
285
|
msgstr "Erreur de stockage inattendue"
|
|
@@ -63,6 +63,14 @@ def global_parameters(spec, method):
|
|
|
63
63
|
p.get('name', '#N/A') for p in spec.get('parameters', [])
|
|
64
64
|
if isinstance(p, dict)}
|
|
65
65
|
for qp in extend_groups(method.rest_api_params):
|
|
66
|
+
if qp.name == 'search':
|
|
67
|
+
search = (
|
|
68
|
+
spec['search'] if 'search' in spec else method.rest_api_search)
|
|
69
|
+
if not search:
|
|
70
|
+
continue
|
|
71
|
+
if isinstance(search, str):
|
|
72
|
+
yield qp.as_parameter(doc=search)
|
|
73
|
+
continue
|
|
66
74
|
if qp.name in existings:
|
|
67
75
|
# Ignore path parameters that are manually described.
|
|
68
76
|
continue
|
|
@@ -255,6 +255,12 @@ class TestAssertMixin:
|
|
|
255
255
|
return self.assertIn(
|
|
256
256
|
element, self.get_val(src, key, msg=msg), msg=msg)
|
|
257
257
|
|
|
258
|
+
# assert JSON array contains
|
|
259
|
+
def assertJNotIn(self, src, key, element, msg=None):
|
|
260
|
+
msg = '\n'.join([msg or '', self.format_json(src)])
|
|
261
|
+
return self.assertNotIn(
|
|
262
|
+
element, self.get_val(src, key, msg=msg), msg=msg)
|
|
263
|
+
|
|
258
264
|
|
|
259
265
|
class TestPermsAssertMixin(TestAssertMixin):
|
|
260
266
|
USER_TESTS = {}
|
|
@@ -26,10 +26,12 @@ class ModelSerialization(ParameterGroup):
|
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
class MetaList(ParameterGroup):
|
|
29
|
+
"""Parameters group for meta list services."""
|
|
29
30
|
parameters = [MetaFields, MetaFilters, MetaOrders]
|
|
30
31
|
|
|
31
32
|
|
|
32
33
|
class List(ParameterGroup):
|
|
34
|
+
"""Parameters group for list services."""
|
|
33
35
|
parameters = [
|
|
34
36
|
ListCount, ListItems, ListSearch, ListOrder, ListMode,
|
|
35
37
|
Subset, SubsetPage, SubsetPageSize,
|
|
@@ -808,7 +808,8 @@ class ListRestViewMixin(ModelResponseMixin):
|
|
|
808
808
|
return JsonResponse(res)
|
|
809
809
|
|
|
810
810
|
@rest_api(
|
|
811
|
-
"", method="get", parameters=[parameters.groups.List],
|
|
811
|
+
"", method="get", parameters=[parameters.groups.List],
|
|
812
|
+
filters=True, search=True,
|
|
812
813
|
response_schema='model_list_schema', priority_doc=-20)
|
|
813
814
|
def get_list(self, *args, **kwargs):
|
|
814
815
|
"""Entrypoint for :code:`GET /` route.
|
|
@@ -1123,11 +1124,41 @@ class MediaRestViewMixin(ModelMixin):
|
|
|
1123
1124
|
---
|
|
1124
1125
|
get:
|
|
1125
1126
|
summary: Get upload URL
|
|
1126
|
-
description:
|
|
1127
|
+
description: |
|
|
1128
|
+
Get upload URL for a `MediaField` field.
|
|
1129
|
+
|
|
1130
|
+
You can upload a file ont the received URL. When the upload
|
|
1131
|
+
query is done, you have to confirm the process with an
|
|
1132
|
+
update request (`PUT`) on {model}. The body of this request
|
|
1133
|
+
must contain the name of the `MediaField` with the contents
|
|
1134
|
+
of the `file` value in the response of this request.
|
|
1135
|
+
|
|
1136
|
+
1. `GET /<int:pk>/<str:field>/upload-url/<str:filename>`
|
|
1137
|
+
→ `data`
|
|
1138
|
+
2. `PUT data.url aFile Content-Type: aFile.type`
|
|
1139
|
+
3. `PUT /<int:pk> {{field: data.file}}`
|
|
1127
1140
|
parameters extras:
|
|
1128
|
-
pk:
|
|
1129
|
-
field:
|
|
1130
|
-
|
|
1141
|
+
pk: The {model} pk.
|
|
1142
|
+
field: The {model} field name. Must be the name of
|
|
1143
|
+
a `MediaField` field.
|
|
1144
|
+
filename: The desired filename.
|
|
1145
|
+
responses:
|
|
1146
|
+
200:
|
|
1147
|
+
description: The upload URL
|
|
1148
|
+
content:
|
|
1149
|
+
application/json:
|
|
1150
|
+
schema:
|
|
1151
|
+
properties:
|
|
1152
|
+
url:
|
|
1153
|
+
type: string
|
|
1154
|
+
format: uri
|
|
1155
|
+
file:
|
|
1156
|
+
type: object
|
|
1157
|
+
properties:
|
|
1158
|
+
name:
|
|
1159
|
+
type: string
|
|
1160
|
+
key:
|
|
1161
|
+
type: string
|
|
1131
1162
|
"""
|
|
1132
1163
|
obj = self.get_object(pk=pk)
|
|
1133
1164
|
try:
|
|
@@ -1157,6 +1188,18 @@ class MediaRestViewMixin(ModelMixin):
|
|
|
1157
1188
|
parameters extras:
|
|
1158
1189
|
pk: the {model} pk
|
|
1159
1190
|
field: the {model} field name
|
|
1191
|
+
responses:
|
|
1192
|
+
200:
|
|
1193
|
+
description: The file URL, if `redirect` is `false`.
|
|
1194
|
+
content:
|
|
1195
|
+
application/json:
|
|
1196
|
+
schema:
|
|
1197
|
+
properties:
|
|
1198
|
+
url:
|
|
1199
|
+
type: string
|
|
1200
|
+
format: uri
|
|
1201
|
+
302:
|
|
1202
|
+
description: The redirect, if `redirect` is `true`.
|
|
1160
1203
|
"""
|
|
1161
1204
|
obj = self.get_object(pk=pk)
|
|
1162
1205
|
try:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from .basic_api_errors import BasicAPIErrorTest
|
|
2
2
|
from .basic_api_test import BasicAPITest
|
|
3
3
|
from .test_api_doc import ApiDocTest
|
|
4
|
+
from .test_api_doc_search import TestAPIDocSearch
|
|
4
5
|
from .test_auth_api import AuthAPITest
|
|
5
6
|
from .test_body_mixin import TestBodyMixin
|
|
6
7
|
from .test_cache import TestCache
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
from django.http import JsonResponse
|
|
2
|
+
from django.test import TestCase, override_settings
|
|
3
|
+
from django.urls import include, path
|
|
4
|
+
|
|
5
|
+
from pfx.pfxcore.decorator.rest import rest_api, rest_doc, rest_view
|
|
6
|
+
from pfx.pfxcore.management.commands.makeapidoc import get_spec
|
|
7
|
+
from pfx.pfxcore.shortcuts import register_views
|
|
8
|
+
from pfx.pfxcore.test import APIClient, TestAssertMixin
|
|
9
|
+
from pfx.pfxcore.views import (
|
|
10
|
+
BaseRestView,
|
|
11
|
+
ListRestViewMixin,
|
|
12
|
+
ModelResponseMixin,
|
|
13
|
+
parameters,
|
|
14
|
+
)
|
|
15
|
+
from tests.models import Author
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@rest_view("/list/default")
|
|
19
|
+
class DefaultListView(ListRestViewMixin, ModelResponseMixin, BaseRestView):
|
|
20
|
+
model = Author
|
|
21
|
+
default_public = True
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@rest_doc("", "get", search=False)
|
|
25
|
+
@rest_view("/list/no-search")
|
|
26
|
+
class NoSearchListView(ListRestViewMixin, ModelResponseMixin, BaseRestView):
|
|
27
|
+
model = Author
|
|
28
|
+
default_public = True
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@rest_doc("", "get", search="A custom description.")
|
|
32
|
+
@rest_view("/list/custom-description")
|
|
33
|
+
class CustomDescriptionListView(
|
|
34
|
+
ListRestViewMixin, ModelResponseMixin, BaseRestView):
|
|
35
|
+
model = Author
|
|
36
|
+
default_public = True
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@rest_view("/custom")
|
|
40
|
+
class CustomServiceView(BaseRestView):
|
|
41
|
+
model = Author
|
|
42
|
+
default_public = True
|
|
43
|
+
|
|
44
|
+
@rest_api(
|
|
45
|
+
"/default", method="get", parameters=[parameters.groups.List],
|
|
46
|
+
search=True)
|
|
47
|
+
def get_default(self, *args, **kwargs):
|
|
48
|
+
return JsonResponse({})
|
|
49
|
+
|
|
50
|
+
@rest_api(
|
|
51
|
+
"/no-search", method="get", parameters=[parameters.groups.List],
|
|
52
|
+
search=False)
|
|
53
|
+
def get_no_search(self, *args, **kwargs):
|
|
54
|
+
return JsonResponse({})
|
|
55
|
+
|
|
56
|
+
@rest_api(
|
|
57
|
+
"/custom-description", method="get",
|
|
58
|
+
parameters=[parameters.groups.List],
|
|
59
|
+
search="A custom description.")
|
|
60
|
+
def get_custom_description(self, *args, **kwargs):
|
|
61
|
+
return JsonResponse({})
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
urlpatterns = [
|
|
65
|
+
path('api/', include(register_views(
|
|
66
|
+
DefaultListView,
|
|
67
|
+
NoSearchListView,
|
|
68
|
+
CustomDescriptionListView,
|
|
69
|
+
CustomServiceView))),
|
|
70
|
+
path('api/', include('pfx.pfxcore.urls'))
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@override_settings(ROOT_URLCONF=__name__)
|
|
75
|
+
class TestAPIDocSearch(TestAssertMixin, TestCase):
|
|
76
|
+
def setUp(self):
|
|
77
|
+
self.client = APIClient(default_locale='en')
|
|
78
|
+
|
|
79
|
+
def test_body_to_model(self):
|
|
80
|
+
def get_params(spec, path):
|
|
81
|
+
for p in self.get_val(spec, f'paths.{path}.get.parameters'):
|
|
82
|
+
yield p.get('$ref') or p.get('name'), p
|
|
83
|
+
|
|
84
|
+
spec = get_spec(set()).to_dict()
|
|
85
|
+
|
|
86
|
+
params = dict(get_params(spec, '/list/default'))
|
|
87
|
+
self.assertJEExists(params, '#/components/parameters/ListSearch')
|
|
88
|
+
self.assertJENotExists(params, 'search')
|
|
89
|
+
|
|
90
|
+
params = dict(get_params(spec, '/list/no-search'))
|
|
91
|
+
self.assertJENotExists(params, '#/components/parameters/ListSearch')
|
|
92
|
+
self.assertJENotExists(params, 'search')
|
|
93
|
+
|
|
94
|
+
params = dict(get_params(spec, '/list/custom-description'))
|
|
95
|
+
self.assertJENotExists(params, '#/components/parameters/ListSearch')
|
|
96
|
+
self.assertJEExists(params, 'search')
|
|
97
|
+
self.assertJE(params, 'search.description', "A custom description.")
|
|
98
|
+
|
|
99
|
+
params = dict(get_params(spec, '/custom/default'))
|
|
100
|
+
self.assertJEExists(params, '#/components/parameters/ListSearch')
|
|
101
|
+
self.assertJENotExists(params, 'search')
|
|
102
|
+
|
|
103
|
+
params = dict(get_params(spec, '/custom/no-search'))
|
|
104
|
+
self.assertJENotExists(params, '#/components/parameters/ListSearch')
|
|
105
|
+
self.assertJENotExists(params, 'search')
|
|
106
|
+
|
|
107
|
+
params = dict(get_params(spec, '/custom/custom-description'))
|
|
108
|
+
self.assertJENotExists(params, '#/components/parameters/ListSearch')
|
|
109
|
+
self.assertJEExists(params, 'search')
|
|
110
|
+
self.assertJE(params, 'search.description', "A custom description.")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/models/user_filtered_queryset_mixin.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/templates/registration/otp_code_email.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/templates/registration/welcome_email.txt
RENAMED
|
File without changes
|
{django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/templates/registration/welcome_subject.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/parameters/media_redirect.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/parameters/subset_page_size.py
RENAMED
|
File without changes
|
{django_pfx-1.4.dev50 → django_pfx-1.4.dev54}/pfx/pfxcore/views/parameters/subset_page_subset.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|