django-pfx 1.7.6.dev41__tar.gz → 1.7.6.dev45__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.
Files changed (332) hide show
  1. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/PKG-INFO +1 -1
  2. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/django_pfx.egg-info/PKG-INFO +1 -1
  3. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/django_pfx.egg-info/SOURCES.txt +2 -0
  4. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/django_pfx.egg-info/scm_file_list.json +2 -0
  5. django_pfx-1.7.6.dev45/django_pfx.egg-info/scm_version.json +8 -0
  6. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/doc/source/api.views.rst +3 -0
  7. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/doc/source/model.md +46 -0
  8. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/fields/media_field.py +25 -12
  9. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.po +2 -2
  10. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/models/pfx_models.py +41 -14
  11. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/test.py +16 -1
  12. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/locale/fr/LC_MESSAGES/django.po +3 -3
  13. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/migrations/0001_initial.py +9 -0
  14. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/models.py +13 -1
  15. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/__init__.py +2 -0
  16. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/basic_api_test.py +48 -16
  17. django_pfx-1.7.6.dev45/tests/tests/test_fields_media_local.py +295 -0
  18. django_pfx-1.7.6.dev45/tests/tests/test_fields_media_s3.py +76 -0
  19. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_table_file_helpers.py +1 -2
  20. django_pfx-1.7.6.dev41/django_pfx.egg-info/scm_version.json +0 -8
  21. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/.gitignore +0 -0
  22. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/.gitlab-ci.yml +0 -0
  23. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/.pre-commit-config.yaml +0 -0
  24. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/LICENSE +0 -0
  25. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/MANIFEST.in +0 -0
  26. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/README.md +0 -0
  27. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/django_pfx.egg-info/dependency_links.txt +0 -0
  28. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/django_pfx.egg-info/requires.txt +0 -0
  29. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/django_pfx.egg-info/top_level.txt +0 -0
  30. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/doc/Makefile +0 -0
  31. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/doc/conf.py +0 -0
  32. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/doc/index.rst +0 -0
  33. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/doc/source/authentication.md +0 -0
  34. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/doc/source/decorator.md +0 -0
  35. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/doc/source/generate_openapi.md +0 -0
  36. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/doc/source/getting_started.md +0 -0
  37. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/doc/source/internationalisation.md +0 -0
  38. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/doc/source/pfx_views.md +0 -0
  39. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/doc/source/profiling.md +0 -0
  40. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/doc/source/settings.md +0 -0
  41. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/doc/source/testing.md +0 -0
  42. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/doc/source/user_jobs.md +0 -0
  43. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/img/pfx.png +0 -0
  44. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/img/pfx.svg +0 -0
  45. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/make_messages +0 -0
  46. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/manage.py +0 -0
  47. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/__init__.py +0 -0
  48. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/__init__.py +0 -0
  49. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/apps.py +0 -0
  50. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/email_backend/__init__.py +0 -0
  51. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/email_backend/memory_email_backend.py +0 -0
  52. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/email_backend/q_mailjet_email_backend.py +0 -0
  53. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/exceptions.py +0 -0
  54. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/locale/fr/LC_MESSAGES/django.mo +0 -0
  55. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/locale/fr/LC_MESSAGES/django.po +0 -0
  56. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/mailjet/__init__.py +0 -0
  57. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/mailjet/mailjet.py +0 -0
  58. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/mailjet/message_result.py +0 -0
  59. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/migrations/0001_initial.py +0 -0
  60. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/migrations/0002_alter_email_headers.py +0 -0
  61. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/migrations/0003_alter_email_options.py +0 -0
  62. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/migrations/0004_alter_email_mail_from.py +0 -0
  63. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/migrations/0005_lowercase_verbose_names.py +0 -0
  64. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/migrations/0006_alter_email_attachments.py +0 -0
  65. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/migrations/__init__.py +0 -0
  66. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/models/__init__.py +0 -0
  67. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/models/email.py +0 -0
  68. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/providers/__init__.py +0 -0
  69. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/providers/email_django_q_executor.py +0 -0
  70. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/shortcuts.py +0 -0
  71. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/tasks.py +0 -0
  72. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/templates/emails/base_template.html +0 -0
  73. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/templates/emails/base_template.txt +0 -0
  74. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/templates/emails/base_template_subject.txt +0 -0
  75. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/test.py +0 -0
  76. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/urls.py +0 -0
  77. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/views/__init__.py +0 -0
  78. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/emails/views/email_rest_view.py +0 -0
  79. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/__init__.py +0 -0
  80. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/apidoc/__init__.py +0 -0
  81. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/apidoc/parameters.py +0 -0
  82. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/apidoc/schema.py +0 -0
  83. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/apidoc/tags.py +0 -0
  84. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/apps.py +0 -0
  85. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/decorator/__init__.py +0 -0
  86. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/decorator/rest.py +0 -0
  87. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/default_settings.py +0 -0
  88. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/exceptions.py +0 -0
  89. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/fields/__init__.py +0 -0
  90. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/fields/decimal_field.py +0 -0
  91. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/fields/minutes_duration_field.py +0 -0
  92. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/fields/rich_text_field.py +0 -0
  93. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/http/__init__.py +0 -0
  94. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/http/json_response.py +0 -0
  95. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.mo +0 -0
  96. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/management/__init__.py +0 -0
  97. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/management/commands/__init__.py +0 -0
  98. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/management/commands/makeapidoc.py +0 -0
  99. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/management/commands/profile.py +0 -0
  100. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/mfa/__init__.py +0 -0
  101. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/mfa/authenticator.py +0 -0
  102. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/mfa/base.py +0 -0
  103. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/mfa/email.py +0 -0
  104. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/mfa/legacy.py +0 -0
  105. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/mfa/sms.py +0 -0
  106. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/middleware/__init__.py +0 -0
  107. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/middleware/authentication.py +0 -0
  108. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/middleware/locale.py +0 -0
  109. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/middleware/profiling.py +0 -0
  110. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/middleware/request_too_big_middleware.py +0 -0
  111. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/middleware/ws_cookie_authentication_middleware.py +0 -0
  112. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/migrations/0001_initial.py +0 -0
  113. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/migrations/0002_pfxpermissionsuser.py +0 -0
  114. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/migrations/0003_delete_pfxpermissionsuser.py +0 -0
  115. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/migrations/0004_alter_loginban_failed_counter_and_more.py +0 -0
  116. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/migrations/0005_mfadevice_mfadevice_mfa_device_unique_backend.py +0 -0
  117. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/migrations/0006_alter_pfxuser_managers.py +0 -0
  118. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/migrations/0007_alter_pfxuser_options.py +0 -0
  119. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/migrations/0008_lowercase_verbose_names.py +0 -0
  120. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/migrations/__init__.py +0 -0
  121. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/migrations/operations/__init__.py +0 -0
  122. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/migrations/operations/permissions.py +0 -0
  123. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/models/__init__.py +0 -0
  124. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/models/abstract_pfx_base_user.py +0 -0
  125. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/models/attachment_mixin.py +0 -0
  126. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/models/cache_mixins.py +0 -0
  127. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/models/login_ban.py +0 -0
  128. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/models/mfa_device.py +0 -0
  129. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/models/mfa_user_mixin.py +0 -0
  130. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/models/not_null_fields.py +0 -0
  131. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/models/ordered_model_mixin.py +0 -0
  132. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/models/pfx_user.py +0 -0
  133. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/models/user_filtered_queryset_mixin.py +0 -0
  134. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/serializers/__init__.py +0 -0
  135. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/serializers/json.py +0 -0
  136. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/settings.py +0 -0
  137. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/shortcuts.py +0 -0
  138. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/sms/__init__.py +0 -0
  139. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/sms/backends/__init__.py +0 -0
  140. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/sms/backends/base.py +0 -0
  141. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/sms/backends/console.py +0 -0
  142. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/sms/backends/memory.py +0 -0
  143. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/storage/__init__.py +0 -0
  144. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/storage/exceptions.py +0 -0
  145. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/storage/local_storage.py +0 -0
  146. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/storage/s3_storage.py +0 -0
  147. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/templates/registration/otp_code_email.txt +0 -0
  148. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/templates/registration/otp_code_subject.txt +0 -0
  149. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/templates/registration/password_reset_email.txt +0 -0
  150. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/templates/registration/password_reset_subject.txt +0 -0
  151. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/templates/registration/welcome_email.txt +0 -0
  152. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/templates/registration/welcome_subject.txt +0 -0
  153. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/urls.py +0 -0
  154. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/version_check.py +0 -0
  155. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/__init__.py +0 -0
  156. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/authentication_views.py +0 -0
  157. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/fields.py +0 -0
  158. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/filters_views.py +0 -0
  159. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/locale_views.py +0 -0
  160. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/media_rest_view_mixin.py +0 -0
  161. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/mfa_authentication_views.py +0 -0
  162. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/ordered_rest_view_mixin.py +0 -0
  163. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/parameters/__init__.py +0 -0
  164. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/parameters/date_format.py +0 -0
  165. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/parameters/groups.py +0 -0
  166. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/parameters/list_count.py +0 -0
  167. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/parameters/list_items.py +0 -0
  168. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/parameters/list_mode.py +0 -0
  169. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/parameters/list_order.py +0 -0
  170. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/parameters/list_search.py +0 -0
  171. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/parameters/media_redirect.py +0 -0
  172. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/parameters/meta_fields.py +0 -0
  173. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/parameters/meta_filters.py +0 -0
  174. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/parameters/meta_orders.py +0 -0
  175. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/parameters/subset.py +0 -0
  176. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/parameters/subset_limit.py +0 -0
  177. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/parameters/subset_offset.py +0 -0
  178. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/parameters/subset_page.py +0 -0
  179. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/parameters/subset_page_size.py +0 -0
  180. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/parameters/subset_page_subset.py +0 -0
  181. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/views/rest_views.py +0 -0
  182. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/pfxcore/xml_test_runner.py +0 -0
  183. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/settings/__init__.py +0 -0
  184. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/settings/dev.py +0 -0
  185. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/userjobs/__init__.py +0 -0
  186. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/userjobs/apps.py +0 -0
  187. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/userjobs/locale/fr/LC_MESSAGES/django.mo +0 -0
  188. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/userjobs/locale/fr/LC_MESSAGES/django.po +0 -0
  189. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/userjobs/migrations/0001_initial.py +0 -0
  190. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/userjobs/migrations/0002_alter_userjob_options_alter_userjob_auto_queued_and_more.py +0 -0
  191. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/userjobs/migrations/0003_clean_scheduled_tasks.py +0 -0
  192. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/userjobs/migrations/0004_userjob_summary_url.py +0 -0
  193. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/userjobs/migrations/0005_swappable_user_job.py +0 -0
  194. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/userjobs/migrations/0006_lowercase_verbose_names.py +0 -0
  195. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/userjobs/migrations/__init__.py +0 -0
  196. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/userjobs/models/__init__.py +0 -0
  197. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/userjobs/models/user_job.py +0 -0
  198. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/userjobs/models/user_job_attachment.py +0 -0
  199. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/userjobs/providers/__init__.py +0 -0
  200. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/userjobs/providers/user_job_django_q_executor.py +0 -0
  201. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/userjobs/providers/user_job_websocket_channel_provider.py +0 -0
  202. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/userjobs/tasks.py +0 -0
  203. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/userjobs/urls.py +0 -0
  204. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/userjobs/views/__init__.py +0 -0
  205. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/userjobs/views/user_job_attachment_view.py +0 -0
  206. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/userjobs/views/user_job_view.py +0 -0
  207. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pfx/userjobs/views/user_websocket_consumer.py +0 -0
  208. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/pyproject.toml +0 -0
  209. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/requirements.txt +0 -0
  210. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/serve-doc +0 -0
  211. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/setup.cfg +0 -0
  212. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/setup.py +0 -0
  213. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/__init__.py +0 -0
  214. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/apps.py +0 -0
  215. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/channel_group_send_calls.py +0 -0
  216. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/mfa_urls.py +0 -0
  217. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/migrations/__init__.py +0 -0
  218. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/settings/__init__.py +0 -0
  219. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/settings/ci.py +0 -0
  220. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/settings/common.py +0 -0
  221. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/settings/dev.py +0 -0
  222. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/settings/dev_custom_example.py +0 -0
  223. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/settings/dev_default.py +0 -0
  224. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/basic_api_errors.py +0 -0
  225. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/mfa_test_utils.py +0 -0
  226. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/parallel_scenarios.py +0 -0
  227. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_api_doc.py +0 -0
  228. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_api_doc_search.py +0 -0
  229. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_auth_api.py +0 -0
  230. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_body_mixin.py +0 -0
  231. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_cache.py +0 -0
  232. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_client.py +0 -0
  233. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_emails_email_api.py +0 -0
  234. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_emails_email_model.py +0 -0
  235. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_emails_q_mailjet_email_backend.py +0 -0
  236. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_emails_send_email_message.py +0 -0
  237. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_emails_test_tools.py +0 -0
  238. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_fields_choices.py +0 -0
  239. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_fields_date.py +0 -0
  240. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_fields_decimal.py +0 -0
  241. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_fields_help_text.py +0 -0
  242. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_fields_minutes_duration.py +0 -0
  243. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_fields_one2many.py +0 -0
  244. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_fields_rich_text.py +0 -0
  245. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_fields_time.py +0 -0
  246. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_filters.py +0 -0
  247. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_locale_api.py +0 -0
  248. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_mfa_legacy.py +0 -0
  249. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_mfa_login_api.py +0 -0
  250. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_mfa_setup_api.py +0 -0
  251. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_ordered_rest_view_mixin.py +0 -0
  252. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_perm_tests.py +0 -0
  253. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_permissions.py +0 -0
  254. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_perms_api.py +0 -0
  255. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_post_migrate_groups_update.py +0 -0
  256. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_profiling_middleware.py +0 -0
  257. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_settings.py +0 -0
  258. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_shortcuts.py +0 -0
  259. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_test_runner.py +0 -0
  260. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_timezone_middleware.py +0 -0
  261. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_tools.py +0 -0
  262. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_user_job_api.py +0 -0
  263. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_user_job_messages.py +0 -0
  264. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_user_job_model.py +0 -0
  265. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_user_queryset.py +0 -0
  266. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_user_websocket.py +0 -0
  267. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_verbose_names.py +0 -0
  268. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_version_check.py +0 -0
  269. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_view_decorators.py +0 -0
  270. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_view_fields.py +0 -0
  271. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/tests/test_ws_cookie_middleware.py +0 -0
  272. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/urls.py +0 -0
  273. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests/views.py +0 -0
  274. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_base_user/__init__.py +0 -0
  275. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_base_user/migrations/__init__.py +0 -0
  276. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_base_user/settings/__init__.py +0 -0
  277. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_base_user/settings/ci.py +0 -0
  278. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_base_user/settings/common.py +0 -0
  279. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_base_user/settings/dev.py +0 -0
  280. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_base_user/settings/dev_custom_example.py +0 -0
  281. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_base_user/settings/dev_default.py +0 -0
  282. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_base_user/tests/__init__.py +0 -0
  283. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_base_user/tests/test_api.py +0 -0
  284. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_base_user/tests/test_auth_api.py +0 -0
  285. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_base_user/urls.py +0 -0
  286. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_base_user/views.py +0 -0
  287. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user/__init__.py +0 -0
  288. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user/migrations/0001_initial.py +0 -0
  289. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user/migrations/__init__.py +0 -0
  290. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user/models.py +0 -0
  291. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user/settings/__init__.py +0 -0
  292. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user/settings/ci.py +0 -0
  293. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user/settings/common.py +0 -0
  294. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user/settings/dev.py +0 -0
  295. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user/settings/dev_custom_example.py +0 -0
  296. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user/settings/dev_default.py +0 -0
  297. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user/tests/__init__.py +0 -0
  298. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user/tests/test_api.py +0 -0
  299. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user/tests/test_auth_api.py +0 -0
  300. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user/urls.py +0 -0
  301. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user/views.py +0 -0
  302. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user_job/__init__.py +0 -0
  303. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user_job/migrations/0001_initial.py +0 -0
  304. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user_job/migrations/0002_lowercase_verbose_names.py +0 -0
  305. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user_job/migrations/__init__.py +0 -0
  306. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user_job/models.py +0 -0
  307. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user_job/providers.py +0 -0
  308. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user_job/scope.py +0 -0
  309. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user_job/settings/__init__.py +0 -0
  310. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user_job/settings/ci.py +0 -0
  311. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user_job/settings/common.py +0 -0
  312. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user_job/settings/dev.py +0 -0
  313. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user_job/settings/dev_custom_example.py +0 -0
  314. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user_job/settings/dev_default.py +0 -0
  315. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user_job/tests/__init__.py +0 -0
  316. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user_job/tests/test_user_job.py +0 -0
  317. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_custom_user_job/urls.py +0 -0
  318. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_user_email_login/__init__.py +0 -0
  319. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_user_email_login/migrations/0001_initial.py +0 -0
  320. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_user_email_login/migrations/__init__.py +0 -0
  321. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_user_email_login/models.py +0 -0
  322. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_user_email_login/settings/__init__.py +0 -0
  323. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_user_email_login/settings/ci.py +0 -0
  324. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_user_email_login/settings/common.py +0 -0
  325. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_user_email_login/settings/dev.py +0 -0
  326. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_user_email_login/settings/dev_custom_example.py +0 -0
  327. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_user_email_login/settings/dev_default.py +0 -0
  328. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_user_email_login/tests/__init__.py +0 -0
  329. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_user_email_login/tests/test_api.py +0 -0
  330. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_user_email_login/tests/test_auth_api.py +0 -0
  331. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_user_email_login/urls.py +0 -0
  332. {django_pfx-1.7.6.dev41 → django_pfx-1.7.6.dev45}/tests_user_email_login/views.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-pfx
3
- Version: 1.7.6.dev41
3
+ Version: 1.7.6.dev45
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.7.6.dev41
3
+ Version: 1.7.6.dev45
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
@@ -236,6 +236,8 @@ tests/tests/test_fields_choices.py
236
236
  tests/tests/test_fields_date.py
237
237
  tests/tests/test_fields_decimal.py
238
238
  tests/tests/test_fields_help_text.py
239
+ tests/tests/test_fields_media_local.py
240
+ tests/tests/test_fields_media_s3.py
239
241
  tests/tests/test_fields_minutes_duration.py
240
242
  tests/tests/test_fields_one2many.py
241
243
  tests/tests/test_fields_rich_text.py
@@ -226,6 +226,8 @@
226
226
  "tests/tests/test_fields_date.py",
227
227
  "tests/tests/test_fields_decimal.py",
228
228
  "tests/tests/test_fields_help_text.py",
229
+ "tests/tests/test_fields_media_local.py",
230
+ "tests/tests/test_fields_media_s3.py",
229
231
  "tests/tests/test_fields_minutes_duration.py",
230
232
  "tests/tests/test_fields_one2many.py",
231
233
  "tests/tests/test_fields_rich_text.py",
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "1.7.5",
3
+ "distance": 45,
4
+ "node": "g8845c1a04b6c6ac983aab0c8fe02537313956a5f",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-10-28"
8
+ }
@@ -32,6 +32,9 @@ API Reference
32
32
  :undoc-members:
33
33
  :show-inheritance:
34
34
 
35
+ .. autoclass:: pfx.pfxcore.models.UniqueConstraint
36
+ :show-inheritance:
37
+
35
38
  .. autoclass:: pfx.pfxcore.models.AbstractPFXBaseUser
36
39
  :members:
37
40
  :show-inheritance:
@@ -102,6 +102,52 @@ Which give the following result on the book service :
102
102
  }
103
103
  ```
104
104
 
105
+ ## UniqueConstraint
106
+
107
+ `pfx.pfxcore.models.UniqueConstraint` extends the Django constraint
108
+ to give a better validation error to the [PFX views](./pfx_views.md),
109
+ which return it in their `422` response. It takes two optional
110
+ parameters:
111
+
112
+ * `message`: the error message, formatted with the values of the
113
+ constraint fields. It requires a model inheriting `PFXModelMixin`
114
+ and applies to a constraint on `fields` only.
115
+ * `violation_error_field`: the field to report the error under.
116
+ Without it, Django reports the error globally (`__all__`) as soon as
117
+ the constraint is made of several fields or of expressions.
118
+
119
+ ```python
120
+ from django.db import models
121
+ from django.db.models.functions import Lower
122
+ from pfx.pfxcore.models import PFXModelMixin, UniqueConstraint
123
+
124
+ class Book(PFXModelMixin, models.Model):
125
+ name = models.CharField("Name", max_length=100)
126
+ author = models.ForeignKey(
127
+ 'tests.Author', on_delete=models.RESTRICT,
128
+ related_name='books', verbose_name="Author")
129
+
130
+ class Meta:
131
+ constraints = [
132
+ UniqueConstraint(
133
+ 'author', Lower('name'),
134
+ name='book_unique_author_and_name',
135
+ violation_error_message=(
136
+ "This name is already used for this author."),
137
+ violation_error_field='name')]
138
+ ```
139
+
140
+ A duplicate name then returns:
141
+ ```python
142
+ {
143
+ "name": ["This name is already used for this author."]
144
+ }
145
+ ```
146
+
147
+ `violation_error_field` changes the validation only: from Django 5.2,
148
+ changing it generates an `AlterConstraint` migration, without any
149
+ change to the database.
150
+
105
151
  ## Not null char fields
106
152
 
107
153
  To avoid storing a mixture of empty strings and `null` values, while automatically
@@ -6,8 +6,8 @@ from importlib import import_module
6
6
  from urllib.parse import unquote_to_bytes
7
7
 
8
8
  from django.core.exceptions import ValidationError
9
- from django.db import models
10
- from django.db.models.signals import post_delete, post_init, post_save
9
+ from django.db import models, transaction
10
+ from django.db.models.signals import post_init, post_save, pre_delete
11
11
  from django.dispatch import receiver
12
12
 
13
13
  from pfx.pfxcore.shortcuts import settings
@@ -104,6 +104,12 @@ class MediaField(models.JSONField):
104
104
  setattr(obj, self.name, self.upload(obj, file, name))
105
105
  obj.save(update_fields={self.name})
106
106
 
107
+ def delete_file(value):
108
+ transaction.on_commit(
109
+ lambda value=value: self.delete(value),
110
+ robust=True,
111
+ )
112
+
107
113
  after_save = []
108
114
  value = self.value_from_object(obj)
109
115
  if isinstance(value, dict):
@@ -112,10 +118,10 @@ class MediaField(models.JSONField):
112
118
  else:
113
119
  b64 = None
114
120
 
115
- db_value = obj._media_db_values.get(self.name)
121
+ db_value = getattr(obj, f'_db_{self.name}')
116
122
  if db_value and (b64 or not value):
117
- after_save.append(lambda: self.delete(db_value))
118
123
  setattr(obj, self.name, None)
124
+ after_save.append(lambda: delete_file(db_value))
119
125
  if b64:
120
126
  try:
121
127
  file = decode_data_uri(b64)
@@ -160,21 +166,28 @@ class MediaField(models.JSONField):
160
166
  return self.storage.delete(value)
161
167
 
162
168
 
163
- # Kept on the instance: the field is shared by every instance of the model.
164
169
  @receiver(post_init)
165
170
  @receiver(post_save)
166
- def store_media_db_values(sender, instance, update_fields=None, **kwargs):
171
+ def init_db_value(sender, instance, update_fields=None, **kwargs):
172
+ """Set the current db value in _db_{field_name} to track changes.
173
+
174
+ _db_{field_name} is the standard naming convention in PFX to track
175
+ current db value (normally done in `__init__()`).
176
+ """
167
177
  for field in sender._meta.fields:
168
178
  if isinstance(field, MediaField) and (
169
179
  update_fields is None or field.name in update_fields):
170
- if not hasattr(instance, '_media_db_values'):
171
- instance._media_db_values = {}
172
- instance._media_db_values[field.name] = (
180
+ setattr(
181
+ instance, f'_db_{field.name}',
173
182
  field.value_from_object(instance))
174
183
 
175
184
 
176
- @receiver(post_delete)
177
- def post_delete_media(sender, instance, **kwargs):
185
+ @receiver(pre_delete)
186
+ def pre_delete_media(sender, instance, **kwargs):
178
187
  for field in sender._meta.fields:
179
188
  if isinstance(field, MediaField) and field.auto_delete:
180
- field.storage.delete(field.value_from_object(instance))
189
+ value = field.value_from_object(instance)
190
+ transaction.on_commit(
191
+ lambda field=field, value=value: field.storage.delete(value),
192
+ robust=True,
193
+ )
@@ -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: 2026-10-05 17:02+0200\n"
10
+ "POT-Creation-Date: 2026-10-28 14:02+0100\n"
11
11
  "PO-Revision-Date: 2021-06-22 23:31+0200\n"
12
12
  "Last-Translator: \n"
13
13
  "Language-Team: \n"
@@ -175,7 +175,7 @@ msgstr "MFA forcé"
175
175
  msgid "sequence"
176
176
  msgstr "séquence"
177
177
 
178
- #: models/pfx_models.py:14
178
+ #: models/pfx_models.py:26
179
179
  #, python-format
180
180
  msgid "%(model_name)s with this %(field_labels)s already exists."
181
181
  msgstr "%(model_name)s avec ce/cette %(field_labels)s existe déjà."
@@ -1,7 +1,7 @@
1
1
 
2
2
  import logging
3
3
 
4
- from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
4
+ from django.core.exceptions import ValidationError
5
5
  from django.db import models
6
6
  from django.utils.translation import gettext_lazy as _
7
7
 
@@ -9,14 +9,42 @@ logger = logging.getLogger(__name__)
9
9
 
10
10
 
11
11
  class UniqueConstraint(models.UniqueConstraint):
12
- def __init__(self, *args, message=None, **kwargs):
12
+ """A unique constraint with a richer validation error.
13
+
14
+ :param message: The error message, formatted with the values of the
15
+ constraint fields (``%(name)s already exists``)
16
+ :param violation_error_field: The field to report the error under,
17
+ instead of the global errors (``__all__``)
18
+ """
19
+ non_db_attrs = (
20
+ *getattr(models.UniqueConstraint, 'non_db_attrs', ()),
21
+ 'violation_error_field')
22
+
23
+ def __init__(
24
+ self, *args, message=None, violation_error_field=None, **kwargs):
13
25
  self.message = message or _(
14
26
  "%(model_name)s with this %(field_labels)s already exists.")
27
+ self.violation_error_field = violation_error_field
15
28
  super().__init__(*args, **kwargs)
16
29
 
30
+ def validate(self, *args, **kwargs):
31
+ try:
32
+ super().validate(*args, **kwargs)
33
+ except ValidationError as e:
34
+ if not self.violation_error_field:
35
+ raise
36
+ raise ValidationError({self.violation_error_field: e})
37
+
38
+ def __eq__(self, other):
39
+ return super().__eq__(other) and (
40
+ self.violation_error_field ==
41
+ getattr(other, 'violation_error_field', None))
42
+
17
43
  def deconstruct(self):
18
44
  path, args, kwargs = super().deconstruct()
19
45
  kwargs['message'] = self.message
46
+ if self.violation_error_field:
47
+ kwargs['violation_error_field'] = self.violation_error_field
20
48
  return path, args, kwargs
21
49
 
22
50
 
@@ -85,8 +113,9 @@ class ErrorMessageMixin():
85
113
  """A model mixin to improve mode validation errors."""
86
114
 
87
115
  def validate_constraints(self, exclude=None):
88
- """Override validate_unique to allow to customize error message
89
- for :class:`UniqueConstraint`.
116
+ """Override validate_constraints to apply the custom message
117
+ of a :class:`UniqueConstraint`, whatever the field its error is
118
+ reported under.
90
119
  """
91
120
  def objgetattr(obj, f):
92
121
  v = getattr(obj, f)
@@ -99,11 +128,11 @@ class ErrorMessageMixin():
99
128
  try:
100
129
  return super().validate_constraints(exclude=exclude)
101
130
  except ValidationError as e:
102
- errors = e.update_error_dict({})
103
- non_field_errors = []
104
- for error in errors.get(NON_FIELD_ERRORS, []):
105
- if isinstance(error, ValidationError):
106
- if error.code in ('unique', 'unique_together'):
131
+ errors = {}
132
+ for key, field_errors in e.update_error_dict({}).items():
133
+ for error in field_errors:
134
+ if (isinstance(error, ValidationError) and
135
+ error.code in ('unique', 'unique_together')):
107
136
  model = error.params['model_class']
108
137
  unique_check = error.params['unique_check']
109
138
  unique = next(filter(
@@ -114,13 +143,11 @@ class ErrorMessageMixin():
114
143
  params = dict(error.params)
115
144
  params.update({
116
145
  f: objgetattr(self, f) for f in unique_check})
117
- non_field_errors.append(ValidationError(
146
+ error = ValidationError(
118
147
  message=unique.message,
119
148
  code=error.code,
120
- params=params))
121
- continue
122
- non_field_errors.append(error)
123
- errors[NON_FIELD_ERRORS] = non_field_errors
149
+ params=params)
150
+ errors.setdefault(key, []).append(error)
124
151
  raise ValidationError(errors)
125
152
 
126
153
 
@@ -1,3 +1,4 @@
1
+ import base64
1
2
  import copy
2
3
  import csv
3
4
  import io
@@ -218,7 +219,6 @@ class APIClient(DjangoClient):
218
219
 
219
220
 
220
221
  class TestAssertMixin:
221
-
222
222
  # assert response status code
223
223
  def assertRC(self, response, code, msg=None):
224
224
  msg = '\n'.join([msg or '', response.formatted()])
@@ -291,6 +291,21 @@ class TestAssertMixin:
291
291
  return self.assertNotIn(
292
292
  element, self.get_val(src, key, msg=msg), msg=msg)
293
293
 
294
+ def as_data_url(self, content="Test content"):
295
+ if isinstance(content, str):
296
+ content = content.encode()
297
+ b64 = base64.b64encode(content).decode('ascii')
298
+ return f"data:text/plain;charset=utf-8;base64,{b64}"
299
+
300
+ @classmethod
301
+ def upload_file(
302
+ cls, obj, field='file', file_name='test.txt',
303
+ content="A test file content.", **kw):
304
+ if isinstance(content, str):
305
+ content = content.encode()
306
+ return obj.__class__._meta.get_field(field).upload(
307
+ obj, content, file_name)
308
+
294
309
 
295
310
  class TestPermsAssertMixin(TestAssertMixin):
296
311
  USER_TESTS = {}
@@ -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: 2026-07-30 09:12+0200\n"
11
+ "POT-Creation-Date: 2026-10-28 14:02+0100\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:57
21
+ #: models.py:58
22
22
  msgid "First Name"
23
23
  msgstr "Prénom"
24
24
 
25
- #: models.py:58
25
+ #: models.py:59
26
26
  msgid "Last Name"
27
27
  msgstr "Nom"
28
28
 
@@ -3,6 +3,7 @@
3
3
 
4
4
  import django.contrib.auth.validators
5
5
  import django.db.models.deletion
6
+ import django.db.models.functions.text
6
7
  import django.utils.timezone
7
8
  from django.db import migrations, models
8
9
 
@@ -128,4 +129,12 @@ class Migration(migrations.Migration):
128
129
  model_name='book',
129
130
  constraint=pfx.pfxcore.models.pfx_models.UniqueConstraint(fields=('author', 'name'), message='%(name)s already exists for %(author)s', name='book_unique_author_and_name'),
130
131
  ),
132
+ migrations.AddConstraint(
133
+ model_name='book',
134
+ constraint=pfx.pfxcore.models.pfx_models.UniqueConstraint(models.F('author'), django.db.models.functions.text.Lower('name'), message='%(model_name)s with this %(field_labels)s already exists.', name='book_unique_author_and_lower_name', violation_error_field='name', violation_error_message='This name is already used for this author.'),
135
+ ),
136
+ migrations.AddConstraint(
137
+ model_name='book',
138
+ constraint=pfx.pfxcore.models.pfx_models.UniqueConstraint(fields=('author', 'pages'), message='A book of %(pages)s pages exists for %(author)s', name='book_unique_author_and_pages', violation_error_field='pages'),
139
+ ),
131
140
  ]
@@ -1,5 +1,6 @@
1
1
  from django.db import models
2
2
  from django.db.models import Q
3
+ from django.db.models.functions import Lower
3
4
  from django.utils.functional import cached_property
4
5
  from django.utils.translation import gettext_lazy as _
5
6
 
@@ -151,7 +152,18 @@ class Book(CacheDependsMixin, PFXModelMixin, models.Model):
151
152
  UniqueConstraint(
152
153
  fields=['author', 'name'],
153
154
  name='book_unique_author_and_name',
154
- message="%(name)s already exists for %(author)s")]
155
+ message="%(name)s already exists for %(author)s"),
156
+ UniqueConstraint(
157
+ 'author', Lower('name'),
158
+ name='book_unique_author_and_lower_name',
159
+ violation_error_message=(
160
+ "This name is already used for this author."),
161
+ violation_error_field='name'),
162
+ UniqueConstraint(
163
+ fields=['author', 'pages'],
164
+ name='book_unique_author_and_pages',
165
+ message="A book of %(pages)s pages exists for %(author)s",
166
+ violation_error_field='pages')]
155
167
 
156
168
  def __str__(self):
157
169
  return f"{self.name}"
@@ -17,6 +17,8 @@ from .test_emails_test_tools import (
17
17
  from .test_fields_choices import TestFieldsChoices
18
18
  from .test_fields_date import TestFieldsDate
19
19
  from .test_fields_decimal import TestFieldsDecimal
20
+ from .test_fields_media_local import TestFieldsMediaLocal
21
+ from .test_fields_media_s3 import TestFieldsMediaS3
20
22
  from .test_fields_minutes_duration import TestFieldsMinutesDuration
21
23
  from .test_fields_one2many import TestFieldsOne2Many
22
24
  from .test_fields_rich_text import TestFieldsRichText
@@ -828,11 +828,12 @@ class BasicAPITest(TestAssertMixin, TestCase):
828
828
  self.assertTrue(Path(f'/tmp/django-pfx-filestore/{key1}').exists())
829
829
 
830
830
  # Replace file
831
- response = self.client.put(f'/api/books/{book_pk}', dict(
832
- local_file=dict(
833
- name='test2.txt',
834
- base64='data:text/plain;charset=utf-8;'
835
- 'base64,TmV3IGNvbnRlbnQ=')))
831
+ with self.captureOnCommitCallbacks(execute=True):
832
+ response = self.client.put(f'/api/books/{book_pk}', dict(
833
+ local_file=dict(
834
+ name='test2.txt',
835
+ base64='data:text/plain;charset=utf-8;'
836
+ 'base64,TmV3IGNvbnRlbnQ=')))
836
837
  self.assertRC(response, 200)
837
838
  self.assertNJE(response, 'local_file.key', key1)
838
839
  self.assertJE(response, 'local_file.name', 'test2.txt')
@@ -841,8 +842,9 @@ class BasicAPITest(TestAssertMixin, TestCase):
841
842
  self.assertTrue(Path(f'/tmp/django-pfx-filestore/{key2}').exists())
842
843
 
843
844
  # Delete file
844
- response = self.client.put(f'/api/books/{book_pk}', dict(
845
- local_file=None))
845
+ with self.captureOnCommitCallbacks(execute=True):
846
+ response = self.client.put(f'/api/books/{book_pk}', dict(
847
+ local_file=None))
846
848
  self.assertRC(response, 200)
847
849
  self.assertJE(response, 'local_file', None)
848
850
  self.assertFalse(Path(f'/tmp/django-pfx-filestore/{key2}').exists())
@@ -865,10 +867,11 @@ class BasicAPITest(TestAssertMixin, TestCase):
865
867
  book.local_file = dict(
866
868
  name='new.txt',
867
869
  base64='data:text/plain;charset=utf-8;base64,TmV3IGNvbnRlbnQ=')
868
- funcs = Book._meta.get_field('local_file').media_pre_save(book)
869
- book.save()
870
- for func in funcs:
871
- func()
870
+ with self.captureOnCommitCallbacks(execute=True):
871
+ funcs = Book._meta.get_field('local_file').media_pre_save(book)
872
+ book.save()
873
+ for func in funcs:
874
+ func()
872
875
  return book.local_file['key']
873
876
 
874
877
  def test_replace_file_after_loading_another_object(self):
@@ -1052,6 +1055,34 @@ class BasicAPITest(TestAssertMixin, TestCase):
1052
1055
  response, '__all__.@0',
1053
1056
  'The Hobbit already exists for John Ronald Reuel Tolkien')
1054
1057
 
1058
+ def test_create_unique_error_field(self):
1059
+ """A constraint on an expression reports its error globally in
1060
+ Django: `violation_error_field` puts it under the field."""
1061
+ response = self.client.post(
1062
+ '/api/books', dict(
1063
+ name="the hobbit",
1064
+ author=self.author1.pk,
1065
+ pub_date="1937-09-21"))
1066
+ self.assertRC(response, 422)
1067
+ self.assertJE(
1068
+ response, 'name.@0', "This name is already used for this author.")
1069
+ self.assertJENotExists(response, '__all__')
1070
+
1071
+ def test_create_unique_custom_message_error_field(self):
1072
+ """The custom message still applies to an error reported under a
1073
+ field."""
1074
+ response = self.client.post(
1075
+ '/api/books', dict(
1076
+ name="The End of Eternity",
1077
+ author=self.author3.pk,
1078
+ pub_date="1955-01-01",
1079
+ pages=224))
1080
+ self.assertRC(response, 422)
1081
+ self.assertJE(
1082
+ response, 'pages.@0',
1083
+ 'A book of 224 pages exists for Isaac Asimov')
1084
+ self.assertJENotExists(response, '__all__')
1085
+
1055
1086
  def test_create_invalid_date(self):
1056
1087
  response = self.client.post(
1057
1088
  '/api/books', dict(
@@ -1426,14 +1457,15 @@ class BasicAPITest(TestAssertMixin, TestCase):
1426
1457
  response, f"http://dragonfly.fake/{key}",
1427
1458
  fetch_redirect_response=False)
1428
1459
 
1429
- with patch.object(
1460
+ with (patch.object(
1430
1461
  MockBoto3Client, 'delete_object',
1431
- return_value=None) as mock_delete:
1462
+ return_value=None) as mock_delete,
1463
+ self.captureOnCommitCallbacks(execute=True)):
1432
1464
  response = self.client.delete(
1433
1465
  f'/api/books/{self.author1_book1.pk}')
1434
- mock_delete.assert_called_with(
1435
- Bucket='dragonfly.fake',
1436
- Key=key)
1466
+ mock_delete.assert_called_with(
1467
+ Bucket='dragonfly.fake',
1468
+ Key=key)
1437
1469
 
1438
1470
  def test_annotate_meta_service(self):
1439
1471
  response = self.client.get('/api/authors-annotate/meta')