django-pfx 1.2.dev94__tar.gz → 1.2.dev99__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.dev94 → django-pfx-1.2.dev99}/PKG-INFO +1 -1
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/django_pfx.egg-info/PKG-INFO +1 -1
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/doc/conf.py +3 -1
- django-pfx-1.2.dev99/doc/source/decorator.md +61 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/doc/source/getting_started.md +2 -3
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/doc/source/model.md +16 -1
- django-pfx-1.2.dev99/doc/source/pfx_views.md +274 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/doc/source/testing.md +98 -22
- django-pfx-1.2.dev94/doc/source/decorator.md +0 -45
- django-pfx-1.2.dev94/doc/source/pfx_views.md +0 -100
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/.gitignore +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/.gitlab-ci.yml +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/.pre-commit-config.yaml +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/LICENSE +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/MANIFEST.in +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/README.md +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/django_pfx.egg-info/SOURCES.txt +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/django_pfx.egg-info/dependency_links.txt +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/django_pfx.egg-info/requires.txt +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/django_pfx.egg-info/top_level.txt +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/doc/Makefile +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/doc/index.rst +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/doc/source/authentication.md +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/doc/source/cookbook.md +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/doc/source/internationalisation.md +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/doc/source/pfxcore.views.rst +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/img/pfx.png +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/img/pfx.svg +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/__init__.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/__init__.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/apidoc/__init__.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/apidoc/parameters.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/apidoc/schema.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/apidoc/tags.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/apps.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/decorator/__init__.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/decorator/rest.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/default_settings.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/exceptions.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/fields.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/http/__init__.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/http/json_response.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.mo +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.po +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/management/__init__.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/management/commands/__init__.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/management/commands/makeapidoc.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/middleware/__init__.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/middleware/authentication.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/middleware/locale.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/models/__init__.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/models/cache_mixins.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/models/not_null_fields.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/models/pfx_models.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/models/user_filtered_queryset_mixin.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/serializers/__init__.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/serializers/json.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/settings.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/shortcuts.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/storage/__init__.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/storage/s3_storage.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/templates/registration/password_reset_email.txt +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/templates/registration/password_reset_subject.txt +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/templates/registration/welcome_email.txt +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/templates/registration/welcome_subject.txt +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/test.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/urls.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/views/__init__.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/views/authentication_views.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/views/fields.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/views/filters_views.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/views/locale_views.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/views/parameters/__init__.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/views/parameters/date_format.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/views/parameters/groups.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/views/parameters/list_count.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/views/parameters/list_items.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/views/parameters/list_mode.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/views/parameters/list_order.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/views/parameters/list_search.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/views/parameters/media_redirect.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/views/parameters/meta_fields.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/views/parameters/meta_filters.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/views/parameters/meta_orders.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/views/parameters/subset.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/views/parameters/subset_limit.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/views/parameters/subset_offset.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/views/parameters/subset_page.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/views/parameters/subset_page_size.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/views/parameters/subset_page_subset.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/views/rest_views.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pyproject.toml +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/requirements.txt +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/runtest.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/setup.cfg +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/setup.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/__init__.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/models.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/settings/__init__.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/settings/ci.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/settings/common.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/settings/dev.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/settings/dev_custom_example.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/settings/dev_default.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/tests/__init__.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/tests/basic_api_errors.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/tests/basic_api_test.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/tests/test_api_doc.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/tests/test_auth_api.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/tests/test_cache.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/tests/test_fields.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/tests/test_filters.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/tests/test_locale_api.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/tests/test_perm_tests.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/tests/test_perms_api.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/tests/test_shortcuts.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/tests/test_timezone_middleware.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/tests/test_tools.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/tests/test_user_queryset.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/tests/test_view_decorators.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/tests/test_view_fields.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/urls.py +0 -0
- {django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/tests/views.py +0 -0
|
@@ -66,4 +66,6 @@ html_theme = "sphinx_rtd_theme"
|
|
|
66
66
|
# Add any paths that contain custom static files (such as style sheets) here,
|
|
67
67
|
# relative to this directory. They are copied after the builtin static files,
|
|
68
68
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
|
69
|
-
html_static_path = ['_static']
|
|
69
|
+
# html_static_path = ['_static']
|
|
70
|
+
|
|
71
|
+
myst_heading_anchors = 3
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# View decorator and URL
|
|
2
|
+
## @rest_view
|
|
3
|
+
Used to provide the base path of a view class
|
|
4
|
+
```
|
|
5
|
+
@rest_view("/base-path")
|
|
6
|
+
class ViewClass():
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## @rest_api
|
|
10
|
+
Used to annotate the rest services method.
|
|
11
|
+
Parameters are the path and the HTTP method.
|
|
12
|
+
```
|
|
13
|
+
@rest_api("/path", method="get")
|
|
14
|
+
def class_method(self):
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
A short example
|
|
18
|
+
```
|
|
19
|
+
@rest_view("/books")
|
|
20
|
+
class BookRestView():
|
|
21
|
+
|
|
22
|
+
@rest_api("/list", method="get")
|
|
23
|
+
def get_list(self):
|
|
24
|
+
return JsonResponse(
|
|
25
|
+
dict(books=['The Man in the High Castle', 'A Scanner Darkly']))
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### path parameters
|
|
29
|
+
Path can contain parameters that are passed to the method.
|
|
30
|
+
```
|
|
31
|
+
@rest_api("/path/<int:pk>/test/<slug:slug>", method="get")
|
|
32
|
+
def class_method(self, pk, slug):
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Registering urls
|
|
36
|
+
To be available, annotated view class must be registered
|
|
37
|
+
in your urls.py file as follows.
|
|
38
|
+
```
|
|
39
|
+
from pfx.pfxcore import register_views
|
|
40
|
+
|
|
41
|
+
from . import views
|
|
42
|
+
|
|
43
|
+
urlpatterns = register_views(
|
|
44
|
+
views.AuthorRestView,
|
|
45
|
+
views.BookRestView)
|
|
46
|
+
```
|
|
47
|
+
You can include multiple views under one path, or add a path
|
|
48
|
+
wih a specific class method for each HTTP methods:
|
|
49
|
+
```
|
|
50
|
+
from pfx.pfxcore import register_views
|
|
51
|
+
|
|
52
|
+
from . import views
|
|
53
|
+
|
|
54
|
+
urlpatterns = [
|
|
55
|
+
path('api/', include(register_views(
|
|
56
|
+
views.AuthorRestView,
|
|
57
|
+
views.BookRestView))),
|
|
58
|
+
path('other/thing', views.OtherRestView.as_view(
|
|
59
|
+
pfx_methods=dict(get='get_other'))),
|
|
60
|
+
]
|
|
61
|
+
```
|
|
@@ -70,11 +70,10 @@ from django_request_mapping import UrlPattern
|
|
|
70
70
|
|
|
71
71
|
from book import views
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
urlpatterns.register(views.BookRestView)
|
|
73
|
+
apipatterns = register_views(views.BookRestView)
|
|
75
74
|
|
|
76
75
|
urlpatterns = [
|
|
77
|
-
path('api/', include(
|
|
76
|
+
path('api/', include(apipatterns)),
|
|
78
77
|
]
|
|
79
78
|
```
|
|
80
79
|
|
|
@@ -5,7 +5,7 @@ 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
10
|
you have to annotate them with @rest_property.
|
|
11
11
|
|
|
@@ -95,3 +95,18 @@ Which give the following result on the book service :
|
|
|
95
95
|
"resource_name": "The Fellowship of the Ring",
|
|
96
96
|
}
|
|
97
97
|
```
|
|
98
|
+
|
|
99
|
+
## Not null char fields
|
|
100
|
+
|
|
101
|
+
To avoid storing a mixture of empty strings and `null` values, while automatically
|
|
102
|
+
converting `null` values to empty strings, you can use the following model fields:
|
|
103
|
+
|
|
104
|
+
* `NotNullCharField`
|
|
105
|
+
* `NotNullTextField`
|
|
106
|
+
* `NotNullURLField`
|
|
107
|
+
```
|
|
108
|
+
a_string_field = NotNullCharField(
|
|
109
|
+
_("A string"), max_length=255, blank=True, default="")
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The `null` parameter of these fields is automatically set to `False`.
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# Django PFX Views
|
|
2
|
+
REST services are provided as `ViewMixin` for `BaseRestView`.
|
|
3
|
+
Each of these views must set the queryset used to query the model data either
|
|
4
|
+
by defining the queryset attribute or by overriding the `get_queryset` method.
|
|
5
|
+
|
|
6
|
+
Fields can be listed in the fields attribute, else all the fields are provided.
|
|
7
|
+
|
|
8
|
+
## DetailRestViewMixin
|
|
9
|
+
Provide a get detail service for a model class.
|
|
10
|
+
|
|
11
|
+
By default, all model fields are included in response. You can customize
|
|
12
|
+
the response by specifying the fields attribute (see [Define Fields](pfx_views.md#define-fields) for details).
|
|
13
|
+
```
|
|
14
|
+
from pfx.pfxcore.views import BaseRestView, DetailRestViewMixin
|
|
15
|
+
|
|
16
|
+
@rest_view("/books")
|
|
17
|
+
class BookRestView(DetailRestViewMixin, BaseRestView):
|
|
18
|
+
queryset = Book.objects
|
|
19
|
+
fields = ['name', 'author', 'pub_date', 'created_at', 'type']
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## SlugDetailRestViewMixin
|
|
23
|
+
Provide a get detail service for a model class with a slug.
|
|
24
|
+
Slug field is searched in the `slug` field of the model by default,
|
|
25
|
+
but it can be overridden with the `SLUG_FIELD` attribute.
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
from pfx.pfxcore.views import BaseRestView, SlugDetailRestViewMixin
|
|
29
|
+
|
|
30
|
+
@rest_view("/authors")
|
|
31
|
+
class AuthorRestView(SlugDetailRestViewMixin, BaseRestView):
|
|
32
|
+
queryset = Author.objects
|
|
33
|
+
SLUG_FIELD = "slug"
|
|
34
|
+
fields = ['name', 'slug', 'created_at', 'type']
|
|
35
|
+
```
|
|
36
|
+
## ListRestViewMixin
|
|
37
|
+
Provide a list service for a model class.
|
|
38
|
+
|
|
39
|
+
By default, list fields are taken from fields attributes.
|
|
40
|
+
They can also be listed in the list_fields attribute if you need to have
|
|
41
|
+
different fields in the list than in other views.
|
|
42
|
+
```
|
|
43
|
+
from pfx.pfxcore.views import BaseRestView, ListRestViewMixin
|
|
44
|
+
|
|
45
|
+
@rest_view("/books")
|
|
46
|
+
class BookRestView(ListRestViewMixin, BaseRestView):
|
|
47
|
+
queryset = Book.objects
|
|
48
|
+
list_fields = ['name', 'author', 'pub_date', 'created_at', 'type']
|
|
49
|
+
```
|
|
50
|
+
### Pagination
|
|
51
|
+
If you pass `?subset=pagination`, the response will include pagination data in meta:
|
|
52
|
+
```
|
|
53
|
+
{
|
|
54
|
+
"items": […],
|
|
55
|
+
"meta": {
|
|
56
|
+
"page": 1,
|
|
57
|
+
"page_size": 10,
|
|
58
|
+
"count": 200,
|
|
59
|
+
"page_count": 20,
|
|
60
|
+
"subset": [1, 2, 3, 4, 5],
|
|
61
|
+
"page_subset": 5
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
The page_size cannot be greater than `PFX_MAX_LIST_RESULT_SIZE` settings.
|
|
66
|
+
Override `pagination_result(self, qs)` to customize the behavior.
|
|
67
|
+
|
|
68
|
+
If you pass `?subset=offset`, the response will include offset/limit data in meta:
|
|
69
|
+
```
|
|
70
|
+
{
|
|
71
|
+
"items": […],
|
|
72
|
+
"meta": {
|
|
73
|
+
"count": 200,
|
|
74
|
+
"page_count": 20,
|
|
75
|
+
"limit": 10,
|
|
76
|
+
"offset": 0
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
The limit cannot be greater than `PFX_MAX_LIST_RESULT_SIZE` settings.
|
|
81
|
+
Override `offset_result(self, qs)` to customize the behavior.
|
|
82
|
+
### Filters
|
|
83
|
+
List view can have filters.
|
|
84
|
+
#### ModelFilter
|
|
85
|
+
Use `ModelFilter` to add a filter on a ORM field:
|
|
86
|
+
```
|
|
87
|
+
from pfx.pfxcore.views import (
|
|
88
|
+
RestView,
|
|
89
|
+
ModelFilter)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class AuthorRestView(RestView):
|
|
93
|
+
filters = [
|
|
94
|
+
ModelFilter(Author, 'name'),
|
|
95
|
+
]
|
|
96
|
+
```
|
|
97
|
+
#### Filter
|
|
98
|
+
Use `Filter` to add a custom filter:
|
|
99
|
+
```
|
|
100
|
+
from django.db.models import Q
|
|
101
|
+
|
|
102
|
+
from pfx.pfxcore.views import (
|
|
103
|
+
RestView,
|
|
104
|
+
FieldType,
|
|
105
|
+
Filter)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def name_filter(value):
|
|
109
|
+
return Q(first_name__icontains=value) | Q(last_name__icontains=value)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class AuthorRestView(RestView):
|
|
113
|
+
filters = [
|
|
114
|
+
Filter('name', _("Name"), FieldType.CharField, name_filter),
|
|
115
|
+
]
|
|
116
|
+
```
|
|
117
|
+
## CreateRestViewMixin
|
|
118
|
+
Provide a creation service for a model class.
|
|
119
|
+
```
|
|
120
|
+
from pfx.pfxcore.views import BaseRestView, CreateRestViewMixin
|
|
121
|
+
|
|
122
|
+
@rest_view("/books")
|
|
123
|
+
class BookRestView(CreateRestViewMixin, BaseRestView):
|
|
124
|
+
queryset = Book.objects
|
|
125
|
+
fields = ['name', 'author', 'pub_date', 'created_at', 'type']
|
|
126
|
+
```
|
|
127
|
+
### Default values
|
|
128
|
+
You can set default values for fields in the ORM object field. But if
|
|
129
|
+
you need to set it at view level, you can use the `default_values`
|
|
130
|
+
class attribute.
|
|
131
|
+
```
|
|
132
|
+
@rest_view("/books")
|
|
133
|
+
class BookRestView(CreateRestViewMixin, BaseRestView):
|
|
134
|
+
queryset = Book.objects
|
|
135
|
+
default_values = dict(
|
|
136
|
+
format='octavo'
|
|
137
|
+
)
|
|
138
|
+
```
|
|
139
|
+
If you need to set dynamics default values,
|
|
140
|
+
you can override following methods (depending of your needs):
|
|
141
|
+
* `get_default_values(self)`: return `default_values`.
|
|
142
|
+
* `new_object(self)`: return a new object instance with `get_default_values()`.
|
|
143
|
+
* `is_valid(self, obj, created=False, rel_data=None)`: persist the instance after validation.
|
|
144
|
+
|
|
145
|
+
## UpdateRestViewMixin
|
|
146
|
+
Provide an update service for a model class.
|
|
147
|
+
```
|
|
148
|
+
from pfx.pfxcore.views import BaseRestView, UpdateRestViewMixin
|
|
149
|
+
|
|
150
|
+
@rest_view("/books")
|
|
151
|
+
class BookRestView(UpdateRestViewMixin, BaseRestView):
|
|
152
|
+
queryset = Book.objects
|
|
153
|
+
fields = ['name', 'author', 'pub_date', 'created_at', 'type']
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## DeleteRestViewMixin
|
|
157
|
+
Provide a delete service for a model class.
|
|
158
|
+
```
|
|
159
|
+
from pfx.pfxcore.views import BaseRestView, DeleteRestViewMixin
|
|
160
|
+
|
|
161
|
+
@rest_view("/books")
|
|
162
|
+
class BookRestView(DeleteRestViewMixin, BaseRestView):
|
|
163
|
+
queryset = Book.objects
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## SecuredRestViewMixin
|
|
167
|
+
`SecuredRestViewMixin` allows you to define whether methods
|
|
168
|
+
are public or private (requires a logged-in user),
|
|
169
|
+
and to check access conditions to the method for a user.
|
|
170
|
+
|
|
171
|
+
If you inherit `BaseRestView` or `RestView`, `SecuredRestViewMixin` is
|
|
172
|
+
already included. The only way to ignore it or defining your custom security
|
|
173
|
+
system from scratch id to inherit Django original `View` instead.
|
|
174
|
+
|
|
175
|
+
### Default behavior
|
|
176
|
+
By default, all methods are private. You can modify
|
|
177
|
+
the `default_public` attribute to change this.
|
|
178
|
+
```
|
|
179
|
+
@rest_view("/books")
|
|
180
|
+
class BookRestView(RestView):
|
|
181
|
+
queryset = Book.objects
|
|
182
|
+
default_public = True
|
|
183
|
+
```
|
|
184
|
+
### Public method
|
|
185
|
+
If you want to define specific methods as a public methods,
|
|
186
|
+
add corresponding attributes `${method_name}_public`:
|
|
187
|
+
```
|
|
188
|
+
@rest_view("/books")
|
|
189
|
+
class BookRestView(RestView):
|
|
190
|
+
queryset = Book.objects
|
|
191
|
+
get_public = True
|
|
192
|
+
get_list_public = True
|
|
193
|
+
```
|
|
194
|
+
### Check user access
|
|
195
|
+
For private methods, you can verify user access in two steps:
|
|
196
|
+
* By overriding the `perm(self)` method.
|
|
197
|
+
* By overriding the `${method_name}_perm(self)` method.
|
|
198
|
+
|
|
199
|
+
the `perm` method is called first, and if it returns `false`, access is denied.
|
|
200
|
+
If it returns `true` (default behavior), access is allowed
|
|
201
|
+
if `${method_name}_perm(self)` method does not exists.
|
|
202
|
+
If `${method_name}_perm` exists, it is called and must
|
|
203
|
+
return `true` to allow access.
|
|
204
|
+
```
|
|
205
|
+
@rest_view("/books")
|
|
206
|
+
class BookRestView(RestView):
|
|
207
|
+
queryset = Book.objects
|
|
208
|
+
|
|
209
|
+
def my_method_perm(self)
|
|
210
|
+
return self.request.user.is_admin
|
|
211
|
+
```
|
|
212
|
+
### Check user access based on data
|
|
213
|
+
You can check user access based on data by overriding following methods:
|
|
214
|
+
* `object_create_perm(self, data)`
|
|
215
|
+
* `object_update_perm(self, obj, data)`
|
|
216
|
+
* `object_delete_perm(self, obj)`
|
|
217
|
+
Where `data` is the dictionary of new values and `obj` the existing object.
|
|
218
|
+
|
|
219
|
+
These methods are called by the put/post/delete methods of standard mixins
|
|
220
|
+
before validation. If you write a custom method and you want to use
|
|
221
|
+
one of these method, you have to call it yourself.
|
|
222
|
+
|
|
223
|
+
## Base classes
|
|
224
|
+
You can use PFX view mixins with following base classes:
|
|
225
|
+
|
|
226
|
+
* `View`: the Django base view class.
|
|
227
|
+
* `BaseRestView`: The PFX base view for API, inherits `SecuredRestViewMixin` and `View`.
|
|
228
|
+
* `RestView`: The PFX base view for a Rest API, inherits:
|
|
229
|
+
* `ListRestViewMixin`
|
|
230
|
+
* `DetailRestViewMixin`
|
|
231
|
+
* `CreateRestViewMixin`
|
|
232
|
+
* `UpdateRestViewMixin`
|
|
233
|
+
* `DeleteRestViewMixin`
|
|
234
|
+
* `BaseRestView`
|
|
235
|
+
|
|
236
|
+
## Define Fields
|
|
237
|
+
Fields in `fields` and `list_fields` attributes can be:
|
|
238
|
+
* A string: the field attribute name.
|
|
239
|
+
* A `pfx.pfxcore.views.VF` object.
|
|
240
|
+
|
|
241
|
+
`VF` object can be used to customize field behavior and must define at least the field name:
|
|
242
|
+
```
|
|
243
|
+
field = [VF('name')]
|
|
244
|
+
```
|
|
245
|
+
You can specify following optional attributes:
|
|
246
|
+
* `verbose_name`
|
|
247
|
+
* `field_type`
|
|
248
|
+
* `alias`
|
|
249
|
+
* `readonly`
|
|
250
|
+
* `readonly_create`
|
|
251
|
+
* `readonly_update`
|
|
252
|
+
* `choices`
|
|
253
|
+
* `select`
|
|
254
|
+
* `json_repr`
|
|
255
|
+
|
|
256
|
+
Refer to method documentation for details.
|
|
257
|
+
|
|
258
|
+
### Meta
|
|
259
|
+
|
|
260
|
+
The `ModelResponseMixin` (included in every mixin with a service whose response is a simple object),
|
|
261
|
+
exposes a `/meta` service to identify fields (and their characteristics: type, required, readonly, ...),
|
|
262
|
+
to enable automatic generation of forms.
|
|
263
|
+
|
|
264
|
+
See the generated API documentation for more details.
|
|
265
|
+
|
|
266
|
+
On the same principle, `ListRestViewMixin` exposes the `/meta/list` service, which,
|
|
267
|
+
in addition to the fields, returns the list of available filters and
|
|
268
|
+
the list of available fields to order the list.
|
|
269
|
+
|
|
270
|
+
#### Notes for future releases
|
|
271
|
+
|
|
272
|
+
In a future version, these services should return a JSON OpenAPI structure. The list
|
|
273
|
+
of orderable fields is resource-intensive to compute and should be removed and moved
|
|
274
|
+
in the generated API doc.
|
|
@@ -18,12 +18,12 @@ so that each request header contains
|
|
|
18
18
|
the HTTP_X_CUSTOM_LANGUAGE attribute with the locale.
|
|
19
19
|
|
|
20
20
|
Example :
|
|
21
|
-
```
|
|
21
|
+
```python
|
|
22
22
|
client = APIClient(default_locale='en_GB')
|
|
23
23
|
```
|
|
24
24
|
Each request to a service can also override the locale.
|
|
25
25
|
Example :
|
|
26
|
-
```
|
|
26
|
+
```python
|
|
27
27
|
client = APIClient()
|
|
28
28
|
response = client.get('/api/authors', locale='fr_CH')
|
|
29
29
|
```
|
|
@@ -33,7 +33,7 @@ response = client.get('/api/authors', locale='fr_CH')
|
|
|
33
33
|
Send a get request.
|
|
34
34
|
|
|
35
35
|
Example :
|
|
36
|
-
```
|
|
36
|
+
```python
|
|
37
37
|
client = APIClient()
|
|
38
38
|
response = client.get('/api/authors')
|
|
39
39
|
```
|
|
@@ -42,7 +42,7 @@ response = client.get('/api/authors')
|
|
|
42
42
|
Send a post request with content type 'application/json'.
|
|
43
43
|
|
|
44
44
|
Example :
|
|
45
|
-
```
|
|
45
|
+
```python
|
|
46
46
|
client = APIClient()
|
|
47
47
|
response = client.post(
|
|
48
48
|
'/api/authors', {
|
|
@@ -54,7 +54,7 @@ response = client.post(
|
|
|
54
54
|
Send a put request with content type 'application/json'.
|
|
55
55
|
|
|
56
56
|
Example :
|
|
57
|
-
```
|
|
57
|
+
```python
|
|
58
58
|
client = APIClient()
|
|
59
59
|
response = self.client.put(
|
|
60
60
|
f'/api/authors/{author_pk}',
|
|
@@ -67,13 +67,12 @@ response = self.client.put(
|
|
|
67
67
|
Send a delete request with content type 'application/json'.
|
|
68
68
|
|
|
69
69
|
Example :
|
|
70
|
-
```
|
|
70
|
+
```python
|
|
71
71
|
client = APIClient()
|
|
72
72
|
response = client.delete(
|
|
73
73
|
f'/api/authors/{author_pk}')
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
-
|
|
77
76
|
### Login
|
|
78
77
|
If you use PFX authentication views you can use this login method.
|
|
79
78
|
|
|
@@ -81,7 +80,7 @@ Once you called login, you can call any other method
|
|
|
81
80
|
listed above, the authentication token will be sent with the requests.
|
|
82
81
|
|
|
83
82
|
Example :
|
|
84
|
-
```
|
|
83
|
+
```python
|
|
85
84
|
client = APIClient()
|
|
86
85
|
client.login(
|
|
87
86
|
username='jrr.tolkien',
|
|
@@ -98,7 +97,7 @@ Test the response code of a response.
|
|
|
98
97
|
It takes two parameters, the response and the expected status code.
|
|
99
98
|
|
|
100
99
|
Example :
|
|
101
|
-
```
|
|
100
|
+
```python
|
|
102
101
|
class ATestClass(TestAssertMixin, TransactionTestCase):
|
|
103
102
|
|
|
104
103
|
def a_test(self):
|
|
@@ -110,12 +109,12 @@ class ATestClass(TestAssertMixin, TransactionTestCase):
|
|
|
110
109
|
self.assertRC(response, 200)
|
|
111
110
|
```
|
|
112
111
|
|
|
113
|
-
###
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
112
|
+
### get_val
|
|
113
|
+
|
|
114
|
+
Get a value for a python dictionary or a json HTTP response by a string path key.
|
|
115
|
+
The src parameter can be a Python dictionary or an object with `json_content` attribute.
|
|
117
116
|
For instance if you have a response like
|
|
118
|
-
```
|
|
117
|
+
```python
|
|
119
118
|
{
|
|
120
119
|
"author": {
|
|
121
120
|
"pk": 2,
|
|
@@ -125,32 +124,58 @@ For instance if you have a response like
|
|
|
125
124
|
"pk": 6,
|
|
126
125
|
}
|
|
127
126
|
```
|
|
128
|
-
you can reach the author pk by providing "author.pk" as the key.
|
|
127
|
+
you can reach the author pk by providing `"author.pk"` as the key.
|
|
129
128
|
|
|
130
|
-
|
|
131
|
-
The syntax is "@index"
|
|
129
|
+
get_val also allows to specify the index in an array.
|
|
130
|
+
The syntax is `"@index"`.
|
|
132
131
|
|
|
133
132
|
For instance if you want the pk of the author of the
|
|
134
|
-
third item in an array of books you can specify "items.@3.author.pk"
|
|
133
|
+
third item in an array of books you can specify `"items.@3.author.pk"`
|
|
135
134
|
|
|
136
135
|
Example :
|
|
136
|
+
```python
|
|
137
|
+
class ATestClass(TestAssertMixin, TransactionTestCase):
|
|
138
|
+
|
|
139
|
+
def a_test(self):
|
|
140
|
+
client = APIClient()
|
|
141
|
+
response = client.get('/api/books')
|
|
142
|
+
author_pk = self.get_val(response, 'items.@3.author.pk')
|
|
137
143
|
```
|
|
144
|
+
|
|
145
|
+
### assertJE
|
|
146
|
+
Test the value of a dictionary property (using `get_val`).
|
|
147
|
+
It takes 3 parameters, the source, the key and the expected value.
|
|
148
|
+
|
|
149
|
+
Example :
|
|
150
|
+
```python
|
|
138
151
|
class ATestClass(TestAssertMixin, TransactionTestCase):
|
|
139
152
|
|
|
140
153
|
def a_test(self):
|
|
141
154
|
client = APIClient()
|
|
142
155
|
response = client.get('/api/books')
|
|
143
|
-
self.assertJE(response, 'items.@3.author.pk', 6)
|
|
156
|
+
author_pk = self.assertJE(response, 'items.@3.author.pk', 6)
|
|
144
157
|
```
|
|
145
158
|
|
|
146
159
|
### assertNJE
|
|
147
|
-
|
|
160
|
+
`AssertNJE` is the same as `AssertJE`, but it tests that the value is not equal.
|
|
161
|
+
|
|
162
|
+
### assertJEExists
|
|
163
|
+
Assert the path exists in the source.
|
|
164
|
+
|
|
165
|
+
### assertJENotExists
|
|
166
|
+
Assert the path does not exists in the source.
|
|
167
|
+
|
|
168
|
+
### assertSize
|
|
169
|
+
Test the size of a value (if the value is a collection).
|
|
170
|
+
|
|
171
|
+
### assertJIn
|
|
172
|
+
Test that an element is part of a collection value.
|
|
148
173
|
|
|
149
174
|
## Print Response
|
|
150
175
|
You can use the print_response helper to print a response in the console.
|
|
151
176
|
|
|
152
177
|
Example :
|
|
153
|
-
```
|
|
178
|
+
```python
|
|
154
179
|
client = APIClient()
|
|
155
180
|
response = client.get('/api/books')
|
|
156
181
|
print_response(response)
|
|
@@ -158,7 +183,7 @@ print_response(response)
|
|
|
158
183
|
|
|
159
184
|
will produce
|
|
160
185
|
|
|
161
|
-
```
|
|
186
|
+
```plain
|
|
162
187
|
*********************http response*********************
|
|
163
188
|
Status : 200 OK
|
|
164
189
|
Headers :
|
|
@@ -186,3 +211,54 @@ Content :
|
|
|
186
211
|
}
|
|
187
212
|
*******************************************************
|
|
188
213
|
```
|
|
214
|
+
|
|
215
|
+
## PermsAPITest
|
|
216
|
+
|
|
217
|
+
This test class can be used to test permissions on services with multiple users.
|
|
218
|
+
|
|
219
|
+
Create multiple users in `setUpTestData` and add a method for each service
|
|
220
|
+
you want to test.
|
|
221
|
+
|
|
222
|
+
You can test the response status code or the response list count for items list responses.
|
|
223
|
+
|
|
224
|
+
Example :
|
|
225
|
+
```python
|
|
226
|
+
class BookPermsAPITest(PermsAPITest):
|
|
227
|
+
def setUp(self):
|
|
228
|
+
self.client = APIClient(with_cookie=True)
|
|
229
|
+
|
|
230
|
+
@classmethod
|
|
231
|
+
def setUpTestData(cls):
|
|
232
|
+
# Create your users here
|
|
233
|
+
|
|
234
|
+
def list(self):
|
|
235
|
+
# Create a book
|
|
236
|
+
return self.client.get('/api/books?items=1&count=1')
|
|
237
|
+
|
|
238
|
+
def get(self):
|
|
239
|
+
# book = … (create a book)
|
|
240
|
+
return self.client.get(f'/api/books/{book.pk}')
|
|
241
|
+
|
|
242
|
+
def post(self):
|
|
243
|
+
return self.client.post('/api/books', dict(
|
|
244
|
+
name="Test new"))
|
|
245
|
+
|
|
246
|
+
def put(self):
|
|
247
|
+
return self.client.put(
|
|
248
|
+
f'/api/books/{self.organization.pk}', dict(
|
|
249
|
+
name="Updated"))
|
|
250
|
+
|
|
251
|
+
def delete(self):
|
|
252
|
+
# book = … (create a book)
|
|
253
|
+
return self.client.delete(f'/api/books/{book.pk}')
|
|
254
|
+
|
|
255
|
+
USER_TESTS = {
|
|
256
|
+
"admin@user.org": dict(
|
|
257
|
+
list=200, list__count=1, get=200
|
|
258
|
+
post=200, put=200, delete=200),
|
|
259
|
+
"user@user.org": dict(
|
|
260
|
+
list=200, list__count=1, get=200,
|
|
261
|
+
post=403, put=403, delete=403),
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
```
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# View decorator and URL
|
|
2
|
-
## @rest_view
|
|
3
|
-
Used to provide the base path of a view class
|
|
4
|
-
```
|
|
5
|
-
@rest_view("/base-path")
|
|
6
|
-
class ViewClass():
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
## @rest_api
|
|
10
|
-
Used to annotate the rest services method.
|
|
11
|
-
Parameters are the path and the HTTP method.
|
|
12
|
-
```
|
|
13
|
-
@rest_api("/path", method="get")
|
|
14
|
-
def class_method(self):
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
A short example
|
|
18
|
-
```
|
|
19
|
-
@rest_view("/books")
|
|
20
|
-
class BookRestView():
|
|
21
|
-
|
|
22
|
-
@rest_api("/list", method="get")
|
|
23
|
-
def get_list(self):
|
|
24
|
-
return JsonResponse(
|
|
25
|
-
dict(books=['The Man in the High Castle', 'A Scanner Darkly']))
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
### path parameters
|
|
29
|
-
Path can contain parameters that are passed to the method.
|
|
30
|
-
```
|
|
31
|
-
@rest_api("/path/<int:id>/test/<slug:slug>", method="get")
|
|
32
|
-
def class_method(self, id, slug):
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## Registering urls
|
|
36
|
-
To be available, annotated view class must be registered
|
|
37
|
-
in your urls.py file as follows.
|
|
38
|
-
```
|
|
39
|
-
from django_request_mapping import UrlPattern
|
|
40
|
-
|
|
41
|
-
from . import views
|
|
42
|
-
|
|
43
|
-
urlpatterns = UrlPattern()
|
|
44
|
-
urlpatterns.register(views.BookRestView)
|
|
45
|
-
```
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
# Django PFX Views
|
|
2
|
-
REST services are provided as ViewMixin.
|
|
3
|
-
Each of these views must set the queryset used to query the model data either
|
|
4
|
-
by defining the queryset attribute or by overriding the get_queryset method.
|
|
5
|
-
|
|
6
|
-
Fields can be listed in the fields attribute, else all the fields are provided.
|
|
7
|
-
|
|
8
|
-
## ListRestViewMixin
|
|
9
|
-
Provide a list service for a model class.
|
|
10
|
-
|
|
11
|
-
By default, list fields are taken from fields attributes if defined.
|
|
12
|
-
They can also be listed in the list_fields attribute if you need to have
|
|
13
|
-
different fields in the list than in other views.
|
|
14
|
-
```
|
|
15
|
-
@rest_view("/books")
|
|
16
|
-
class BookRestView(ListRestViewMixinRestView):
|
|
17
|
-
queryset = Book.objects
|
|
18
|
-
list_fields = ['name', 'author', 'pub_date', 'created_at', 'type']
|
|
19
|
-
```
|
|
20
|
-
### Meta
|
|
21
|
-
### Pagination
|
|
22
|
-
|
|
23
|
-
### Filters
|
|
24
|
-
List view can have filters.
|
|
25
|
-
|
|
26
|
-
TODO explain filters.
|
|
27
|
-
|
|
28
|
-
## DetailRestViewMixin
|
|
29
|
-
Provide a get detail service for a model class.
|
|
30
|
-
|
|
31
|
-
```
|
|
32
|
-
@rest_view("/books")
|
|
33
|
-
class BookRestView(DetailRestViewMixin):
|
|
34
|
-
queryset = Book.objects
|
|
35
|
-
fields = ['name', 'author', 'pub_date', 'created_at', 'type']
|
|
36
|
-
```
|
|
37
|
-
### Meta
|
|
38
|
-
|
|
39
|
-
## CreateRestViewMixin
|
|
40
|
-
Provide a creation service for a model class.
|
|
41
|
-
Readonly fields can be provided in the read_only attribute.
|
|
42
|
-
|
|
43
|
-
TODO : explain "default_values"
|
|
44
|
-
```
|
|
45
|
-
@rest_view("/books")
|
|
46
|
-
class BookRestView(CreateRestViewMixin):
|
|
47
|
-
queryset = Book.objects
|
|
48
|
-
fields = ['name', 'author', 'pub_date', 'created_at', 'type']
|
|
49
|
-
readonly_fields = ['created_at']
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
## UpdateRestViewMixin
|
|
53
|
-
Provide an update service for a model class.
|
|
54
|
-
Readonly fields can be provided in the read_only attribute.
|
|
55
|
-
```
|
|
56
|
-
@rest_view("/books")
|
|
57
|
-
class BookRestView(UpdateRestViewMixin):
|
|
58
|
-
queryset = Book.objects
|
|
59
|
-
fields = ['name', 'author', 'pub_date', 'created_at', 'type']
|
|
60
|
-
readonly_fields = ['created_at']
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
## DeleteRestViewMixin
|
|
64
|
-
Provide a delete service for a model class.
|
|
65
|
-
|
|
66
|
-
```
|
|
67
|
-
@rest_view("/books")
|
|
68
|
-
class BookRestView(DeleteRestViewMixin):
|
|
69
|
-
queryset = Book.objects
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## SlugDetailRestViewMixin
|
|
73
|
-
Provide a get detail service for a model class with a slug.
|
|
74
|
-
Slug field is searched in the "slug" field of the model by default,
|
|
75
|
-
but it can be overridden with the "SLUG_FIELD" attribute.
|
|
76
|
-
|
|
77
|
-
```
|
|
78
|
-
@rest_view("/authors")
|
|
79
|
-
class AuthorRestView(SlugDetailRestViewMixin):
|
|
80
|
-
queryset = Author.objects
|
|
81
|
-
SLUG_FIELD = "slug"
|
|
82
|
-
fields = ['name', 'slug', 'created_at', 'type']
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
## SecuredRestViewMixin
|
|
86
|
-
TODO Explain :
|
|
87
|
-
- default_public = True
|
|
88
|
-
- def perm(self):
|
|
89
|
-
- {func_name}_perm
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
## RestView
|
|
93
|
-
A base class that inherits :
|
|
94
|
-
ListRestViewMixin,
|
|
95
|
-
DetailRestViewMixin,
|
|
96
|
-
CreateRestViewMixin,
|
|
97
|
-
UpdateRestViewMixin,
|
|
98
|
-
DeleteRestViewMixin,
|
|
99
|
-
SecuredRestViewMixin,
|
|
100
|
-
django.views.View
|
|
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.2.dev94 → django-pfx-1.2.dev99}/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
|
|
File without changes
|
|
File without changes
|
{django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/templates/registration/welcome_email.txt
RENAMED
|
File without changes
|
{django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/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
|
|
File without changes
|
|
File without changes
|
{django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/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.2.dev94 → django-pfx-1.2.dev99}/pfx/pfxcore/views/parameters/subset_page_size.py
RENAMED
|
File without changes
|
{django-pfx-1.2.dev94 → django-pfx-1.2.dev99}/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
|