django-pfx 1.4.dev144__tar.gz → 1.4.dev146__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.dev144 → django_pfx-1.4.dev146}/PKG-INFO +1 -1
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/django_pfx.egg-info/PKG-INFO +1 -1
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/django_pfx.egg-info/SOURCES.txt +1 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.po +5 -5
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/models/pfx_models.py +1 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/views/fields.py +61 -12
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/views/rest_views.py +53 -24
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/__init__.py +1 -0
- django_pfx-1.4.dev146/tests/tests/test_fields_one2many.py +178 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/.gitignore +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/.gitlab-ci.yml +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/.pre-commit-config.yaml +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/LICENSE +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/MANIFEST.in +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/README.md +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/django_pfx.egg-info/dependency_links.txt +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/django_pfx.egg-info/requires.txt +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/django_pfx.egg-info/top_level.txt +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/doc/Makefile +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/doc/conf.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/doc/index.rst +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/doc/source/api.views.rst +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/doc/source/authentication.md +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/doc/source/decorator.md +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/doc/source/generate_openapi.md +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/doc/source/getting_started.md +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/doc/source/internationalisation.md +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/doc/source/model.md +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/doc/source/pfx_views.md +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/doc/source/profiling.md +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/doc/source/settings.md +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/doc/source/testing.md +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/img/pfx.png +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/img/pfx.svg +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/make_messages +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/manage.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/apidoc/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/apidoc/parameters.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/apidoc/schema.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/apidoc/tags.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/apps.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/decorator/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/decorator/rest.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/default_settings.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/exceptions.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/fields/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/fields/decimal_field.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/fields/media_field.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/fields/minutes_duration_field.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/fields/nh3_field.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/http/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/http/json_response.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.mo +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/management/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/management/commands/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/management/commands/makeapidoc.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/management/commands/profile.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/middleware/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/middleware/authentication.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/middleware/locale.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/middleware/profiling.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/migrations/0001_initial.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/migrations/0002_pfxpermissionsuser.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/migrations/0003_delete_pfxpermissionsuser.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/migrations/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/migrations/operations/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/migrations/operations/permissions.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/models/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/models/abstract_pfx_base_user.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/models/cache_mixins.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/models/login_ban.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/models/not_null_fields.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/models/ordered_model_mixin.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/models/otp_user_mixin.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/models/pfx_user.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/models/user_filtered_queryset_mixin.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/serializers/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/serializers/json.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/settings.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/shortcuts.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/storage/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/storage/exceptions.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/storage/local_storage.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/storage/s3_storage.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/templates/registration/otp_code_email.txt +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/templates/registration/otp_code_subject.txt +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/templates/registration/password_reset_email.txt +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/templates/registration/password_reset_subject.txt +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/templates/registration/welcome_email.txt +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/templates/registration/welcome_subject.txt +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/test.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/urls.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/views/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/views/authentication_views.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/views/filters_views.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/views/locale_views.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/views/media_rest_view_mixin.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/views/ordered_rest_view_mixin.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/views/parameters/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/views/parameters/date_format.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/views/parameters/groups.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/views/parameters/list_count.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/views/parameters/list_items.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/views/parameters/list_mode.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/views/parameters/list_order.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/views/parameters/list_search.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/views/parameters/media_redirect.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/views/parameters/meta_fields.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/views/parameters/meta_filters.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/views/parameters/meta_orders.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/views/parameters/subset.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/views/parameters/subset_limit.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/views/parameters/subset_offset.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/views/parameters/subset_page.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/views/parameters/subset_page_size.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/views/parameters/subset_page_subset.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/settings/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/settings/dev.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pyproject.toml +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/requirements.txt +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/serve-doc +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/setup.cfg +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/setup.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/apps.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/locale/fr/LC_MESSAGES/django.po +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/migrations/0001_initial.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/migrations/0002_alter_book_cover.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/migrations/0003_book_local_file.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/migrations/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/models.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/settings/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/settings/ci.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/settings/common.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/settings/dev.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/settings/dev_custom_example.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/settings/dev_default.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/basic_api_errors.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/basic_api_test.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/test_api_doc.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/test_api_doc_search.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/test_auth_api.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/test_body_mixin.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/test_cache.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/test_client.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/test_fields_choices.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/test_fields_date.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/test_fields_decimal.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/test_fields_minutes_duration.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/test_fields_nh3.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/test_filters.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/test_locale_api.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/test_ordered_rest_view_mixin.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/test_perm_tests.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/test_permissions.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/test_perms_api.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/test_post_migrate_groups_update.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/test_profiling_middleware.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/test_settings.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/test_shortcuts.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/test_timezone_middleware.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/test_tools.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/test_user_queryset.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/test_view_decorators.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/tests/test_view_fields.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/urls.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests/views.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_base_user/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_base_user/migrations/0001_initial.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_base_user/migrations/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_base_user/settings/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_base_user/settings/ci.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_base_user/settings/common.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_base_user/settings/dev.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_base_user/settings/dev_custom_example.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_base_user/settings/dev_default.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_base_user/tests/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_base_user/tests/test_api.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_base_user/tests/test_auth_api.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_base_user/urls.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_base_user/views.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_custom_user/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_custom_user/migrations/0001_initial.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_custom_user/migrations/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_custom_user/models.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_custom_user/settings/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_custom_user/settings/ci.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_custom_user/settings/common.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_custom_user/settings/dev.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_custom_user/settings/dev_custom_example.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_custom_user/settings/dev_default.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_custom_user/tests/__init__.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_custom_user/tests/test_api.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_custom_user/tests/test_auth_api.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/tests_custom_user/urls.py +0 -0
- {django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/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.dev146
|
|
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.dev146
|
|
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
|
|
@@ -150,6 +150,7 @@ tests/tests/test_fields_date.py
|
|
|
150
150
|
tests/tests/test_fields_decimal.py
|
|
151
151
|
tests/tests/test_fields_minutes_duration.py
|
|
152
152
|
tests/tests/test_fields_nh3.py
|
|
153
|
+
tests/tests/test_fields_one2many.py
|
|
153
154
|
tests/tests/test_filters.py
|
|
154
155
|
tests/tests/test_locale_api.py
|
|
155
156
|
tests/tests/test_ordered_rest_view_mixin.py
|
|
@@ -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-09-
|
|
10
|
+
"POT-Creation-Date: 2025-09-11 10:12+0200\n"
|
|
11
11
|
"PO-Revision-Date: 2021-06-22 23:31+0200\n"
|
|
12
12
|
"Last-Translator: \n"
|
|
13
13
|
"Language-Team: \n"
|
|
@@ -286,23 +286,23 @@ msgstr "Le paramètre object est requis pour move={move}."
|
|
|
286
286
|
msgid "object {pk} does not exists in this move context."
|
|
287
287
|
msgstr "object {pk} n'existe pas dans ce contexte de déplacement."
|
|
288
288
|
|
|
289
|
-
#: views/rest_views.py:
|
|
289
|
+
#: views/rest_views.py:220
|
|
290
290
|
#, python-brace-format
|
|
291
291
|
msgid "{obj} cannot be deleted because it is referenced by other objects."
|
|
292
292
|
msgstr ""
|
|
293
293
|
"{obj} ne peut pas être supprimé car il est référencé par d’autres objets."
|
|
294
294
|
|
|
295
|
-
#: views/rest_views.py:
|
|
295
|
+
#: views/rest_views.py:344
|
|
296
296
|
#, python-brace-format
|
|
297
297
|
msgid "{model} {obj} created."
|
|
298
298
|
msgstr "{model} {obj} créé."
|
|
299
299
|
|
|
300
|
-
#: views/rest_views.py:
|
|
300
|
+
#: views/rest_views.py:345
|
|
301
301
|
#, python-brace-format
|
|
302
302
|
msgid "{model} {obj} updated."
|
|
303
303
|
msgstr "{model} {obj} modifié."
|
|
304
304
|
|
|
305
|
-
#: views/rest_views.py:
|
|
305
|
+
#: views/rest_views.py:1213
|
|
306
306
|
#, python-brace-format
|
|
307
307
|
msgid "{model} {obj} deleted."
|
|
308
308
|
msgstr "{model} {obj} supprimé."
|
|
@@ -97,8 +97,8 @@ class ViewField:
|
|
|
97
97
|
def __init__(
|
|
98
98
|
self, name, verbose_name=None, alias=None, field_type=None,
|
|
99
99
|
readonly=False, readonly_create=False, readonly_update=False,
|
|
100
|
-
choices=None, json_repr=None,
|
|
101
|
-
related_model=None, related_model_api=None,
|
|
100
|
+
choices=None, json_repr=None, media_field_api=None,
|
|
101
|
+
related_model=None, related_model_api=None, related_fields=None,
|
|
102
102
|
select_related=None, prefetch_related=None):
|
|
103
103
|
self.name = name
|
|
104
104
|
self.alias = alias or name
|
|
@@ -108,8 +108,13 @@ class ViewField:
|
|
|
108
108
|
self.field_type = field_type
|
|
109
109
|
self.choices = dict(choices or [])
|
|
110
110
|
self.json_repr = json_repr
|
|
111
|
+
self.media_field_api = media_field_api
|
|
111
112
|
self.related_model = related_model
|
|
112
113
|
self.related_model_api = related_model_api
|
|
114
|
+
self.related_fields = None
|
|
115
|
+
if self.related_model and related_fields:
|
|
116
|
+
self.related_fields = process_fields(
|
|
117
|
+
self.related_model, related_fields)
|
|
113
118
|
self.select_related = select_related or []
|
|
114
119
|
self.prefetch_related = prefetch_related or []
|
|
115
120
|
self.model_field = None
|
|
@@ -126,6 +131,9 @@ class ViewField:
|
|
|
126
131
|
res['model'] = self.related_model._meta.label
|
|
127
132
|
res['api'] = self.related_model_api or getattr(
|
|
128
133
|
self.related_model, 'api', None)
|
|
134
|
+
if self.related_fields:
|
|
135
|
+
res['fields'] = {
|
|
136
|
+
n: f.meta() for n, f in self.related_fields.items()}
|
|
129
137
|
res['readonly'] = dict(
|
|
130
138
|
post=self.readonly_create,
|
|
131
139
|
put=self.readonly_update)
|
|
@@ -144,27 +152,39 @@ class ViewField:
|
|
|
144
152
|
return None
|
|
145
153
|
return getattr(obj, name)
|
|
146
154
|
|
|
147
|
-
def
|
|
155
|
+
def _related_json_repr(self, value):
|
|
148
156
|
if not value:
|
|
149
157
|
return None
|
|
150
158
|
if self.json_repr:
|
|
151
|
-
|
|
152
|
-
|
|
159
|
+
vals = self.json_repr(value)
|
|
160
|
+
else:
|
|
161
|
+
vals = value.json_repr()
|
|
162
|
+
if self.related_fields:
|
|
163
|
+
vals.update({
|
|
164
|
+
n: f.to_json(value) for n, f in self.related_fields.items()})
|
|
165
|
+
return vals
|
|
153
166
|
|
|
154
|
-
def to_json(self,
|
|
167
|
+
def to_json(self, obj, view=None):
|
|
155
168
|
value = self.get_value(obj)
|
|
156
169
|
|
|
157
170
|
if self.field_type == FieldType.ModelObject:
|
|
158
|
-
return self.
|
|
171
|
+
return self._related_json_repr(value)
|
|
159
172
|
if self.field_type == FieldType.ModelObjectList:
|
|
160
|
-
return [self.
|
|
173
|
+
return [self._related_json_repr(o) for o in value.all()]
|
|
161
174
|
|
|
162
175
|
if self.json_repr:
|
|
163
176
|
return self.json_repr(value)
|
|
164
177
|
|
|
165
178
|
if self.field_type == FieldType.MediaField:
|
|
166
179
|
if value:
|
|
167
|
-
|
|
180
|
+
if self.media_field_api:
|
|
181
|
+
api_url = self.media_field_api
|
|
182
|
+
elif view:
|
|
183
|
+
api_url = view._rest_view_path
|
|
184
|
+
else:
|
|
185
|
+
raise Exception(
|
|
186
|
+
"media_field_api must be defined if the field "
|
|
187
|
+
"is not a view root field.")
|
|
168
188
|
value['url'] = f'{api_url}/{obj.pk}/{self.name}'
|
|
169
189
|
return value
|
|
170
190
|
else:
|
|
@@ -317,6 +337,7 @@ class ViewField:
|
|
|
317
337
|
class ViewModelField(ViewField):
|
|
318
338
|
def __init__(
|
|
319
339
|
self, name, model_field=None, **kwargs):
|
|
340
|
+
related_fields = kwargs.get('related_fields')
|
|
320
341
|
super().__init__(name, **kwargs)
|
|
321
342
|
self.model_field = model_field
|
|
322
343
|
if hasattr(model_field, 'to_json'):
|
|
@@ -324,6 +345,9 @@ class ViewModelField(ViewField):
|
|
|
324
345
|
if (hasattr(self.model_field, 'related_model') and
|
|
325
346
|
self.model_field.related_model and not self.related_model):
|
|
326
347
|
self.related_model = self.model_field.related_model
|
|
348
|
+
if self.related_model and related_fields:
|
|
349
|
+
self.related_fields = process_fields(
|
|
350
|
+
self.related_model, related_fields)
|
|
327
351
|
if not self.select_related and (
|
|
328
352
|
self.field_type == FieldType.ModelObject):
|
|
329
353
|
# Auto add the field in select_related if select_related
|
|
@@ -350,7 +374,13 @@ class ViewModelField(ViewField):
|
|
|
350
374
|
|
|
351
375
|
def to_model_value(self, value, get_related_queryset):
|
|
352
376
|
def _get_obj(v):
|
|
353
|
-
|
|
377
|
+
if isinstance(v, dict):
|
|
378
|
+
if 'pk' in v:
|
|
379
|
+
pk = v['pk']
|
|
380
|
+
else:
|
|
381
|
+
return self.model_field.related_model()
|
|
382
|
+
else:
|
|
383
|
+
pk = v
|
|
354
384
|
return pk and get_object(
|
|
355
385
|
get_related_queryset(self.model_field.related_model),
|
|
356
386
|
related_field=self.name, pk=pk) or None
|
|
@@ -397,7 +427,7 @@ class VF:
|
|
|
397
427
|
readonly=False, readonly_create=False, readonly_update=False,
|
|
398
428
|
choices=None, select_related=None, prefetch_related=None,
|
|
399
429
|
json_repr=None, related_model=None, related_model_api=None,
|
|
400
|
-
field=None):
|
|
430
|
+
field=None, related_fields=None, media_field_api=None):
|
|
401
431
|
self.kwargs = dict(
|
|
402
432
|
name=name, verbose_name=verbose_name, field_type=field_type,
|
|
403
433
|
alias=alias,
|
|
@@ -405,7 +435,26 @@ class VF:
|
|
|
405
435
|
readonly_update=readonly_update, choices=choices,
|
|
406
436
|
select_related=select_related, prefetch_related=prefetch_related,
|
|
407
437
|
json_repr=json_repr, related_model=related_model,
|
|
408
|
-
related_model_api=related_model_api, field=field
|
|
438
|
+
related_model_api=related_model_api, field=field,
|
|
439
|
+
related_fields=related_fields, media_field_api=media_field_api)
|
|
409
440
|
|
|
410
441
|
def to_field(self, model):
|
|
411
442
|
return ViewField.from_name(model, **self.kwargs)
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def process_fields(model, fields):
|
|
446
|
+
if not fields:
|
|
447
|
+
return {
|
|
448
|
+
_f.name: ViewField.from_model_field(_f.name, _f)
|
|
449
|
+
for _f in model._meta.fields}
|
|
450
|
+
|
|
451
|
+
def _field(e):
|
|
452
|
+
if isinstance(e, ViewField):
|
|
453
|
+
field = e
|
|
454
|
+
elif isinstance(e, VF):
|
|
455
|
+
field = e.to_field(model)
|
|
456
|
+
else:
|
|
457
|
+
field = ViewField.from_name(model, e)
|
|
458
|
+
return field.alias, field
|
|
459
|
+
|
|
460
|
+
return dict(_field(e) for e in fields)
|
|
@@ -36,11 +36,11 @@ from pfx.pfxcore.shortcuts import (
|
|
|
36
36
|
get_object,
|
|
37
37
|
model_permissions,
|
|
38
38
|
)
|
|
39
|
+
from pfx.pfxcore.views.fields import ModelList
|
|
39
40
|
|
|
40
41
|
from . import parameters
|
|
41
|
-
from .fields import VF
|
|
42
42
|
from .fields import FieldType as FT
|
|
43
|
-
from .fields import
|
|
43
|
+
from .fields import process_fields
|
|
44
44
|
|
|
45
45
|
logger = logging.getLogger(__name__)
|
|
46
46
|
|
|
@@ -145,21 +145,7 @@ class ModelMixin():
|
|
|
145
145
|
|
|
146
146
|
@classmethod
|
|
147
147
|
def _process_fields(cls, fields):
|
|
148
|
-
|
|
149
|
-
return {
|
|
150
|
-
_f.name: ViewField.from_model_field(_f.name, _f)
|
|
151
|
-
for _f in cls.model._meta.fields}
|
|
152
|
-
|
|
153
|
-
def _field(e):
|
|
154
|
-
if isinstance(e, ViewField):
|
|
155
|
-
field = e
|
|
156
|
-
elif isinstance(e, VF):
|
|
157
|
-
field = e.to_field(cls.model)
|
|
158
|
-
else:
|
|
159
|
-
field = ViewField.from_name(cls.model, e)
|
|
160
|
-
return field.alias, field
|
|
161
|
-
|
|
162
|
-
return dict(_field(e) for e in fields)
|
|
148
|
+
return process_fields(cls.model, fields)
|
|
163
149
|
|
|
164
150
|
@classmethod
|
|
165
151
|
def get_fields(cls):
|
|
@@ -303,7 +289,7 @@ class ModelResponseMixin(ModelMixin):
|
|
|
303
289
|
:rtype: :class:`JsonResponse`
|
|
304
290
|
"""
|
|
305
291
|
return JsonResponse(self.serialize_object(o, **{
|
|
306
|
-
_f.alias: _f.to_json(
|
|
292
|
+
_f.alias: _f.to_json(o, self)
|
|
307
293
|
for _f in self.get_fields().values()}, meta=meta))
|
|
308
294
|
|
|
309
295
|
def validate(self, obj, rel_data=None, created=False, **kwargs):
|
|
@@ -316,7 +302,34 @@ class ModelResponseMixin(ModelMixin):
|
|
|
316
302
|
:param created: If object instance is created
|
|
317
303
|
:param \\**kwargs: Additional arguments for :code:`full_clean`
|
|
318
304
|
"""
|
|
319
|
-
|
|
305
|
+
def model_lists(rel_data):
|
|
306
|
+
if rel_data:
|
|
307
|
+
for k, v in rel_data.items():
|
|
308
|
+
if isinstance(v, ModelList):
|
|
309
|
+
yield k, v
|
|
310
|
+
|
|
311
|
+
errors = {}
|
|
312
|
+
for k, v in model_lists(rel_data):
|
|
313
|
+
for i, o in enumerate(v):
|
|
314
|
+
save_field = getattr(o, '_save_related', False)
|
|
315
|
+
if save_field:
|
|
316
|
+
try:
|
|
317
|
+
o.full_clean(exclude={save_field})
|
|
318
|
+
except ValidationError as e:
|
|
319
|
+
for mk, ms in e.error_dict.items():
|
|
320
|
+
errors.setdefault(
|
|
321
|
+
f'{k}::{i}::{mk}', []).extend(ms)
|
|
322
|
+
obj._rel_data = rel_data or {}
|
|
323
|
+
if errors:
|
|
324
|
+
try:
|
|
325
|
+
obj._rel_data = rel_data
|
|
326
|
+
obj.full_clean(**kwargs)
|
|
327
|
+
except ValidationError as e:
|
|
328
|
+
for k, ms in e.error_dict.items():
|
|
329
|
+
errors.setdefault(k, []).extend(ms)
|
|
330
|
+
raise ValidationError(errors)
|
|
331
|
+
else:
|
|
332
|
+
obj.full_clean(**kwargs)
|
|
320
333
|
|
|
321
334
|
def is_valid_response_meta(self, obj, created=False):
|
|
322
335
|
"""Prepare the defaut meta for is_valid responce.
|
|
@@ -356,6 +369,12 @@ class ModelResponseMixin(ModelMixin):
|
|
|
356
369
|
obj.save()
|
|
357
370
|
if rel_data:
|
|
358
371
|
for k, v in rel_data.items():
|
|
372
|
+
if isinstance(v, ModelList):
|
|
373
|
+
for o in v:
|
|
374
|
+
save_field = getattr(o, '_save_related', False)
|
|
375
|
+
if save_field:
|
|
376
|
+
setattr(o, save_field, obj)
|
|
377
|
+
o.save()
|
|
359
378
|
getattr(obj, k).set(v)
|
|
360
379
|
self.post_save(obj, created=created, funcs=funcs)
|
|
361
380
|
obj = self.get_object(pk=obj.pk)
|
|
@@ -499,7 +518,7 @@ class ModelBodyMixin(BodyMixin, ModelMixin):
|
|
|
499
518
|
"""
|
|
500
519
|
fields = self.get_fields()
|
|
501
520
|
|
|
502
|
-
def can_write(fname):
|
|
521
|
+
def can_write(fname, fields):
|
|
503
522
|
if fname not in fields:
|
|
504
523
|
return False
|
|
505
524
|
if fields[fname].is_readonly(created=created):
|
|
@@ -512,9 +531,19 @@ class ModelBodyMixin(BodyMixin, ModelMixin):
|
|
|
512
531
|
res = {}
|
|
513
532
|
res_rel = {}
|
|
514
533
|
for k, v in data.items():
|
|
515
|
-
if can_write(k):
|
|
516
|
-
|
|
517
|
-
|
|
534
|
+
if can_write(k, fields):
|
|
535
|
+
field = fields[k]
|
|
536
|
+
mk, mv = field.to_model_value(v, self.get_related_queryset)
|
|
537
|
+
if field.field_type == FT.ModelObjectList:
|
|
538
|
+
if field.related_fields:
|
|
539
|
+
for i, rv in enumerate(v):
|
|
540
|
+
for rk, rf in field.related_fields.items():
|
|
541
|
+
rmk, rmv = rf.to_model_value(
|
|
542
|
+
rv.get(rk), self.get_related_queryset)
|
|
543
|
+
setattr(mv[i], rmk, rmv)
|
|
544
|
+
setattr(
|
|
545
|
+
mv[i], '_save_related',
|
|
546
|
+
field.model_field.field.name)
|
|
518
547
|
res_rel[mk] = mv
|
|
519
548
|
else:
|
|
520
549
|
res[mk] = mv
|
|
@@ -768,7 +797,7 @@ class ListRestViewMixin(ModelResponseMixin):
|
|
|
768
797
|
*self.get_list_fields_prefetch_related())
|
|
769
798
|
for o in qs:
|
|
770
799
|
yield self.serialize_object(o, **{
|
|
771
|
-
_f.alias: _f.to_json(
|
|
800
|
+
_f.alias: _f.to_json(o, self)
|
|
772
801
|
for _f in self.get_list_fields().values()})
|
|
773
802
|
|
|
774
803
|
def get_short_list_result(self, qs):
|
|
@@ -11,6 +11,7 @@ from .test_fields_date import TestFieldsDate
|
|
|
11
11
|
from .test_fields_decimal import TestFieldsDecimal
|
|
12
12
|
from .test_fields_minutes_duration import TestFieldsMinutesDuration
|
|
13
13
|
from .test_fields_nh3 import TestFieldsNh3
|
|
14
|
+
from .test_fields_one2many import TestFieldsOne2Many
|
|
14
15
|
from .test_filters import FiltersTest
|
|
15
16
|
from .test_locale_api import LocaleAPITest
|
|
16
17
|
from .test_ordered_rest_view_mixin import TestOrderedRestViewMixin
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
|
|
2
|
+
from django.db import connection, models
|
|
3
|
+
from django.test import TestCase
|
|
4
|
+
from django.test.utils import override_settings
|
|
5
|
+
from django.urls import include, path
|
|
6
|
+
|
|
7
|
+
from pfx.pfxcore import register_views
|
|
8
|
+
from pfx.pfxcore.decorator import rest_view
|
|
9
|
+
from pfx.pfxcore.models import JSONReprMixin
|
|
10
|
+
from pfx.pfxcore.test import APIClient, TestAssertMixin
|
|
11
|
+
from pfx.pfxcore.views import VF, RestView
|
|
12
|
+
from tests.views import FakeViewMixin
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TestOne2ManyModel(JSONReprMixin, models.Model):
|
|
16
|
+
name = models.CharField(max_length=30)
|
|
17
|
+
|
|
18
|
+
class Meta:
|
|
19
|
+
verbose_name = "TestOne2ManyModel"
|
|
20
|
+
verbose_name_plural = "TestOne2ManyModel"
|
|
21
|
+
ordering = ['pk']
|
|
22
|
+
|
|
23
|
+
def clean_fields(self, exclude=None):
|
|
24
|
+
errors = {}
|
|
25
|
+
|
|
26
|
+
if 'rels' not in exclude and 'rels' in self._rel_data:
|
|
27
|
+
if len(self._rel_data['rels']) < 2:
|
|
28
|
+
errors.setdefault(NON_FIELD_ERRORS, []).append(
|
|
29
|
+
"You have to set minimum 2 rels.")
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
super().clean_fields(exclude)
|
|
33
|
+
except ValidationError as e:
|
|
34
|
+
for k, ms in e.error_dict.items():
|
|
35
|
+
errors.setdefault(k, []).extend(ms)
|
|
36
|
+
|
|
37
|
+
if errors:
|
|
38
|
+
raise ValidationError(errors)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class TestOne2ManyRelModel(JSONReprMixin, models.Model):
|
|
42
|
+
name = models.CharField(max_length=30)
|
|
43
|
+
descr = models.TextField()
|
|
44
|
+
rel = models.ForeignKey(
|
|
45
|
+
TestOne2ManyModel, related_name='rels', on_delete=models.CASCADE)
|
|
46
|
+
|
|
47
|
+
class Meta:
|
|
48
|
+
verbose_name = "TestOne2ManyRelModel"
|
|
49
|
+
verbose_name_plural = "TestOne2ManyRelModels"
|
|
50
|
+
ordering = ['pk']
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@rest_view("/test")
|
|
54
|
+
class TestOne2ManyRestView(FakeViewMixin, RestView):
|
|
55
|
+
default_public = True
|
|
56
|
+
model = TestOne2ManyModel
|
|
57
|
+
|
|
58
|
+
fields = ['name', VF('rels', related_fields=[
|
|
59
|
+
'name', VF('descr', readonly=True)])]
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
urlpatterns = [
|
|
63
|
+
path('api/', include(register_views(TestOne2ManyRestView))),
|
|
64
|
+
path('api/', include('pfx.pfxcore.urls'))
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@override_settings(ROOT_URLCONF=__name__)
|
|
69
|
+
class TestFieldsOne2Many(TestAssertMixin, TestCase):
|
|
70
|
+
|
|
71
|
+
def setUp(self):
|
|
72
|
+
self.client = APIClient(default_locale='en')
|
|
73
|
+
|
|
74
|
+
@classmethod
|
|
75
|
+
def setUpTestData(cls):
|
|
76
|
+
with connection.schema_editor() as schema_editor:
|
|
77
|
+
schema_editor.create_model(TestOne2ManyModel)
|
|
78
|
+
schema_editor.create_model(TestOne2ManyRelModel)
|
|
79
|
+
|
|
80
|
+
def test_meta(self):
|
|
81
|
+
response = self.client.get('/api/test/meta')
|
|
82
|
+
self.assertRC(response, 200)
|
|
83
|
+
fields = 'fields.rels.fields'
|
|
84
|
+
self.assertJE(response, f'{fields}.name.name', "name")
|
|
85
|
+
self.assertJE(response, f'{fields}.name.type', "CharField")
|
|
86
|
+
self.assertJE(response, f'{fields}.name.required', True)
|
|
87
|
+
self.assertJE(response, f'{fields}.name.readonly.post', False)
|
|
88
|
+
self.assertJE(response, f'{fields}.name.readonly.put', False)
|
|
89
|
+
self.assertJE(response, f'{fields}.descr.name', "descr")
|
|
90
|
+
self.assertJE(response, f'{fields}.descr.type', "TextField")
|
|
91
|
+
self.assertJE(response, f'{fields}.descr.required', True)
|
|
92
|
+
self.assertJE(response, f'{fields}.descr.readonly.post', True)
|
|
93
|
+
self.assertJE(response, f'{fields}.descr.readonly.put', True)
|
|
94
|
+
|
|
95
|
+
def test_get(self):
|
|
96
|
+
o = TestOne2ManyModel.objects.create(name="Test")
|
|
97
|
+
r1 = TestOne2ManyRelModel.objects.create(rel=o, name="R1", descr="D1")
|
|
98
|
+
r2 = TestOne2ManyRelModel.objects.create(rel=o, name="R2", descr="D2")
|
|
99
|
+
|
|
100
|
+
response = self.client.get(f'/api/test/{o.pk}')
|
|
101
|
+
self.assertRC(response, 200)
|
|
102
|
+
self.assertSize(response, 'rels', 2)
|
|
103
|
+
self.assertJE(response, 'rels.@0.pk', r1.pk)
|
|
104
|
+
self.assertJE(response, 'rels.@0.name', "R1")
|
|
105
|
+
self.assertJE(response, 'rels.@0.descr', "D1")
|
|
106
|
+
self.assertJE(response, 'rels.@1.pk', r2.pk)
|
|
107
|
+
self.assertJE(response, 'rels.@1.name', "R2")
|
|
108
|
+
self.assertJE(response, 'rels.@1.descr', "D2")
|
|
109
|
+
|
|
110
|
+
def test_post(self):
|
|
111
|
+
response = self.client.post('/api/test', dict(
|
|
112
|
+
name="Test",
|
|
113
|
+
rels=[dict(name="R1", descr="D1"), dict(name="R2", descr="D2")]
|
|
114
|
+
))
|
|
115
|
+
self.assertRC(response, 200)
|
|
116
|
+
self.assertSize(response, 'rels', 2)
|
|
117
|
+
self.assertJE(response, 'rels.@0.name', "R1")
|
|
118
|
+
self.assertJE(response, 'rels.@0.descr', "D1")
|
|
119
|
+
self.assertJE(response, 'rels.@1.name', "R2")
|
|
120
|
+
self.assertJE(response, 'rels.@1.descr', "D2")
|
|
121
|
+
|
|
122
|
+
def test_post_field_errors(self):
|
|
123
|
+
response = self.client.post('/api/test', dict(
|
|
124
|
+
rels=[dict(name="R1", descr="D1"), dict(descr="D2")]
|
|
125
|
+
))
|
|
126
|
+
self.assertRC(response, 422)
|
|
127
|
+
self.assertEqual(
|
|
128
|
+
set(response.json().keys()), {'name', 'rels::1::name'})
|
|
129
|
+
self.assertJE(response, 'name', ["This field cannot be blank."])
|
|
130
|
+
self.assertJE(
|
|
131
|
+
response, 'rels::1::name', ["This field cannot be null."])
|
|
132
|
+
|
|
133
|
+
response = self.client.post('/api/test', dict(
|
|
134
|
+
rels=[dict(descr="D1")]
|
|
135
|
+
))
|
|
136
|
+
self.assertRC(response, 422)
|
|
137
|
+
self.assertEqual(
|
|
138
|
+
set(response.json().keys()), {'__all__', 'name', 'rels::0::name'})
|
|
139
|
+
self.assertJE(response, '__all__', ["You have to set minimum 2 rels."])
|
|
140
|
+
self.assertJE(response, 'name', ["This field cannot be blank."])
|
|
141
|
+
self.assertJE(
|
|
142
|
+
response, 'rels::0::name', ["This field cannot be null."])
|
|
143
|
+
|
|
144
|
+
def test_put(self):
|
|
145
|
+
o = TestOne2ManyModel.objects.create(name="Test")
|
|
146
|
+
r1 = TestOne2ManyRelModel.objects.create(rel=o, name="R1", descr="D1")
|
|
147
|
+
|
|
148
|
+
vals = self.client.get(f'/api/test/{o.pk}').json()
|
|
149
|
+
vals['rels'][0]['name'] = "R1 updated"
|
|
150
|
+
vals['rels'].append({'name': "R2", "descr": "D2"})
|
|
151
|
+
|
|
152
|
+
response = self.client.put(f'/api/test/{o.pk}', vals)
|
|
153
|
+
self.assertRC(response, 200)
|
|
154
|
+
self.assertSize(response, 'rels', 2)
|
|
155
|
+
self.assertJE(response, 'rels.@0.pk', r1.pk)
|
|
156
|
+
self.assertJE(response, 'rels.@0.name', "R1 updated")
|
|
157
|
+
self.assertJE(response, 'rels.@0.descr', "D1")
|
|
158
|
+
self.assertJE(response, 'rels.@1.name', "R2")
|
|
159
|
+
self.assertJE(response, 'rels.@1.descr', "D2")
|
|
160
|
+
|
|
161
|
+
def test_put_field_errors(self):
|
|
162
|
+
o = TestOne2ManyModel.objects.create(name="Test")
|
|
163
|
+
TestOne2ManyRelModel.objects.create(rel=o, name="R1", descr="D1")
|
|
164
|
+
TestOne2ManyRelModel.objects.create(rel=o, name="R2", descr="D2")
|
|
165
|
+
|
|
166
|
+
vals = self.client.get(f'/api/test/{o.pk}').json()
|
|
167
|
+
vals['name'] = ""
|
|
168
|
+
vals['rels'][0]['name'] = None
|
|
169
|
+
del vals['rels'][1]
|
|
170
|
+
|
|
171
|
+
response = self.client.put(f'/api/test/{o.pk}', vals)
|
|
172
|
+
self.assertRC(response, 422)
|
|
173
|
+
self.assertEqual(
|
|
174
|
+
set(response.json().keys()), {'__all__', 'name', 'rels::0::name'})
|
|
175
|
+
self.assertJE(response, '__all__', ["You have to set minimum 2 rels."])
|
|
176
|
+
self.assertJE(response, 'name', ["This field cannot be blank."])
|
|
177
|
+
self.assertJE(
|
|
178
|
+
response, 'rels::0::name', ["This field cannot be null."])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_pfx-1.4.dev144 → django_pfx-1.4.dev146}/pfx/pfxcore/fields/minutes_duration_field.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|