django-pfx 1.4.dev110__tar.gz → 1.4.dev114__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.dev110 → django_pfx-1.4.dev114}/PKG-INFO +1 -1
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/django_pfx.egg-info/PKG-INFO +1 -1
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/django_pfx.egg-info/SOURCES.txt +4 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/decorator/rest.py +1 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/fields/__init__.py +1 -0
- django_pfx-1.4.dev114/pfx/pfxcore/fields/decimal_field.py +15 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/fields/media_field.py +24 -4
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.po +2 -2
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/models/pfx_models.py +4 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/storage/local_storage.py +3 -1
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/views/fields.py +17 -3
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/views/rest_views.py +9 -5
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/locale/fr/LC_MESSAGES/django.po +3 -3
- django_pfx-1.4.dev114/tests/migrations/0002_alter_book_cover.py +21 -0
- django_pfx-1.4.dev114/tests/migrations/0003_book_local_file.py +21 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/models.py +6 -1
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/__init__.py +1 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/basic_api_test.py +57 -0
- django_pfx-1.4.dev114/tests/tests/test_fields_decimal.py +59 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/test_view_fields.py +1 -1
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/views.py +1 -1
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/.gitignore +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/.gitlab-ci.yml +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/.pre-commit-config.yaml +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/LICENSE +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/MANIFEST.in +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/README.md +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/django_pfx.egg-info/dependency_links.txt +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/django_pfx.egg-info/requires.txt +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/django_pfx.egg-info/top_level.txt +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/doc/Makefile +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/doc/conf.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/doc/index.rst +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/doc/source/api.views.rst +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/doc/source/authentication.md +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/doc/source/decorator.md +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/doc/source/generate_openapi.md +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/doc/source/getting_started.md +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/doc/source/internationalisation.md +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/doc/source/model.md +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/doc/source/pfx_views.md +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/doc/source/profiling.md +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/doc/source/settings.md +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/doc/source/testing.md +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/img/pfx.png +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/img/pfx.svg +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/make_messages +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/manage.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/apidoc/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/apidoc/parameters.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/apidoc/schema.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/apidoc/tags.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/apps.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/decorator/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/default_settings.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/exceptions.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/fields/minutes_duration_field.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/fields/nh3_field.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/http/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/http/json_response.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.mo +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/management/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/management/commands/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/management/commands/makeapidoc.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/management/commands/profile.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/middleware/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/middleware/authentication.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/middleware/locale.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/middleware/profiling.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/migrations/0001_initial.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/migrations/0002_pfxpermissionsuser.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/migrations/0003_delete_pfxpermissionsuser.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/migrations/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/migrations/operations/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/migrations/operations/permissions.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/models/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/models/abstract_pfx_base_user.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/models/cache_mixins.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/models/login_ban.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/models/not_null_fields.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/models/ordered_model_mixin.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/models/otp_user_mixin.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/models/pfx_user.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/models/user_filtered_queryset_mixin.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/serializers/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/serializers/json.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/settings.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/shortcuts.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/storage/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/storage/exceptions.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/storage/s3_storage.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/templates/registration/otp_code_email.txt +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/templates/registration/otp_code_subject.txt +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/templates/registration/password_reset_email.txt +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/templates/registration/password_reset_subject.txt +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/templates/registration/welcome_email.txt +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/templates/registration/welcome_subject.txt +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/test.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/urls.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/views/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/views/authentication_views.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/views/filters_views.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/views/locale_views.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/views/media_rest_view_mixin.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/views/ordered_rest_view_mixin.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/views/parameters/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/views/parameters/date_format.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/views/parameters/groups.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/views/parameters/list_count.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/views/parameters/list_items.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/views/parameters/list_mode.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/views/parameters/list_order.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/views/parameters/list_search.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/views/parameters/media_redirect.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/views/parameters/meta_fields.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/views/parameters/meta_filters.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/views/parameters/meta_orders.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/views/parameters/subset.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/views/parameters/subset_limit.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/views/parameters/subset_offset.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/views/parameters/subset_page.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/views/parameters/subset_page_size.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/views/parameters/subset_page_subset.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/settings/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pfx/settings/dev.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/pyproject.toml +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/requirements.txt +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/serve-doc +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/setup.cfg +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/setup.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/apps.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/migrations/0001_initial.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/migrations/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/settings/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/settings/ci.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/settings/common.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/settings/dev.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/settings/dev_custom_example.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/settings/dev_default.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/basic_api_errors.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/test_api_doc.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/test_api_doc_search.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/test_auth_api.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/test_body_mixin.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/test_cache.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/test_client.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/test_fields_choices.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/test_fields_minutes_duration.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/test_fields_nh3.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/test_filters.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/test_locale_api.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/test_ordered_rest_view_mixin.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/test_perm_tests.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/test_permissions.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/test_perms_api.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/test_post_migrate_groups_update.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/test_profiling_middleware.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/test_settings.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/test_shortcuts.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/test_timezone_middleware.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/test_tools.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/test_user_queryset.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/tests/test_view_decorators.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests/urls.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_base_user/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_base_user/migrations/0001_initial.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_base_user/migrations/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_base_user/settings/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_base_user/settings/ci.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_base_user/settings/common.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_base_user/settings/dev.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_base_user/settings/dev_custom_example.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_base_user/settings/dev_default.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_base_user/tests/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_base_user/tests/test_api.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_base_user/tests/test_auth_api.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_base_user/urls.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_base_user/views.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_custom_user/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_custom_user/migrations/0001_initial.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_custom_user/migrations/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_custom_user/models.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_custom_user/settings/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_custom_user/settings/ci.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_custom_user/settings/common.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_custom_user/settings/dev.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_custom_user/settings/dev_custom_example.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_custom_user/settings/dev_default.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_custom_user/tests/__init__.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_custom_user/tests/test_api.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_custom_user/tests/test_auth_api.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_custom_user/urls.py +0 -0
- {django_pfx-1.4.dev110 → django_pfx-1.4.dev114}/tests_custom_user/views.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-pfx
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.dev114
|
|
4
4
|
Summary: Django PFX is a toolkit designed to streamline the development of RESTful APIs using the Django framework.
|
|
5
5
|
Author: Hervé Martinet
|
|
6
6
|
Author-email: herve.martinet@gmail.com
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-pfx
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.dev114
|
|
4
4
|
Summary: Django PFX is a toolkit designed to streamline the development of RESTful APIs using the Django framework.
|
|
5
5
|
Author: Hervé Martinet
|
|
6
6
|
Author-email: herve.martinet@gmail.com
|
|
@@ -48,6 +48,7 @@ pfx/pfxcore/apidoc/tags.py
|
|
|
48
48
|
pfx/pfxcore/decorator/__init__.py
|
|
49
49
|
pfx/pfxcore/decorator/rest.py
|
|
50
50
|
pfx/pfxcore/fields/__init__.py
|
|
51
|
+
pfx/pfxcore/fields/decimal_field.py
|
|
51
52
|
pfx/pfxcore/fields/media_field.py
|
|
52
53
|
pfx/pfxcore/fields/minutes_duration_field.py
|
|
53
54
|
pfx/pfxcore/fields/nh3_field.py
|
|
@@ -126,6 +127,8 @@ tests/urls.py
|
|
|
126
127
|
tests/views.py
|
|
127
128
|
tests/locale/fr/LC_MESSAGES/django.po
|
|
128
129
|
tests/migrations/0001_initial.py
|
|
130
|
+
tests/migrations/0002_alter_book_cover.py
|
|
131
|
+
tests/migrations/0003_book_local_file.py
|
|
129
132
|
tests/migrations/__init__.py
|
|
130
133
|
tests/settings/__init__.py
|
|
131
134
|
tests/settings/ci.py
|
|
@@ -143,6 +146,7 @@ tests/tests/test_body_mixin.py
|
|
|
143
146
|
tests/tests/test_cache.py
|
|
144
147
|
tests/tests/test_client.py
|
|
145
148
|
tests/tests/test_fields_choices.py
|
|
149
|
+
tests/tests/test_fields_decimal.py
|
|
146
150
|
tests/tests/test_fields_minutes_duration.py
|
|
147
151
|
tests/tests/test_fields_nh3.py
|
|
148
152
|
tests/tests/test_filters.py
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from decimal import Decimal
|
|
2
|
+
|
|
3
|
+
from django.db import models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DecimalField(models.DecimalField):
|
|
7
|
+
def __init__(self, *args, json_decimal_places=None, **kw):
|
|
8
|
+
super().__init__(*args, **kw)
|
|
9
|
+
self.json_quantize = Decimal(10) ** -(
|
|
10
|
+
json_decimal_places or self.decimal_places)
|
|
11
|
+
|
|
12
|
+
def to_json(self, value):
|
|
13
|
+
if value is None:
|
|
14
|
+
return None
|
|
15
|
+
return value.quantize(self.json_quantize)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from importlib import import_module
|
|
3
|
+
from urllib.request import urlopen
|
|
3
4
|
|
|
4
5
|
from django.db import models
|
|
5
6
|
from django.db.models.signals import post_delete
|
|
@@ -27,15 +28,31 @@ class MediaField(models.JSONField):
|
|
|
27
28
|
self.storage = storage or get_storage_class(settings.STORAGE_DEFAULT)
|
|
28
29
|
self.auto_delete = auto_delete
|
|
29
30
|
super().__init__(
|
|
30
|
-
*args, max_length=max_length,
|
|
31
|
-
default=kwargs.pop('default', dict),
|
|
32
|
-
blank=kwargs.pop('blank', True),
|
|
33
|
-
**kwargs)
|
|
31
|
+
*args, max_length=max_length, **kwargs)
|
|
34
32
|
|
|
35
33
|
@staticmethod
|
|
36
34
|
def get_default_key(obj, filename):
|
|
37
35
|
return f"{type(obj).__name__}/{obj.pk}/{filename}"
|
|
38
36
|
|
|
37
|
+
def clean(self, value, obj):
|
|
38
|
+
def save_file(obj, file, name):
|
|
39
|
+
setattr(obj, self.name, self.upload(obj, file, name))
|
|
40
|
+
obj.save(update_fields={self.name})
|
|
41
|
+
|
|
42
|
+
if isinstance(value, dict):
|
|
43
|
+
toDelete = value.pop('toDelete', None)
|
|
44
|
+
b64 = value.pop('base64', None)
|
|
45
|
+
name = value.get('name')
|
|
46
|
+
if toDelete:
|
|
47
|
+
obj._after_save.append(lambda: self.delete(toDelete))
|
|
48
|
+
value = None
|
|
49
|
+
if b64:
|
|
50
|
+
with urlopen(b64) as response:
|
|
51
|
+
file = response.read()
|
|
52
|
+
value = dict(key=self.get_key(obj, file))
|
|
53
|
+
obj._after_save.append(lambda: save_file(obj, file, name))
|
|
54
|
+
return super().clean(value, obj)
|
|
55
|
+
|
|
39
56
|
def to_python(self, value):
|
|
40
57
|
return super().to_python(self.storage.to_python(value))
|
|
41
58
|
|
|
@@ -52,6 +69,9 @@ class MediaField(models.JSONField):
|
|
|
52
69
|
key = self.get_key(obj, filename)
|
|
53
70
|
return self.to_python(self.storage.upload(key, file, **kwargs))
|
|
54
71
|
|
|
72
|
+
def delete(self, value):
|
|
73
|
+
return self.storage.delete(value)
|
|
74
|
+
|
|
55
75
|
|
|
56
76
|
@receiver(post_delete)
|
|
57
77
|
def post_delete_media(sender, instance, **kwargs):
|
|
@@ -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: 2025-04-
|
|
10
|
+
"POT-Creation-Date: 2025-04-08 13:56+0200\n"
|
|
11
11
|
"PO-Revision-Date: 2021-06-22 23:31+0200\n"
|
|
12
12
|
"Last-Translator: \n"
|
|
13
13
|
"Language-Team: \n"
|
|
@@ -297,7 +297,7 @@ msgstr "{model} {obj} créé."
|
|
|
297
297
|
msgid "{model} {obj} updated."
|
|
298
298
|
msgstr "{model} {obj} modifié."
|
|
299
299
|
|
|
300
|
-
#: views/rest_views.py:
|
|
300
|
+
#: views/rest_views.py:1169
|
|
301
301
|
#, python-brace-format
|
|
302
302
|
msgid "{model} {obj} deleted."
|
|
303
303
|
msgstr "{model} {obj} supprimé."
|
|
@@ -40,7 +40,7 @@ class LocalStorage:
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
def delete(self, value):
|
|
43
|
-
if 'key' not in value:
|
|
43
|
+
if not value or 'key' not in value:
|
|
44
44
|
return value # pragma: no cover
|
|
45
45
|
path = Path(settings.STORAGE_LOCAL_ROOT, value['key'])
|
|
46
46
|
path.unlink(missing_ok=True)
|
|
@@ -48,6 +48,8 @@ class LocalStorage:
|
|
|
48
48
|
path = path.parent
|
|
49
49
|
if path == Path(settings.STORAGE_LOCAL_ROOT):
|
|
50
50
|
break
|
|
51
|
+
if not path.exists():
|
|
52
|
+
break
|
|
51
53
|
if any(path.iterdir()):
|
|
52
54
|
break
|
|
53
55
|
try:
|
|
@@ -43,6 +43,8 @@ class FieldType:
|
|
|
43
43
|
|
|
44
44
|
MODEL_FIELD_BINDING = [
|
|
45
45
|
(pfx_fields.MinutesDurationField, MinutesDurationField),
|
|
46
|
+
(pfx_fields.MediaField, MediaField),
|
|
47
|
+
(pfx_fields.DecimalField, DecimalField),
|
|
46
48
|
(models.BooleanField, BooleanField),
|
|
47
49
|
(models.IntegerField, IntegerField),
|
|
48
50
|
(models.FloatField, FloatField),
|
|
@@ -143,14 +145,24 @@ class ViewField:
|
|
|
143
145
|
return self.json_repr(value)
|
|
144
146
|
return value.json_repr()
|
|
145
147
|
|
|
146
|
-
def to_json(self, obj):
|
|
148
|
+
def to_json(self, view, obj):
|
|
147
149
|
value = self.get_value(obj)
|
|
148
|
-
|
|
149
|
-
return pfx_fields.MinutesDurationField.to_json(value)
|
|
150
|
+
|
|
150
151
|
if self.field_type == FieldType.ModelObject:
|
|
151
152
|
return self._json_repr(value)
|
|
152
153
|
if self.field_type == FieldType.ModelObjectList:
|
|
153
154
|
return [self._json_repr(o) for o in value.all()]
|
|
155
|
+
|
|
156
|
+
if self.json_repr:
|
|
157
|
+
return self.json_repr(value)
|
|
158
|
+
|
|
159
|
+
if self.field_type == FieldType.MediaField:
|
|
160
|
+
if value:
|
|
161
|
+
api_url = view._rest_view_path
|
|
162
|
+
value['url'] = f'{api_url}/{obj.pk}/{self.name}'
|
|
163
|
+
return value
|
|
164
|
+
else:
|
|
165
|
+
return None
|
|
154
166
|
if self.choices:
|
|
155
167
|
if value in self.choices:
|
|
156
168
|
return dict(value=value, label=str(self.choices[value]))
|
|
@@ -295,6 +307,8 @@ class ViewModelField(ViewField):
|
|
|
295
307
|
self, name, model_field=None, **kwargs):
|
|
296
308
|
super().__init__(name, **kwargs)
|
|
297
309
|
self.model_field = model_field
|
|
310
|
+
if hasattr(model_field, 'to_json'):
|
|
311
|
+
self.json_repr = model_field.to_json
|
|
298
312
|
if (hasattr(self.model_field, 'related_model') and
|
|
299
313
|
self.model_field.related_model and not self.related_model):
|
|
300
314
|
self.related_model = self.model_field.related_model
|
|
@@ -291,7 +291,7 @@ class ModelResponseMixin(ModelMixin):
|
|
|
291
291
|
:rtype: :class:`JsonResponse`
|
|
292
292
|
"""
|
|
293
293
|
return JsonResponse(self.serialize_object(o, **{
|
|
294
|
-
_f.alias: _f.to_json(o)
|
|
294
|
+
_f.alias: _f.to_json(self, o)
|
|
295
295
|
for _f in self.get_fields().values()}, meta=meta))
|
|
296
296
|
|
|
297
297
|
def validate(self, obj, rel_data=None, created=False, **kwargs):
|
|
@@ -339,11 +339,15 @@ class ModelResponseMixin(ModelMixin):
|
|
|
339
339
|
:returns: The JSON response
|
|
340
340
|
:rtype: :class:`JsonResponse`
|
|
341
341
|
"""
|
|
342
|
-
if rel_data:
|
|
342
|
+
if rel_data or obj._after_save:
|
|
343
343
|
with transaction.atomic():
|
|
344
344
|
obj.save()
|
|
345
|
-
|
|
346
|
-
|
|
345
|
+
if rel_data:
|
|
346
|
+
for k, v in rel_data.items():
|
|
347
|
+
getattr(obj, k).set(v)
|
|
348
|
+
for callback in obj._after_save:
|
|
349
|
+
callback()
|
|
350
|
+
obj._after_save = []
|
|
347
351
|
else:
|
|
348
352
|
obj.save()
|
|
349
353
|
obj = self.get_object(pk=obj.pk)
|
|
@@ -754,7 +758,7 @@ class ListRestViewMixin(ModelResponseMixin):
|
|
|
754
758
|
*self.get_list_fields_prefetch_related())
|
|
755
759
|
for o in qs:
|
|
756
760
|
yield self.serialize_object(o, **{
|
|
757
|
-
_f.alias: _f.to_json(o)
|
|
761
|
+
_f.alias: _f.to_json(self, o)
|
|
758
762
|
for _f in self.get_list_fields().values()})
|
|
759
763
|
|
|
760
764
|
def get_short_list_result(self, qs):
|
|
@@ -8,7 +8,7 @@ msgid ""
|
|
|
8
8
|
msgstr ""
|
|
9
9
|
"Project-Id-Version: PACKAGE VERSION\n"
|
|
10
10
|
"Report-Msgid-Bugs-To: \n"
|
|
11
|
-
"POT-Creation-Date: 2025-
|
|
11
|
+
"POT-Creation-Date: 2025-04-08 13:56+0200\n"
|
|
12
12
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
|
13
13
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
|
14
14
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
|
@@ -18,11 +18,11 @@ msgstr ""
|
|
|
18
18
|
"Content-Transfer-Encoding: 8bit\n"
|
|
19
19
|
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
|
20
20
|
|
|
21
|
-
#: models.py:
|
|
21
|
+
#: models.py:69
|
|
22
22
|
msgid "First Name"
|
|
23
23
|
msgstr "Prénom"
|
|
24
24
|
|
|
25
|
-
#: models.py:
|
|
25
|
+
#: models.py:70
|
|
26
26
|
msgid "Last Name"
|
|
27
27
|
msgstr "Nom"
|
|
28
28
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Generated by Django 4.2.17 on 2025-04-08 05:19
|
|
2
|
+
# flake8: noqa
|
|
3
|
+
|
|
4
|
+
from django.db import migrations
|
|
5
|
+
|
|
6
|
+
import pfx.pfxcore.fields.media_field
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Migration(migrations.Migration):
|
|
10
|
+
|
|
11
|
+
dependencies = [
|
|
12
|
+
('tests', '0001_initial'),
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
operations = [
|
|
16
|
+
migrations.AlterField(
|
|
17
|
+
model_name='book',
|
|
18
|
+
name='cover',
|
|
19
|
+
field=pfx.pfxcore.fields.media_field.MediaField(blank=True, max_length=255, null=True, verbose_name='Cover'),
|
|
20
|
+
),
|
|
21
|
+
]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Generated by Django 4.2.17 on 2025-04-08 06:36
|
|
2
|
+
# flake8: noqa
|
|
3
|
+
|
|
4
|
+
from django.db import migrations
|
|
5
|
+
|
|
6
|
+
import pfx.pfxcore.fields.media_field
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Migration(migrations.Migration):
|
|
10
|
+
|
|
11
|
+
dependencies = [
|
|
12
|
+
('tests', '0002_alter_book_cover'),
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
operations = [
|
|
16
|
+
migrations.AddField(
|
|
17
|
+
model_name='book',
|
|
18
|
+
name='local_file',
|
|
19
|
+
field=pfx.pfxcore.fields.media_field.MediaField(blank=True, max_length=255, null=True, verbose_name='Cover'),
|
|
20
|
+
),
|
|
21
|
+
]
|
|
@@ -18,6 +18,7 @@ from pfx.pfxcore.models import (
|
|
|
18
18
|
UniqueConstraint,
|
|
19
19
|
UserFilteredQuerySetMixin,
|
|
20
20
|
)
|
|
21
|
+
from pfx.pfxcore.storage import LocalStorage
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
class User(CacheableMixin, OtpUserMixin, AbstractPFXUser):
|
|
@@ -145,7 +146,11 @@ class Book(CacheDependsMixin, PFXModelMixin, models.Model):
|
|
|
145
146
|
rating = models.FloatField("Rating", null=True, blank=True)
|
|
146
147
|
reference = models.CharField(
|
|
147
148
|
"Reference", max_length=30, null=True, blank=True)
|
|
148
|
-
cover = MediaField(
|
|
149
|
+
cover = MediaField(
|
|
150
|
+
"Cover", auto_delete=True, blank=True, null=True)
|
|
151
|
+
local_file = MediaField(
|
|
152
|
+
"Cover", auto_delete=True, blank=True, null=True,
|
|
153
|
+
storage=LocalStorage())
|
|
149
154
|
read_time = MinutesDurationField("Read Time", null=True, blank=True)
|
|
150
155
|
|
|
151
156
|
class Meta:
|
|
@@ -7,6 +7,7 @@ from .test_body_mixin import TestBodyMixin
|
|
|
7
7
|
from .test_cache import TestCache
|
|
8
8
|
from .test_client import TestApiClient
|
|
9
9
|
from .test_fields_choices import TestFieldsChoices
|
|
10
|
+
from .test_fields_decimal import TestFieldsDecimal
|
|
10
11
|
from .test_fields_minutes_duration import TestFieldsMinutesDuration
|
|
11
12
|
from .test_fields_nh3 import TestFieldsNh3
|
|
12
13
|
from .test_filters import FiltersTest
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from datetime import date
|
|
2
|
+
from pathlib import Path
|
|
2
3
|
from unittest.mock import MagicMock, patch
|
|
3
4
|
|
|
4
5
|
from django.db import connection
|
|
@@ -8,6 +9,9 @@ from pfx.pfxcore.test import APIClient, MockBoto3Client, TestAssertMixin
|
|
|
8
9
|
from tests.models import Author, Book, BookType
|
|
9
10
|
|
|
10
11
|
|
|
12
|
+
@override_settings(
|
|
13
|
+
STORAGE_LOCAL_ROOT='/tmp/django-pfx-filestore',
|
|
14
|
+
STORAGE_LOCAL_X_ACCEL_REDIRECT=False)
|
|
11
15
|
class BasicAPITest(TestAssertMixin, TestCase):
|
|
12
16
|
|
|
13
17
|
def setUp(self):
|
|
@@ -704,6 +708,59 @@ class BasicAPITest(TestAssertMixin, TestCase):
|
|
|
704
708
|
self.assertJE(response, 'types.@0.pk', self.type_sf.pk)
|
|
705
709
|
self.assertJE(response, 'types.@1.pk', self.type_fantastique.pk)
|
|
706
710
|
|
|
711
|
+
def test_create_file(self):
|
|
712
|
+
response = self.client.post('/api/books', dict(
|
|
713
|
+
name="Test Book",
|
|
714
|
+
author=self.author1.pk,
|
|
715
|
+
pub_date='2000-01-01',
|
|
716
|
+
local_file=dict(
|
|
717
|
+
name='test.txt',
|
|
718
|
+
base64='data:text/plain;charset=utf-8;'
|
|
719
|
+
'base64,VGVzdCBjb250ZW50')))
|
|
720
|
+
book_pk = self.get_val(response, 'pk')
|
|
721
|
+
self.assertRC(response, 200)
|
|
722
|
+
self.assertJE(response, 'local_file.content-length', '12')
|
|
723
|
+
self.assertJE(response, 'local_file.content-type', 'text/plain')
|
|
724
|
+
self.assertJE(
|
|
725
|
+
response, 'local_file.key',
|
|
726
|
+
f"Book/{book_pk}/bca20547e94049e1ffea27223581c567022a5774.txt")
|
|
727
|
+
self.assertJE(response, 'local_file.name', 'test.txt')
|
|
728
|
+
self.assertJE(
|
|
729
|
+
response, 'local_file.url', f"/books/{book_pk}/local_file")
|
|
730
|
+
self.assertTrue(Path(
|
|
731
|
+
f'/tmp/django-pfx-filestore/Book/{book_pk}/'
|
|
732
|
+
'bca20547e94049e1ffea27223581c567022a5774.txt').exists())
|
|
733
|
+
|
|
734
|
+
# Replace file
|
|
735
|
+
old_file = self.get_val(response, 'local_file')
|
|
736
|
+
response = self.client.put(f'/api/books/{book_pk}', dict(
|
|
737
|
+
local_file=dict(
|
|
738
|
+
name='test2.txt',
|
|
739
|
+
base64='data:text/plain;charset=utf-8;'
|
|
740
|
+
'base64,TmV3IGNvbnRlbnQ=',
|
|
741
|
+
toDelete=old_file)))
|
|
742
|
+
self.assertRC(response, 200)
|
|
743
|
+
self.assertJE(
|
|
744
|
+
response, 'local_file.key',
|
|
745
|
+
f"Book/{book_pk}/af9f06b2b3b1546ac44f4a02994d0ef09e074b91.txt")
|
|
746
|
+
self.assertJE(response, 'local_file.name', 'test2.txt')
|
|
747
|
+
self.assertFalse(Path(
|
|
748
|
+
f'/tmp/django-pfx-filestore/Book/{book_pk}/'
|
|
749
|
+
'bca20547e94049e1ffea27223581c567022a5774.txt').exists())
|
|
750
|
+
self.assertTrue(Path(
|
|
751
|
+
f'/tmp/django-pfx-filestore/Book/{book_pk}/'
|
|
752
|
+
'af9f06b2b3b1546ac44f4a02994d0ef09e074b91.txt').exists())
|
|
753
|
+
|
|
754
|
+
# Delete file
|
|
755
|
+
old_file = self.get_val(response, 'local_file')
|
|
756
|
+
response = self.client.put(f'/api/books/{book_pk}', dict(
|
|
757
|
+
local_file=dict(toDelete=old_file)))
|
|
758
|
+
self.assertRC(response, 200)
|
|
759
|
+
self.assertJE(response, 'local_file', None)
|
|
760
|
+
self.assertFalse(Path(
|
|
761
|
+
f'/tmp/django-pfx-filestore/Book/{book_pk}/'
|
|
762
|
+
'af9f06b2b3b1546ac44f4a02994d0ef09e074b91.txt').exists())
|
|
763
|
+
|
|
707
764
|
def test_create_null_values(self):
|
|
708
765
|
response = self.client.post(
|
|
709
766
|
'/api/books', dict(
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from decimal import Decimal
|
|
2
|
+
|
|
3
|
+
from django.db import connection, models
|
|
4
|
+
from django.test import TestCase
|
|
5
|
+
from django.test.utils import override_settings
|
|
6
|
+
from django.urls import include, path
|
|
7
|
+
|
|
8
|
+
from pfx.pfxcore import register_views
|
|
9
|
+
from pfx.pfxcore.decorator import rest_view
|
|
10
|
+
from pfx.pfxcore.fields import DecimalField
|
|
11
|
+
from pfx.pfxcore.models import JSONReprMixin
|
|
12
|
+
from pfx.pfxcore.test import APIClient, TestAssertMixin
|
|
13
|
+
from pfx.pfxcore.views import RestView
|
|
14
|
+
from tests.views import FakeViewMixin
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TestDecimalModel(JSONReprMixin, models.Model):
|
|
18
|
+
decimal = DecimalField(
|
|
19
|
+
max_digits=10, decimal_places=5, json_decimal_places=2)
|
|
20
|
+
|
|
21
|
+
class Meta:
|
|
22
|
+
verbose_name = "TestModel"
|
|
23
|
+
verbose_name_plural = "TestModels"
|
|
24
|
+
ordering = ['pk']
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@rest_view("/test-decimal-model")
|
|
28
|
+
class DecimalModelRestView(FakeViewMixin, RestView):
|
|
29
|
+
default_public = True
|
|
30
|
+
model = TestDecimalModel
|
|
31
|
+
fields = ['decimal']
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
urlpatterns = [
|
|
35
|
+
path('api/', include(register_views(DecimalModelRestView))),
|
|
36
|
+
path('api/', include('pfx.pfxcore.urls'))
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@override_settings(ROOT_URLCONF=__name__)
|
|
41
|
+
class TestFieldsDecimal(TestAssertMixin, TestCase):
|
|
42
|
+
|
|
43
|
+
def setUp(self):
|
|
44
|
+
self.client = APIClient(default_locale='en')
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def setUpTestData(cls):
|
|
48
|
+
with connection.schema_editor() as schema_editor:
|
|
49
|
+
schema_editor.create_model(TestDecimalModel)
|
|
50
|
+
|
|
51
|
+
def test_decimal(self):
|
|
52
|
+
t = TestDecimalModel.objects.create(decimal=3.14)
|
|
53
|
+
t.save()
|
|
54
|
+
t.refresh_from_db()
|
|
55
|
+
self.assertEqual(t.decimal, Decimal('3.14000'))
|
|
56
|
+
|
|
57
|
+
response = self.client.get(f'/api/test-decimal-model/{t.pk}')
|
|
58
|
+
self.assertRC(response, 200)
|
|
59
|
+
self.assertJE(response, 'decimal', "3.14")
|
|
@@ -88,7 +88,7 @@ class ViewFieldTest(TestAssertMixin, TestCase):
|
|
|
88
88
|
FieldType.MinutesDurationField)
|
|
89
89
|
self.assertEqual(
|
|
90
90
|
FieldType.from_model_field(pfx_fields.MediaField),
|
|
91
|
-
FieldType.
|
|
91
|
+
FieldType.MediaField)
|
|
92
92
|
|
|
93
93
|
def test_field_by_name(self):
|
|
94
94
|
field = ViewField.from_name(Book, 'name')
|
|
@@ -218,7 +218,7 @@ class BookRestViewMixin():
|
|
|
218
218
|
model = Book
|
|
219
219
|
fields = [
|
|
220
220
|
'name', 'author', 'pub_date', VF('created_at', readonly=True),
|
|
221
|
-
'type', 'cover', 'pages', 'rating', 'author__last_name',
|
|
221
|
+
'type', 'cover', 'local_file', 'pages', 'rating', 'author__last_name',
|
|
222
222
|
'read_time']
|
|
223
223
|
filters = [
|
|
224
224
|
FilterGroup('custom', _("Custom"), [
|
|
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.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/fields/minutes_duration_field.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.dev110 → django_pfx-1.4.dev114}/pfx/pfxcore/management/commands/makeapidoc.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|