django-pfx 1.2.dev107__tar.gz → 1.2.dev110__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.2.dev107 → django-pfx-1.2.dev110}/PKG-INFO +6 -8
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/README.md +1 -4
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/django_pfx.egg-info/PKG-INFO +6 -8
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/django_pfx.egg-info/requires.txt +1 -1
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/doc/index.rst +1 -7
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/doc/source/decorator.md +18 -6
- django-pfx-1.2.dev110/doc/source/getting_started.md +179 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/doc/source/internationalisation.md +1 -1
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/doc/source/model.md +21 -13
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/doc/source/pfx_views.md +49 -21
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/doc/source/testing.md +40 -13
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/views/authentication_views.py +3 -1
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/setup.cfg +5 -4
- django-pfx-1.2.dev107/doc/source/getting_started.md +0 -81
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/.gitignore +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/.gitlab-ci.yml +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/.pre-commit-config.yaml +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/LICENSE +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/MANIFEST.in +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/django_pfx.egg-info/SOURCES.txt +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/django_pfx.egg-info/dependency_links.txt +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/django_pfx.egg-info/top_level.txt +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/doc/Makefile +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/doc/conf.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/doc/source/api.views.rst +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/doc/source/authentication.md +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/doc/source/cookbook.md +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/img/pfx.png +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/img/pfx.svg +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/__init__.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/__init__.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/apidoc/__init__.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/apidoc/parameters.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/apidoc/schema.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/apidoc/tags.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/apps.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/decorator/__init__.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/decorator/rest.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/default_settings.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/exceptions.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/fields.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/http/__init__.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/http/json_response.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.mo +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.po +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/management/__init__.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/management/commands/__init__.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/management/commands/makeapidoc.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/middleware/__init__.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/middleware/authentication.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/middleware/locale.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/models/__init__.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/models/cache_mixins.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/models/not_null_fields.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/models/pfx_models.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/models/user_filtered_queryset_mixin.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/serializers/__init__.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/serializers/json.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/settings.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/shortcuts.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/storage/__init__.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/storage/s3_storage.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/templates/registration/password_reset_email.txt +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/templates/registration/password_reset_subject.txt +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/templates/registration/welcome_email.txt +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/templates/registration/welcome_subject.txt +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/test.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/urls.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/views/__init__.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/views/fields.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/views/filters_views.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/views/locale_views.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/views/parameters/__init__.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/views/parameters/date_format.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/views/parameters/groups.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/views/parameters/list_count.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/views/parameters/list_items.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/views/parameters/list_mode.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/views/parameters/list_order.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/views/parameters/list_search.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/views/parameters/media_redirect.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/views/parameters/meta_fields.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/views/parameters/meta_filters.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/views/parameters/meta_orders.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/views/parameters/subset.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/views/parameters/subset_limit.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/views/parameters/subset_offset.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/views/parameters/subset_page.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/views/parameters/subset_page_size.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/views/parameters/subset_page_subset.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pfx/pfxcore/views/rest_views.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/pyproject.toml +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/requirements.txt +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/runtest.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/setup.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/__init__.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/models.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/settings/__init__.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/settings/ci.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/settings/common.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/settings/dev.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/settings/dev_custom_example.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/settings/dev_default.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/tests/__init__.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/tests/basic_api_errors.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/tests/basic_api_test.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/tests/test_api_doc.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/tests/test_auth_api.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/tests/test_cache.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/tests/test_fields.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/tests/test_filters.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/tests/test_locale_api.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/tests/test_perm_tests.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/tests/test_perms_api.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/tests/test_shortcuts.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/tests/test_timezone_middleware.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/tests/test_tools.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/tests/test_user_queryset.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/tests/test_view_decorators.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/tests/test_view_fields.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/urls.py +0 -0
- {django-pfx-1.2.dev107 → django-pfx-1.2.dev110}/tests/views.py +0 -0
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: django-pfx
|
|
3
|
-
Version: 1.2.
|
|
4
|
-
Summary: Django PFX is a toolkit to build
|
|
3
|
+
Version: 1.2.dev110
|
|
4
|
+
Summary: Django PFX is a toolkit to build rest APIs with the Django framework.
|
|
5
5
|
Author: Hervé Martinet
|
|
6
6
|
Author-email: herve.martinet@gmail.com
|
|
7
7
|
License: BSD-3-Clause
|
|
8
|
-
Project-URL: Source, https://gitlab.com/
|
|
9
|
-
Project-URL: Tracker, https://gitlab.com/
|
|
8
|
+
Project-URL: Source, https://gitlab.com/pfx4/django-pfx
|
|
9
|
+
Project-URL: Tracker, https://gitlab.com/pfx4/django-pfx/-/issues
|
|
10
|
+
Project-URL: Documentation, https://pfx4.gitlab.io/django-pfx/doc/
|
|
10
11
|
Classifier: Environment :: Web Environment
|
|
11
12
|
Classifier: Framework :: Django
|
|
12
13
|
Classifier: Framework :: Django :: 3.2
|
|
@@ -31,11 +32,8 @@ License-File: LICENSE
|
|
|
31
32
|
|
|
32
33
|
![logo]
|
|
33
34
|
|
|
34
|
-
Django PFX is a toolkit to build
|
|
35
|
+
Django PFX is a toolkit to build rest APIs with the Django framework.
|
|
35
36
|
|
|
36
|
-
> **WARNING**
|
|
37
|
-
>
|
|
38
|
-
> This is an experimental projet. It should not be used in real project because it is incomplete and there is no warranty of keeping compatibility in future version.
|
|
39
37
|
|
|
40
38
|
[logo]: https://gitlab.com/hmartinet/django-pfx/-/raw/master/img/pfx.png "PFX"
|
|
41
39
|
[pipeline]: https://gitlab.com/hmartinet/django-pfx/badges/master/pipeline.svg "Pipeline Status"
|
|
@@ -5,11 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
![logo]
|
|
7
7
|
|
|
8
|
-
Django PFX is a toolkit to build
|
|
8
|
+
Django PFX is a toolkit to build rest APIs with the Django framework.
|
|
9
9
|
|
|
10
|
-
> **WARNING**
|
|
11
|
-
>
|
|
12
|
-
> This is an experimental projet. It should not be used in real project because it is incomplete and there is no warranty of keeping compatibility in future version.
|
|
13
10
|
|
|
14
11
|
[logo]: https://gitlab.com/hmartinet/django-pfx/-/raw/master/img/pfx.png "PFX"
|
|
15
12
|
[pipeline]: https://gitlab.com/hmartinet/django-pfx/badges/master/pipeline.svg "Pipeline Status"
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: django-pfx
|
|
3
|
-
Version: 1.2.
|
|
4
|
-
Summary: Django PFX is a toolkit to build
|
|
3
|
+
Version: 1.2.dev110
|
|
4
|
+
Summary: Django PFX is a toolkit to build rest APIs with the Django framework.
|
|
5
5
|
Author: Hervé Martinet
|
|
6
6
|
Author-email: herve.martinet@gmail.com
|
|
7
7
|
License: BSD-3-Clause
|
|
8
|
-
Project-URL: Source, https://gitlab.com/
|
|
9
|
-
Project-URL: Tracker, https://gitlab.com/
|
|
8
|
+
Project-URL: Source, https://gitlab.com/pfx4/django-pfx
|
|
9
|
+
Project-URL: Tracker, https://gitlab.com/pfx4/django-pfx/-/issues
|
|
10
|
+
Project-URL: Documentation, https://pfx4.gitlab.io/django-pfx/doc/
|
|
10
11
|
Classifier: Environment :: Web Environment
|
|
11
12
|
Classifier: Framework :: Django
|
|
12
13
|
Classifier: Framework :: Django :: 3.2
|
|
@@ -31,11 +32,8 @@ License-File: LICENSE
|
|
|
31
32
|
|
|
32
33
|
![logo]
|
|
33
34
|
|
|
34
|
-
Django PFX is a toolkit to build
|
|
35
|
+
Django PFX is a toolkit to build rest APIs with the Django framework.
|
|
35
36
|
|
|
36
|
-
> **WARNING**
|
|
37
|
-
>
|
|
38
|
-
> This is an experimental projet. It should not be used in real project because it is incomplete and there is no warranty of keeping compatibility in future version.
|
|
39
37
|
|
|
40
38
|
[logo]: https://gitlab.com/hmartinet/django-pfx/-/raw/master/img/pfx.png "PFX"
|
|
41
39
|
[pipeline]: https://gitlab.com/hmartinet/django-pfx/badges/master/pipeline.svg "Pipeline Status"
|
|
@@ -6,13 +6,7 @@
|
|
|
6
6
|
Welcome to Django PFX's documentation!
|
|
7
7
|
======================================
|
|
8
8
|
|
|
9
|
-
Django PFX is a toolkit to build
|
|
10
|
-
to be used by React progressive web app.
|
|
11
|
-
|
|
12
|
-
.. warning::
|
|
13
|
-
This is a project in development.
|
|
14
|
-
It should not be used in real project because it is incomplete
|
|
15
|
-
and changes may break the compatibility in future version.
|
|
9
|
+
Django PFX is a toolkit to build rest APIs with the Django framework.
|
|
16
10
|
|
|
17
11
|
|
|
18
12
|
Topics
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# View decorator and URL
|
|
2
2
|
## @rest_view
|
|
3
3
|
Used to provide the base path of a view class
|
|
4
|
-
```
|
|
4
|
+
```python
|
|
5
|
+
from pfx.pfxcore.decorator import rest_view
|
|
6
|
+
|
|
5
7
|
@rest_view("/base-path")
|
|
6
8
|
class ViewClass():
|
|
7
9
|
```
|
|
@@ -9,13 +11,18 @@ class ViewClass():
|
|
|
9
11
|
## @rest_api
|
|
10
12
|
Used to annotate the rest services method.
|
|
11
13
|
Parameters are the path and the HTTP method.
|
|
12
|
-
```
|
|
14
|
+
```python
|
|
15
|
+
from pfx.pfxcore.decorator import rest_api
|
|
16
|
+
|
|
13
17
|
@rest_api("/path", method="get")
|
|
14
18
|
def class_method(self):
|
|
15
19
|
```
|
|
16
20
|
|
|
17
21
|
A short example
|
|
18
|
-
```
|
|
22
|
+
```python
|
|
23
|
+
from pfx.pfxcore.decorator import rest_view, rest_api
|
|
24
|
+
from pfx.pfxcore.http import JsonResponse
|
|
25
|
+
|
|
19
26
|
@rest_view("/books")
|
|
20
27
|
class BookRestView():
|
|
21
28
|
|
|
@@ -27,7 +34,9 @@ class BookRestView():
|
|
|
27
34
|
|
|
28
35
|
### path parameters
|
|
29
36
|
Path can contain parameters that are passed to the method.
|
|
30
|
-
```
|
|
37
|
+
```python
|
|
38
|
+
from pfx.pfxcore.decorator import rest_api
|
|
39
|
+
|
|
31
40
|
@rest_api("/path/<int:pk>/test/<slug:slug>", method="get")
|
|
32
41
|
def class_method(self, pk, slug):
|
|
33
42
|
```
|
|
@@ -35,7 +44,8 @@ def class_method(self, pk, slug):
|
|
|
35
44
|
## Registering urls
|
|
36
45
|
To be available, annotated view class must be registered
|
|
37
46
|
in your urls.py file as follows.
|
|
38
|
-
|
|
47
|
+
|
|
48
|
+
```python
|
|
39
49
|
from pfx.pfxcore import register_views
|
|
40
50
|
|
|
41
51
|
from . import views
|
|
@@ -44,9 +54,11 @@ urlpatterns = register_views(
|
|
|
44
54
|
views.AuthorRestView,
|
|
45
55
|
views.BookRestView)
|
|
46
56
|
```
|
|
57
|
+
|
|
47
58
|
You can include multiple views under one path, or add a path
|
|
48
59
|
wih a specific class method for each HTTP methods:
|
|
49
|
-
```
|
|
60
|
+
```python
|
|
61
|
+
from django.urls import include, path
|
|
50
62
|
from pfx.pfxcore import register_views
|
|
51
63
|
|
|
52
64
|
from . import views
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# Getting Started with PFX
|
|
2
|
+
|
|
3
|
+
## Install django pfx
|
|
4
|
+
|
|
5
|
+
Using pip
|
|
6
|
+
```bash
|
|
7
|
+
pip install django-pfx
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Configuration
|
|
11
|
+
|
|
12
|
+
Add pfxcore to the installed app
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
INSTALLED_APPS = [
|
|
16
|
+
'pfx.pfxcore',
|
|
17
|
+
]
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Create your services
|
|
21
|
+
|
|
22
|
+
### Model class
|
|
23
|
+
Create a simple model class.
|
|
24
|
+
```python
|
|
25
|
+
from django.db import models
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Book(models.Model):
|
|
29
|
+
BOOK_TYPES = [
|
|
30
|
+
('science_fiction', 'Science Fiction'),
|
|
31
|
+
('heroic_fantasy', 'Heroic Fantasy'),
|
|
32
|
+
('detective', 'Detective')]
|
|
33
|
+
|
|
34
|
+
title = models.CharField("Title", max_length=30)
|
|
35
|
+
author = models.CharField("Author", max_length=150)
|
|
36
|
+
type = models.CharField("Type", max_length=20, choices=BOOK_TYPES)
|
|
37
|
+
pub_date = models.DateField("Pub Date")
|
|
38
|
+
created_at = models.DateField("Created at", auto_now_add=True)
|
|
39
|
+
|
|
40
|
+
class Meta:
|
|
41
|
+
verbose_name = "Book"
|
|
42
|
+
verbose_name_plural = "Books"
|
|
43
|
+
|
|
44
|
+
def __str__(self):
|
|
45
|
+
return f"{self.name}"
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Views
|
|
50
|
+
Create a new view
|
|
51
|
+
```python
|
|
52
|
+
from pfx.pfxcore.decorator import rest_view
|
|
53
|
+
from pfx.pfxcore.views import RestView
|
|
54
|
+
|
|
55
|
+
from book.models import Book
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@rest_view("/books")
|
|
59
|
+
class BookRestView(RestView):
|
|
60
|
+
default_public = True
|
|
61
|
+
queryset = Book.objects
|
|
62
|
+
fields = ['title', 'author', 'type', 'pub_date', 'created_at']
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### URLs
|
|
66
|
+
Register the url in urls.py.
|
|
67
|
+
```python
|
|
68
|
+
from django.urls import path, include
|
|
69
|
+
from pfx.pfxcore import register_views
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
from book import views
|
|
73
|
+
|
|
74
|
+
apipatterns = register_views(views.BookRestView)
|
|
75
|
+
|
|
76
|
+
urlpatterns = [
|
|
77
|
+
path('api/', include(apipatterns)),
|
|
78
|
+
]
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
You now have a fully functional public API for the book objet.
|
|
82
|
+
|
|
83
|
+
### Test the API
|
|
84
|
+
The next step is to create a test class to test your new API.
|
|
85
|
+
PFX provides [some tools](testing.md) to ease the testing.
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from datetime import date
|
|
89
|
+
from django.test import TransactionTestCase
|
|
90
|
+
from pfx.pfxcore.test import TestAssertMixin, APIClient
|
|
91
|
+
|
|
92
|
+
from book.models import Book, Author
|
|
93
|
+
|
|
94
|
+
class BookTestClass(TestAssertMixin, TransactionTestCase):
|
|
95
|
+
|
|
96
|
+
@classmethod
|
|
97
|
+
def setUpTestData(cls):
|
|
98
|
+
# create some test data
|
|
99
|
+
cls.author = Author.objects.create(
|
|
100
|
+
first_name='Isaac',
|
|
101
|
+
last_name='Asimov')
|
|
102
|
+
cls.book1 = Book.objects.create(
|
|
103
|
+
author=cls.author,
|
|
104
|
+
title="The Caves of Steel",
|
|
105
|
+
type='science_fiction',
|
|
106
|
+
pub_date=date(1954, 1, 1))
|
|
107
|
+
cls.book2 = Book.objects.create(
|
|
108
|
+
author=cls.author,
|
|
109
|
+
title="The Naked Sun",
|
|
110
|
+
type='science_fiction',
|
|
111
|
+
pub_date=date(1957, 1, 1))
|
|
112
|
+
|
|
113
|
+
def test_get_book_list(self):
|
|
114
|
+
client = APIClient()
|
|
115
|
+
response = client.get('/api/books')
|
|
116
|
+
|
|
117
|
+
# assert response status is 200 OK
|
|
118
|
+
self.assertRC(response, 200)
|
|
119
|
+
# assert number of item returned
|
|
120
|
+
self.assertSize(response, 'items', 2)
|
|
121
|
+
# test the author of the second item is Asimov
|
|
122
|
+
self.assertJE(response, 'items.@1.author.pk', self.author.pk)
|
|
123
|
+
|
|
124
|
+
def test_get_book(self):
|
|
125
|
+
client = APIClient()
|
|
126
|
+
response = client.get(f'/api/books/{self.book2.pk}')
|
|
127
|
+
|
|
128
|
+
# assert response status is 200 OK
|
|
129
|
+
self.assertRC(response, 200)
|
|
130
|
+
# assert json content of the response
|
|
131
|
+
self.assertJE(response, 'title', "The Naked Sun")
|
|
132
|
+
self.assertJE(response, 'pub_date', "1957-01-01")
|
|
133
|
+
|
|
134
|
+
def test_create_book(self):
|
|
135
|
+
client = APIClient()
|
|
136
|
+
response = client.post(
|
|
137
|
+
'/api/books/',dict(
|
|
138
|
+
title="The Robots of Dawn",
|
|
139
|
+
author=self.author,
|
|
140
|
+
type='science_fiction',
|
|
141
|
+
pub_date=date(1983, 1, 1)
|
|
142
|
+
))
|
|
143
|
+
self.assertRC(response, 200)
|
|
144
|
+
self.assertJE(response, 'title', "The Robots of Dawn")
|
|
145
|
+
|
|
146
|
+
def test_create_book_validation(self):
|
|
147
|
+
client = APIClient()
|
|
148
|
+
# make a post request
|
|
149
|
+
response = client.post(
|
|
150
|
+
'/api/books/',dict(
|
|
151
|
+
title=None,
|
|
152
|
+
author=self.author,
|
|
153
|
+
type='science_fiction',
|
|
154
|
+
pub_date=date(1983, 1, 1)
|
|
155
|
+
))
|
|
156
|
+
self.assertRC(response, 422)
|
|
157
|
+
self.assertJE(
|
|
158
|
+
response, 'title.@0', "This field cannot be null.")
|
|
159
|
+
|
|
160
|
+
def test_update_book(self):
|
|
161
|
+
client = APIClient()
|
|
162
|
+
response = client.put(
|
|
163
|
+
f'/api/books/{self.book2.pk}',dict(
|
|
164
|
+
title="The Robots of Dawn",
|
|
165
|
+
pub_date=date(1983, 1, 1),
|
|
166
|
+
))
|
|
167
|
+
self.assertRC(response, 200)
|
|
168
|
+
self.assertJE(response, 'title', "The Robots of Dawn")
|
|
169
|
+
|
|
170
|
+
def test_delete_book(self):
|
|
171
|
+
client = APIClient()
|
|
172
|
+
response = client.delete(
|
|
173
|
+
f'/api/books/{self.book2.pk}')
|
|
174
|
+
self.assertRC(response, 200)
|
|
175
|
+
|
|
176
|
+
books = Book.objects.filter(pk=self.book2.pk)
|
|
177
|
+
self.assertEqual(books.count(), 0)
|
|
178
|
+
|
|
179
|
+
```
|
|
@@ -12,7 +12,7 @@ In Django, `USE_I18N` must be `True` to use internationalization. Use `LANGUAGE_
|
|
|
12
12
|
You can optionally set `USE_L10N` to `True` (see Django documentation).
|
|
13
13
|
|
|
14
14
|
In Addition, you can set the list of available languages for web services with `LANGUAGES`:
|
|
15
|
-
```
|
|
15
|
+
```python
|
|
16
16
|
LANGUAGES = [
|
|
17
17
|
('fr', "French"),
|
|
18
18
|
('en', "English"),
|
|
@@ -5,15 +5,19 @@ Django PFX use plain Django Models classes.
|
|
|
5
5
|
This library only provides some helpers for properties
|
|
6
6
|
and foreign keys representations.
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## @rest_property
|
|
9
9
|
If you want to use properties in your model,
|
|
10
|
-
you have to annotate them with
|
|
10
|
+
you have to annotate them with `@rest_property`.
|
|
11
11
|
|
|
12
12
|
Rest property takes 2 parameters, a name and
|
|
13
13
|
the type of the Field (CharField, IntegerField, etc.)
|
|
14
14
|
|
|
15
15
|
Rest properties can be listed as fields to be returned in list and detail views.
|
|
16
|
-
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
from django.db import models
|
|
19
|
+
from pfx.pfxcore.decorator import rest_property
|
|
20
|
+
|
|
17
21
|
class Book(models.Model):
|
|
18
22
|
name = models.CharField("Name", max_length=30)
|
|
19
23
|
author = models.ForeignKey(
|
|
@@ -35,12 +39,11 @@ class Book(models.Model):
|
|
|
35
39
|
```
|
|
36
40
|
|
|
37
41
|
## JSONReprMixin
|
|
38
|
-
|
|
39
|
-
Foreign keys are returned by {doc}`Django PFX views <pfx_views>` as a JSON
|
|
42
|
+
Foreign keys are returned by [Django PFX views <pfx_views>](./pfx_views.md) as a JSON
|
|
40
43
|
structure with two fields : pk, resource_name
|
|
41
44
|
|
|
42
45
|
For instance for the book class, with the author foreign key :
|
|
43
|
-
```
|
|
46
|
+
```python
|
|
44
47
|
{
|
|
45
48
|
"author": {
|
|
46
49
|
"pk": 1,
|
|
@@ -60,8 +63,11 @@ by inheriting JSONReprMixin on the Model class and
|
|
|
60
63
|
by overriding the json_repr method.
|
|
61
64
|
|
|
62
65
|
For instance for the Author class :
|
|
63
|
-
```
|
|
64
|
-
|
|
66
|
+
```python
|
|
67
|
+
from django.db import models
|
|
68
|
+
from pfx.pfxcore.models import JSONReprMixin
|
|
69
|
+
|
|
70
|
+
class Author(JSONReprMixin, models.Model):
|
|
65
71
|
first_name = models.CharField("First Name", max_length=30)
|
|
66
72
|
last_name = models.CharField("Last Name", max_length=30)
|
|
67
73
|
slug = models.SlugField("Slug", unique=True)
|
|
@@ -80,11 +86,11 @@ class Author(models.Model):
|
|
|
80
86
|
```
|
|
81
87
|
|
|
82
88
|
Which give the following result on the book service :
|
|
83
|
-
```
|
|
89
|
+
```python
|
|
84
90
|
{
|
|
85
91
|
"author": {
|
|
86
92
|
"pk": 1,
|
|
87
|
-
"resource_name": "John Ronald Reuel Tolkien"
|
|
93
|
+
"resource_name": "John Ronald Reuel Tolkien",
|
|
88
94
|
"resource_slug": "john-ronald-reuel-tolkien"
|
|
89
95
|
},
|
|
90
96
|
"created_at": "2022-01-14",
|
|
@@ -104,9 +110,11 @@ converting `null` values to empty strings, you can use the following model field
|
|
|
104
110
|
* `NotNullCharField`
|
|
105
111
|
* `NotNullTextField`
|
|
106
112
|
* `NotNullURLField`
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
|
|
113
|
+
```python
|
|
114
|
+
from pfx.pfxcore.models import NotNullCharField
|
|
115
|
+
|
|
116
|
+
a_string_field = NotNullCharField(
|
|
117
|
+
"A string", max_length=255, blank=True, default="")
|
|
110
118
|
```
|
|
111
119
|
|
|
112
120
|
The `null` parameter of these fields is automatically set to `False`.
|
|
@@ -10,7 +10,9 @@ Provide a get detail service for a model class.
|
|
|
10
10
|
|
|
11
11
|
By default, all model fields are included in response. You can customize
|
|
12
12
|
the response by specifying the fields attribute (see [Define Fields](pfx_views.md#define-fields) for details).
|
|
13
|
-
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
from pfx.pfxcore.decorator import rest_view
|
|
14
16
|
from pfx.pfxcore.views import BaseRestView, DetailRestViewMixin
|
|
15
17
|
|
|
16
18
|
@rest_view("/books")
|
|
@@ -24,7 +26,8 @@ Provide a get detail service for a model class with a slug.
|
|
|
24
26
|
Slug field is searched in the `slug` field of the model by default,
|
|
25
27
|
but it can be overridden with the `SLUG_FIELD` attribute.
|
|
26
28
|
|
|
27
|
-
```
|
|
29
|
+
```python
|
|
30
|
+
from pfx.pfxcore.decorator import rest_view
|
|
28
31
|
from pfx.pfxcore.views import BaseRestView, SlugDetailRestViewMixin
|
|
29
32
|
|
|
30
33
|
@rest_view("/authors")
|
|
@@ -33,13 +36,15 @@ class AuthorRestView(SlugDetailRestViewMixin, BaseRestView):
|
|
|
33
36
|
SLUG_FIELD = "slug"
|
|
34
37
|
fields = ['name', 'slug', 'created_at', 'type']
|
|
35
38
|
```
|
|
39
|
+
|
|
36
40
|
## ListRestViewMixin
|
|
37
41
|
Provide a list service for a model class.
|
|
38
42
|
|
|
39
43
|
By default, list fields are taken from fields attributes.
|
|
40
44
|
They can also be listed in the list_fields attribute if you need to have
|
|
41
45
|
different fields in the list than in other views.
|
|
42
|
-
```
|
|
46
|
+
```python
|
|
47
|
+
from pfx.pfxcore.decorator import rest_view
|
|
43
48
|
from pfx.pfxcore.views import BaseRestView, ListRestViewMixin
|
|
44
49
|
|
|
45
50
|
@rest_view("/books")
|
|
@@ -47,11 +52,12 @@ class BookRestView(ListRestViewMixin, BaseRestView):
|
|
|
47
52
|
queryset = Book.objects
|
|
48
53
|
list_fields = ['name', 'author', 'pub_date', 'created_at', 'type']
|
|
49
54
|
```
|
|
55
|
+
|
|
50
56
|
### Pagination
|
|
51
57
|
If you pass `?subset=pagination`, the response will include pagination data in meta:
|
|
52
|
-
```
|
|
58
|
+
```python
|
|
53
59
|
{
|
|
54
|
-
"items": […],
|
|
60
|
+
"items": ['…'],
|
|
55
61
|
"meta": {
|
|
56
62
|
"page": 1,
|
|
57
63
|
"page_size": 10,
|
|
@@ -66,9 +72,9 @@ The page_size cannot be greater than `PFX_MAX_LIST_RESULT_SIZE` settings.
|
|
|
66
72
|
Override `pagination_result(self, qs)` to customize the behavior.
|
|
67
73
|
|
|
68
74
|
If you pass `?subset=offset`, the response will include offset/limit data in meta:
|
|
69
|
-
```
|
|
75
|
+
```python
|
|
70
76
|
{
|
|
71
|
-
"items": […],
|
|
77
|
+
"items": ['…'],
|
|
72
78
|
"meta": {
|
|
73
79
|
"count": 200,
|
|
74
80
|
"page_count": 20,
|
|
@@ -79,24 +85,26 @@ If you pass `?subset=offset`, the response will include offset/limit data in met
|
|
|
79
85
|
```
|
|
80
86
|
The limit cannot be greater than `PFX_MAX_LIST_RESULT_SIZE` settings.
|
|
81
87
|
Override `offset_result(self, qs)` to customize the behavior.
|
|
88
|
+
|
|
82
89
|
### Filters
|
|
83
90
|
List view can have filters.
|
|
91
|
+
|
|
84
92
|
#### ModelFilter
|
|
85
|
-
Use `ModelFilter` to add a filter on
|
|
86
|
-
```
|
|
93
|
+
Use `ModelFilter` to add a filter on an ORM field:
|
|
94
|
+
```python
|
|
87
95
|
from pfx.pfxcore.views import (
|
|
88
96
|
RestView,
|
|
89
97
|
ModelFilter)
|
|
90
98
|
|
|
91
|
-
|
|
92
99
|
class AuthorRestView(RestView):
|
|
93
100
|
filters = [
|
|
94
101
|
ModelFilter(Author, 'name'),
|
|
95
102
|
]
|
|
96
103
|
```
|
|
104
|
+
|
|
97
105
|
#### Filter
|
|
98
106
|
Use `Filter` to add a custom filter:
|
|
99
|
-
```
|
|
107
|
+
```python
|
|
100
108
|
from django.db.models import Q
|
|
101
109
|
|
|
102
110
|
from pfx.pfxcore.views import (
|
|
@@ -111,12 +119,14 @@ def name_filter(value):
|
|
|
111
119
|
|
|
112
120
|
class AuthorRestView(RestView):
|
|
113
121
|
filters = [
|
|
114
|
-
Filter('name',
|
|
122
|
+
Filter('name', "Name", FieldType.CharField, name_filter),
|
|
115
123
|
]
|
|
116
124
|
```
|
|
125
|
+
|
|
117
126
|
## CreateRestViewMixin
|
|
118
127
|
Provide a creation service for a model class.
|
|
119
|
-
```
|
|
128
|
+
```python
|
|
129
|
+
from pfx.pfxcore.decorator import rest_view
|
|
120
130
|
from pfx.pfxcore.views import BaseRestView, CreateRestViewMixin
|
|
121
131
|
|
|
122
132
|
@rest_view("/books")
|
|
@@ -124,11 +134,15 @@ class BookRestView(CreateRestViewMixin, BaseRestView):
|
|
|
124
134
|
queryset = Book.objects
|
|
125
135
|
fields = ['name', 'author', 'pub_date', 'created_at', 'type']
|
|
126
136
|
```
|
|
137
|
+
|
|
127
138
|
### Default values
|
|
128
139
|
You can set default values for fields in the ORM object field. But if
|
|
129
140
|
you need to set it at view level, you can use the `default_values`
|
|
130
141
|
class attribute.
|
|
131
|
-
```
|
|
142
|
+
```python
|
|
143
|
+
from pfx.pfxcore.decorator import rest_view
|
|
144
|
+
from pfx.pfxcore.views import BaseRestView, CreateRestViewMixin
|
|
145
|
+
|
|
132
146
|
@rest_view("/books")
|
|
133
147
|
class BookRestView(CreateRestViewMixin, BaseRestView):
|
|
134
148
|
queryset = Book.objects
|
|
@@ -137,14 +151,15 @@ class BookRestView(CreateRestViewMixin, BaseRestView):
|
|
|
137
151
|
)
|
|
138
152
|
```
|
|
139
153
|
If you need to set dynamics default values,
|
|
140
|
-
you can override following methods (depending
|
|
154
|
+
you can override following methods (depending on your needs):
|
|
141
155
|
* `get_default_values(self)`: return `default_values`.
|
|
142
156
|
* `new_object(self)`: return a new object instance with `get_default_values()`.
|
|
143
157
|
* `is_valid(self, obj, created=False, rel_data=None)`: persist the instance after validation.
|
|
144
158
|
|
|
145
159
|
## UpdateRestViewMixin
|
|
146
160
|
Provide an update service for a model class.
|
|
147
|
-
```
|
|
161
|
+
```python
|
|
162
|
+
from pfx.pfxcore.decorator import rest_view
|
|
148
163
|
from pfx.pfxcore.views import BaseRestView, UpdateRestViewMixin
|
|
149
164
|
|
|
150
165
|
@rest_view("/books")
|
|
@@ -155,7 +170,8 @@ class BookRestView(UpdateRestViewMixin, BaseRestView):
|
|
|
155
170
|
|
|
156
171
|
## DeleteRestViewMixin
|
|
157
172
|
Provide a delete service for a model class.
|
|
158
|
-
```
|
|
173
|
+
```python
|
|
174
|
+
from pfx.pfxcore.decorator import rest_view
|
|
159
175
|
from pfx.pfxcore.views import BaseRestView, DeleteRestViewMixin
|
|
160
176
|
|
|
161
177
|
@rest_view("/books")
|
|
@@ -175,22 +191,30 @@ system from scratch id to inherit Django original `View` instead.
|
|
|
175
191
|
### Default behavior
|
|
176
192
|
By default, all methods are private. You can modify
|
|
177
193
|
the `default_public` attribute to change this.
|
|
178
|
-
```
|
|
194
|
+
```python
|
|
195
|
+
from pfx.pfxcore.decorator import rest_view
|
|
196
|
+
from pfx.pfxcore.views import RestView
|
|
197
|
+
|
|
179
198
|
@rest_view("/books")
|
|
180
199
|
class BookRestView(RestView):
|
|
181
200
|
queryset = Book.objects
|
|
182
201
|
default_public = True
|
|
183
202
|
```
|
|
203
|
+
|
|
184
204
|
### Public method
|
|
185
205
|
If you want to define specific methods as a public methods,
|
|
186
206
|
add corresponding attributes `${method_name}_public`:
|
|
187
|
-
```
|
|
207
|
+
```python
|
|
208
|
+
from pfx.pfxcore.decorator import rest_view
|
|
209
|
+
from pfx.pfxcore.views import RestView
|
|
210
|
+
|
|
188
211
|
@rest_view("/books")
|
|
189
212
|
class BookRestView(RestView):
|
|
190
213
|
queryset = Book.objects
|
|
191
214
|
get_public = True
|
|
192
215
|
get_list_public = True
|
|
193
216
|
```
|
|
217
|
+
|
|
194
218
|
### Check user access
|
|
195
219
|
For private methods, you can verify user access in two steps:
|
|
196
220
|
* By overriding the `perm(self)` method.
|
|
@@ -201,14 +225,18 @@ If it returns `true` (default behavior), access is allowed
|
|
|
201
225
|
if `${method_name}_perm(self)` method does not exists.
|
|
202
226
|
If `${method_name}_perm` exists, it is called and must
|
|
203
227
|
return `true` to allow access.
|
|
204
|
-
```
|
|
228
|
+
```python
|
|
229
|
+
from pfx.pfxcore.decorator import rest_view
|
|
230
|
+
from pfx.pfxcore.views import RestView
|
|
231
|
+
|
|
205
232
|
@rest_view("/books")
|
|
206
233
|
class BookRestView(RestView):
|
|
207
234
|
queryset = Book.objects
|
|
208
235
|
|
|
209
|
-
def my_method_perm(self)
|
|
236
|
+
def my_method_perm(self):
|
|
210
237
|
return self.request.user.is_admin
|
|
211
238
|
```
|
|
239
|
+
|
|
212
240
|
### Check user access based on data
|
|
213
241
|
You can check user access based on data by overriding following methods:
|
|
214
242
|
* `object_create_perm(self, data)`
|