djresttoolkit 1.0.0__tar.gz → 1.2.0__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.
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/PKG-INFO +30 -7
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/README.md +29 -6
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/apps/todos/models.py +20 -1
- djresttoolkit-1.2.0/demo/apps/todos/serializers.py +9 -0
- djresttoolkit-1.2.0/demo/apps/todos/urls.py +12 -0
- djresttoolkit-1.2.0/demo/apps/todos/views.py +43 -0
- djresttoolkit-1.2.0/demo/apps_config/__init__.py +3 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/apps_config/settings.py +6 -22
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/pyproject.toml +13 -9
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/pagination/_paginated_data_builder.py +6 -4
- djresttoolkit-1.2.0/src/djresttoolkit/serializers/__init__.py +3 -0
- djresttoolkit-1.2.0/src/djresttoolkit/serializers/_enhanced_model_serializer.py +59 -0
- djresttoolkit-1.2.0/src/djresttoolkit/views/mixins/__init__.py +3 -0
- djresttoolkit-1.2.0/src/djresttoolkit/views/mixins/_retrieve_object_mixin.py +59 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/uv.lock +1 -1
- djresttoolkit-1.0.0/demo/apps/todos/urls.py +0 -7
- djresttoolkit-1.0.0/demo/apps/todos/views.py +0 -0
- djresttoolkit-1.0.0/demo/env_config/__init__.py +0 -3
- djresttoolkit-1.0.0/demo/env_config/env_settings.py +0 -54
- djresttoolkit-1.0.0/src/djresttoolkit/serializers/__init__.py +0 -0
- djresttoolkit-1.0.0/src/djresttoolkit/views/_apiviews/__init__.py +0 -0
- djresttoolkit-1.0.0/src/djresttoolkit/views/mixins/__init__.py +0 -6
- djresttoolkit-1.0.0/src/djresttoolkit/views/mixins/_retrieve_object_mixin.py +0 -41
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/.gitignore +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/.python-version +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/.vscode/settings.json +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/LICENSE +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/apps/todos/__init__.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/apps/todos/admin.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/apps/todos/apps.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/apps/todos/dbseed/__init__.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/apps/todos/dbseed/tag_seed.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/apps/todos/dbseed/todo_seed.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/apps/todos/dbseed/todo_tag_seed.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/apps/todos/migrations/0001_initial.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/apps/todos/migrations/__init__.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/apps/todos/tests.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/apps_config/asgi.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/apps_config/serializers.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/apps_config/urls.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/apps_config/views.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/apps_config/wsgi.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/manage.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/css/autocomplete.css +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/css/base.css +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/css/changelists.css +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/css/dark_mode.css +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/css/dashboard.css +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/css/forms.css +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/css/login.css +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/css/nav_sidebar.css +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/css/responsive.css +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/css/responsive_rtl.css +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/css/rtl.css +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/css/unusable_password_field.css +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/css/vendor/select2/LICENSE-SELECT2.md +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/css/vendor/select2/select2.css +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/css/vendor/select2/select2.min.css +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/css/widgets.css +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/img/LICENSE +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/img/README.txt +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/img/calendar-icons.svg +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/img/gis/move_vertex_off.svg +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/img/gis/move_vertex_on.svg +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/img/icon-addlink.svg +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/img/icon-alert.svg +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/img/icon-calendar.svg +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/img/icon-changelink.svg +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/img/icon-clock.svg +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/img/icon-deletelink.svg +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/img/icon-hidelink.svg +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/img/icon-no.svg +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/img/icon-unknown-alt.svg +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/img/icon-unknown.svg +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/img/icon-viewlink.svg +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/img/icon-yes.svg +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/img/inline-delete.svg +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/img/search.svg +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/img/selector-icons.svg +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/img/sorting-icons.svg +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/img/tooltag-add.svg +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/img/tooltag-arrowright.svg +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/SelectBox.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/SelectFilter2.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/actions.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/admin/DateTimeShortcuts.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/admin/RelatedObjectLookups.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/autocomplete.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/calendar.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/cancel.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/change_form.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/core.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/filters.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/inlines.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/jquery.init.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/nav_sidebar.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/popup_response.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/prepopulate.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/prepopulate_init.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/theme.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/unusable_password_field.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/urlify.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/jquery/LICENSE.txt +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/jquery/jquery.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/jquery/jquery.min.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/LICENSE.md +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/af.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/ar.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/az.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/bg.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/bn.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/bs.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/ca.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/cs.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/da.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/de.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/dsb.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/el.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/en.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/es.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/et.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/eu.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/fa.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/fi.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/fr.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/gl.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/he.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/hi.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/hr.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/hsb.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/hu.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/hy.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/id.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/is.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/it.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/ja.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/ka.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/km.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/ko.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/lt.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/lv.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/mk.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/ms.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/nb.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/ne.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/nl.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/pl.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/ps.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/pt-BR.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/pt.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/ro.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/ru.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/sk.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/sl.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/sq.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/sr-Cyrl.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/sr.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/sv.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/th.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/tk.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/tr.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/uk.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/vi.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/zh-CN.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/i18n/zh-TW.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/select2.full.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/select2/select2.full.min.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/xregexp/LICENSE.txt +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/xregexp/xregexp.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/admin/js/vendor/xregexp/xregexp.min.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/css/bootstrap-theme.min.css +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/css/bootstrap-theme.min.css.map +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/css/bootstrap-tweaks.css +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/css/bootstrap.min.css +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/css/bootstrap.min.css.map +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/css/default.css +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/css/font-awesome-4.0.3.css +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/css/prettify.css +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/docs/css/base.css +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/docs/css/highlight.css +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/docs/css/jquery.json-view.min.css +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/docs/img/favicon.ico +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/docs/img/grid.png +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/docs/js/api.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/docs/js/highlight.pack.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/docs/js/jquery.json-view.min.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/fonts/fontawesome-webfont.eot +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/fonts/fontawesome-webfont.svg +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/fonts/fontawesome-webfont.ttf +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/fonts/fontawesome-webfont.woff +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/fonts/glyphicons-halflings-regular.eot +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/fonts/glyphicons-halflings-regular.svg +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/fonts/glyphicons-halflings-regular.ttf +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/fonts/glyphicons-halflings-regular.woff +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/fonts/glyphicons-halflings-regular.woff2 +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/img/glyphicons-halflings-white.png +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/img/glyphicons-halflings.png +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/img/grid.png +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/js/ajax-form.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/js/bootstrap.min.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/js/coreapi-0.1.1.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/js/csrf.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/js/default.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/js/jquery-3.7.1.min.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/js/load-ajax-form.js +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/demo/staticfiles/rest_framework/js/prettify-min.js +0 -0
- {djresttoolkit-1.0.0/demo/apps_config → djresttoolkit-1.2.0/src/djresttoolkit}/__init__.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/admin.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/apps.py +0 -0
- {djresttoolkit-1.0.0/src/djresttoolkit → djresttoolkit-1.2.0/src/djresttoolkit/cache}/__init__.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/cache/mixins/__init__.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/cache/mixins/_cache_action_mixin.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/cache/mixins/_cache_invalidate_mixin.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/cache/mixins/_cache_key_mixin.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/cache/mixins/_cache_list_retrieve_mixin.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/cache/mixins/_cache_ops_mixin.py +0 -0
- {djresttoolkit-1.0.0/src/djresttoolkit/cache → djresttoolkit-1.2.0/src/djresttoolkit/dbseed}/__init__.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/dbseed/models/__init__.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/dbseed/models/_choice_field.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/dbseed/models/_gen.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/dbseed/models/_seed_model.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/envconfig/__init__.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/envconfig/_base_env_config.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/mail/__init__.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/mail/_email_sender.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/mail/_models.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/mail/_types.py +0 -0
- {djresttoolkit-1.0.0/src/djresttoolkit/dbseed → djresttoolkit-1.2.0/src/djresttoolkit/management}/__init__.py +0 -0
- {djresttoolkit-1.0.0/src/djresttoolkit/management → djresttoolkit-1.2.0/src/djresttoolkit/management/commands}/__init__.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/management/commands/dbflush.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/management/commands/dbseed.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/middlewares/__init__.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/middlewares/_response_time_middleware.py +0 -0
- {djresttoolkit-1.0.0/src/djresttoolkit/management/commands → djresttoolkit-1.2.0/src/djresttoolkit/migrations}/__init__.py +0 -0
- {djresttoolkit-1.0.0/src/djresttoolkit/migrations → djresttoolkit-1.2.0/src/djresttoolkit/models}/__init__.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/models/mixins/__init__.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/models/mixins/_model_choice_fields_mixin.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/pagination/__init__.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/pagination/_page_number_pagination.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/py.typed +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/renderers/__init__.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/renderers/_throttle_info_json_renderer.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/serializers/mixins/__init__.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/serializers/mixins/_absolute_url_file_mixin.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/serializers/mixins/_bulk_create_mixin.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/throttling/__init__.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/throttling/_throttle_inspector.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/urls/__init__.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/urls/_build_absolute_uri.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/views/__init__.py +0 -0
- {djresttoolkit-1.0.0/src/djresttoolkit/models → djresttoolkit-1.2.0/src/djresttoolkit/views/_apiviews}/__init__.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/views/_apiviews/_choice_fields_apiview.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/views/_exceptions/__init__.py +0 -0
- {djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/views/_exceptions/_exception_handler.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: djresttoolkit
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.2.0
|
4
4
|
Summary: A collection of Django and DRF utilities to simplify API development.
|
5
5
|
Project-URL: Homepage, https://github.com/shaileshpandit141/djresttoolkit
|
6
6
|
Project-URL: Documentation, https://shaileshpandit141.github.io/djresttoolkit
|
@@ -240,7 +240,7 @@ or
|
|
240
240
|
Flushed 120 records from all models and reset IDs.
|
241
241
|
```
|
242
242
|
|
243
|
-
###
|
243
|
+
### 3. BaseEnvConfig — API Reference
|
244
244
|
|
245
245
|
```python
|
246
246
|
from djresttoolkit.envconfig import BaseEnvConfig
|
@@ -801,6 +801,7 @@ Retrieve a single model object using the provided filter criteria.
|
|
801
801
|
|
802
802
|
```python
|
803
803
|
from rest_framework.views import APIView
|
804
|
+
from rest_framework.response import Respone
|
804
805
|
from django.http import JsonResponse
|
805
806
|
from myapp.models import Book
|
806
807
|
from djresttoolkit.mixins import RetrieveObjectMixin
|
@@ -808,17 +809,18 @@ from djresttoolkit.mixins import RetrieveObjectMixin
|
|
808
809
|
class BookDetailView(RetrieveObjectMixin[Book], APIView):
|
809
810
|
queryset = Book.objects.all()
|
810
811
|
|
811
|
-
def
|
812
|
+
def not_found_detail(self) -> dict[str, str] | str:
|
813
|
+
return "The requested Book was not found."
|
814
|
+
|
815
|
+
def get(self, request, *args, **kwargs) -> Respone:
|
812
816
|
book = self.get_object(id=kwargs["id"])
|
813
|
-
|
814
|
-
return JsonResponse({"title": book.title, "author": book.author})
|
815
|
-
return JsonResponse({"detail": "Not found"}, status=404)
|
817
|
+
return Respone({"title": book.title, "author": book.author})
|
816
818
|
```
|
817
819
|
|
818
820
|
#### Features of Retrieve Object Mixin
|
819
821
|
|
820
822
|
- Simplifies object retrieval in class-based views or DRF views.
|
821
|
-
-
|
823
|
+
- Raise `http404` if requested resource does not extst.
|
822
824
|
- Works with any Django model and queryset.
|
823
825
|
|
824
826
|
### 13. build_absolute_uri — API Reference
|
@@ -1079,6 +1081,27 @@ class BookViewSet(CacheInvalidateMixin, ModelViewSet):
|
|
1079
1081
|
- Invalidates caches when books are created, updated, or deleted.
|
1080
1082
|
- Supports custom cache keys per action.
|
1081
1083
|
|
1084
|
+
### 17. EnhancedModelSerializer — API Reference
|
1085
|
+
|
1086
|
+
A subclass of Django REST Framework’s `ModelSerializer` that automatically merges Django model field `error_messages` into the serializer field, unless explicitly overridden.
|
1087
|
+
This helps maintain consistent validation messages between the model and the serializer.
|
1088
|
+
|
1089
|
+
#### Type Parameters
|
1090
|
+
|
1091
|
+
- `T` (`Model`): The Django model type that the serializer corresponds to.
|
1092
|
+
|
1093
|
+
#### Example of EnhancedModelSerializer
|
1094
|
+
|
1095
|
+
```python
|
1096
|
+
from myapp.models import Book
|
1097
|
+
from myapp.serializers import EnhancedModelSerializer
|
1098
|
+
|
1099
|
+
class BookSerializer(EnhancedModelSerializer[Book]):
|
1100
|
+
class Meta:
|
1101
|
+
model = Book
|
1102
|
+
fields = "__all__"
|
1103
|
+
```
|
1104
|
+
|
1082
1105
|
## 🛠️ Planned Features
|
1083
1106
|
|
1084
1107
|
- Add more utils
|
@@ -184,7 +184,7 @@ or
|
|
184
184
|
Flushed 120 records from all models and reset IDs.
|
185
185
|
```
|
186
186
|
|
187
|
-
###
|
187
|
+
### 3. BaseEnvConfig — API Reference
|
188
188
|
|
189
189
|
```python
|
190
190
|
from djresttoolkit.envconfig import BaseEnvConfig
|
@@ -745,6 +745,7 @@ Retrieve a single model object using the provided filter criteria.
|
|
745
745
|
|
746
746
|
```python
|
747
747
|
from rest_framework.views import APIView
|
748
|
+
from rest_framework.response import Respone
|
748
749
|
from django.http import JsonResponse
|
749
750
|
from myapp.models import Book
|
750
751
|
from djresttoolkit.mixins import RetrieveObjectMixin
|
@@ -752,17 +753,18 @@ from djresttoolkit.mixins import RetrieveObjectMixin
|
|
752
753
|
class BookDetailView(RetrieveObjectMixin[Book], APIView):
|
753
754
|
queryset = Book.objects.all()
|
754
755
|
|
755
|
-
def
|
756
|
+
def not_found_detail(self) -> dict[str, str] | str:
|
757
|
+
return "The requested Book was not found."
|
758
|
+
|
759
|
+
def get(self, request, *args, **kwargs) -> Respone:
|
756
760
|
book = self.get_object(id=kwargs["id"])
|
757
|
-
|
758
|
-
return JsonResponse({"title": book.title, "author": book.author})
|
759
|
-
return JsonResponse({"detail": "Not found"}, status=404)
|
761
|
+
return Respone({"title": book.title, "author": book.author})
|
760
762
|
```
|
761
763
|
|
762
764
|
#### Features of Retrieve Object Mixin
|
763
765
|
|
764
766
|
- Simplifies object retrieval in class-based views or DRF views.
|
765
|
-
-
|
767
|
+
- Raise `http404` if requested resource does not extst.
|
766
768
|
- Works with any Django model and queryset.
|
767
769
|
|
768
770
|
### 13. build_absolute_uri — API Reference
|
@@ -1023,6 +1025,27 @@ class BookViewSet(CacheInvalidateMixin, ModelViewSet):
|
|
1023
1025
|
- Invalidates caches when books are created, updated, or deleted.
|
1024
1026
|
- Supports custom cache keys per action.
|
1025
1027
|
|
1028
|
+
### 17. EnhancedModelSerializer — API Reference
|
1029
|
+
|
1030
|
+
A subclass of Django REST Framework’s `ModelSerializer` that automatically merges Django model field `error_messages` into the serializer field, unless explicitly overridden.
|
1031
|
+
This helps maintain consistent validation messages between the model and the serializer.
|
1032
|
+
|
1033
|
+
#### Type Parameters
|
1034
|
+
|
1035
|
+
- `T` (`Model`): The Django model type that the serializer corresponds to.
|
1036
|
+
|
1037
|
+
#### Example of EnhancedModelSerializer
|
1038
|
+
|
1039
|
+
```python
|
1040
|
+
from myapp.models import Book
|
1041
|
+
from myapp.serializers import EnhancedModelSerializer
|
1042
|
+
|
1043
|
+
class BookSerializer(EnhancedModelSerializer[Book]):
|
1044
|
+
class Meta:
|
1045
|
+
model = Book
|
1046
|
+
fields = "__all__"
|
1047
|
+
```
|
1048
|
+
|
1026
1049
|
## 🛠️ Planned Features
|
1027
1050
|
|
1028
1051
|
- Add more utils
|
@@ -57,8 +57,18 @@ class Todo(BaseModel):
|
|
57
57
|
on_delete=CASCADE,
|
58
58
|
related_name="todos",
|
59
59
|
help_text="Owner of this todo",
|
60
|
+
error_messages={
|
61
|
+
"null": "Every todo must belong to a user.",
|
62
|
+
"invalid": "Invalid user reference.",
|
63
|
+
},
|
64
|
+
)
|
65
|
+
title: CharField[str, str] = CharField(
|
66
|
+
max_length=255,
|
67
|
+
error_messages={
|
68
|
+
"blank": "Please give your todo a title.",
|
69
|
+
"max_length": "The title cannot exceed 255 characters.",
|
70
|
+
},
|
60
71
|
)
|
61
|
-
title: CharField[str, str] = CharField(max_length=255)
|
62
72
|
description: TextField[str, str] = TextField(blank=True, default="")
|
63
73
|
due_date: DateTimeField[str | None, str | None] = DateTimeField(
|
64
74
|
null=True,
|
@@ -69,16 +79,25 @@ class Todo(BaseModel):
|
|
69
79
|
max_length=20,
|
70
80
|
choices=TodoPriority.choices,
|
71
81
|
default=TodoPriority.MEDIUM,
|
82
|
+
error_messages={
|
83
|
+
"invalid_choice": "Priority must be one of: low, medium, high, critical.",
|
84
|
+
},
|
72
85
|
)
|
73
86
|
status: CharField[str, str] = CharField(
|
74
87
|
max_length=20,
|
75
88
|
choices=TodoStatus.choices,
|
76
89
|
default=TodoStatus.PENDING,
|
90
|
+
error_messages={
|
91
|
+
"invalid_choice": "Status must be one of: pending, in_progress, completed, archived.",
|
92
|
+
},
|
77
93
|
)
|
78
94
|
|
79
95
|
completed_at: DateTimeField[str | None, str | None] = DateTimeField(
|
80
96
|
null=True,
|
81
97
|
blank=True,
|
98
|
+
error_messages={
|
99
|
+
"invalid": "Completed at must be a valid datetime.",
|
100
|
+
},
|
82
101
|
)
|
83
102
|
|
84
103
|
def mark_completed(self) -> None:
|
@@ -0,0 +1,12 @@
|
|
1
|
+
"""
|
2
|
+
URL configuration for todos app.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from django.urls import path
|
6
|
+
|
7
|
+
from .views import TodoDetailView, TodoListView
|
8
|
+
|
9
|
+
urlpatterns = [
|
10
|
+
path("", TodoListView.as_view(), name="todo-list"),
|
11
|
+
path("<int:id>/", TodoDetailView.as_view(), name="todo-detail"),
|
12
|
+
]
|
@@ -0,0 +1,43 @@
|
|
1
|
+
from rest_framework.permissions import AllowAny
|
2
|
+
from rest_framework.request import Request
|
3
|
+
from rest_framework.response import Response
|
4
|
+
from rest_framework.views import APIView
|
5
|
+
|
6
|
+
from djresttoolkit.pagination import PaginatedDataBuilder
|
7
|
+
from djresttoolkit.views.mixins import RetrieveObjectMixin
|
8
|
+
|
9
|
+
from .models import Todo
|
10
|
+
from .serializers import TodoSerializer
|
11
|
+
|
12
|
+
|
13
|
+
class TodoListView(APIView):
|
14
|
+
permission_classes = [AllowAny]
|
15
|
+
|
16
|
+
def get(self, request: Request) -> Response:
|
17
|
+
pagination = PaginatedDataBuilder[Todo](
|
18
|
+
request=request,
|
19
|
+
serializer_class=TodoSerializer,
|
20
|
+
queryset=Todo.objects.all(),
|
21
|
+
)
|
22
|
+
return Response(data=pagination.get_paginated_data())
|
23
|
+
|
24
|
+
def post(self, request: Request) -> Response:
|
25
|
+
serializer = TodoSerializer(
|
26
|
+
data=request.data,
|
27
|
+
many=isinstance(request.data, list),
|
28
|
+
)
|
29
|
+
|
30
|
+
if serializer.is_valid():
|
31
|
+
serializer.save(user=request.user)
|
32
|
+
|
33
|
+
return Response(data=serializer.data)
|
34
|
+
|
35
|
+
|
36
|
+
class TodoDetailView(RetrieveObjectMixin[Todo], APIView):
|
37
|
+
permission_classes = [AllowAny]
|
38
|
+
queryset = Todo.objects.all()
|
39
|
+
|
40
|
+
def get(self, request: Request, id: int) -> Response:
|
41
|
+
todo = self.get_object(id=id)
|
42
|
+
serializer = TodoSerializer(instance=todo, many=False)
|
43
|
+
return Response(data=serializer.data)
|
@@ -1,8 +1,7 @@
|
|
1
1
|
import sys
|
2
|
+
from os import getenv
|
2
3
|
from pathlib import Path
|
3
4
|
|
4
|
-
from env_config import env_settings
|
5
|
-
|
6
5
|
# Configuration Settings File for the django backend
|
7
6
|
# --------------------------------------------------
|
8
7
|
BASE_DIR = Path(__file__).resolve().parent.parent.parent
|
@@ -14,7 +13,7 @@ sys.path.insert(0, str(SRC_DIR))
|
|
14
13
|
|
15
14
|
# Security Configuration Settings
|
16
15
|
# -------------------------------
|
17
|
-
SECRET_KEY =
|
16
|
+
SECRET_KEY = getenv("SECRET_KEY")
|
18
17
|
|
19
18
|
# DEBUG Configuration Settings
|
20
19
|
# ----------------------------
|
@@ -22,11 +21,11 @@ DEBUG = True
|
|
22
21
|
|
23
22
|
# Allowed Host Configuration Settings
|
24
23
|
# -----------------------------------
|
25
|
-
ALLOWED_HOSTS =
|
24
|
+
ALLOWED_HOSTS = ["*"]
|
26
25
|
|
27
26
|
# Configure CORS Settings
|
28
27
|
# -----------------------
|
29
|
-
CORS_ALLOWED_ORIGINS =
|
28
|
+
# CORS_ALLOWED_ORIGINS = []
|
30
29
|
|
31
30
|
# Login Redirect URL Configuration Setting
|
32
31
|
# ----------------------------------------
|
@@ -166,6 +165,7 @@ MEDIA_URL = "/media/"
|
|
166
165
|
# -------------------------------------
|
167
166
|
REST_FRAMEWORK = {
|
168
167
|
"NON_FIELD_ERRORS_KEY": "non_field",
|
168
|
+
"DATETIME_FORMAT": "%Y-%m-%d %H:%M",
|
169
169
|
"DEFAULT_AUTHENTICATION_CLASSES": [
|
170
170
|
"rest_framework.authentication.TokenAuthentication",
|
171
171
|
],
|
@@ -200,29 +200,13 @@ REST_FRAMEWORK = {
|
|
200
200
|
# -------------------------------------
|
201
201
|
AUTHENTICATION_BACKENDS = ["django.contrib.auth.backends.ModelBackend"]
|
202
202
|
|
203
|
-
# EMAIL Configuration Settings
|
204
|
-
# ----------------------------
|
205
|
-
EMAIL_BACKEND = env_settings.email.backend # type: ignore[]
|
206
|
-
EMAIL_HOST = env_settings.email.host
|
207
|
-
EMAIL_PORT = env_settings.email.port
|
208
|
-
EMAIL_USE_TLS = env_settings.email.use_tls
|
209
|
-
EMAIL_USE_SSL = env_settings.email.use_ssl
|
210
|
-
EMAIL_HOST_USER = env_settings.email.host_user
|
211
|
-
EMAIL_HOST_PASSWORD = env_settings.email.host_password
|
212
|
-
DEFAULT_FROM_EMAIL = env_settings.email.default_from_email
|
213
|
-
|
214
|
-
# Google OAuth2 Configuration Settings
|
215
|
-
# ------------------------------------
|
216
|
-
GOOGLE_CLIENT_ID = env_settings.google.client_id
|
217
|
-
GOOGLE_CLIENT_SECRET = env_settings.google.client_secret
|
218
|
-
GOOGLE_REDIRECT_URI = env_settings.google.redirect_url
|
219
203
|
|
220
204
|
# Redis configuration for production
|
221
205
|
# ----------------------------------
|
222
206
|
CACHES = {
|
223
207
|
"default": {
|
224
208
|
"BACKEND": "django_redis.cache.RedisCache",
|
225
|
-
"LOCATION":
|
209
|
+
"LOCATION": "redis://localhost:6379/1",
|
226
210
|
"OPTIONS": {
|
227
211
|
"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
228
212
|
},
|
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "djresttoolkit"
|
7
|
-
version = "1.
|
7
|
+
version = "1.2.0"
|
8
8
|
description = "A collection of Django and DRF utilities to simplify API development."
|
9
9
|
readme = { file = "README.md", content-type = "text/markdown" }
|
10
10
|
license = { file = "LICENSE" }
|
@@ -45,10 +45,7 @@ keywords = [
|
|
45
45
|
"python-package",
|
46
46
|
]
|
47
47
|
requires-python = ">=3.13"
|
48
|
-
dependencies = [
|
49
|
-
"faker>=37.5.3",
|
50
|
-
"pydantic>=2.11.7",
|
51
|
-
]
|
48
|
+
dependencies = ["faker>=37.5.3", "pydantic>=2.11.7"]
|
52
49
|
|
53
50
|
# CLI scripts entry point configuration
|
54
51
|
# -------------------------------------
|
@@ -109,12 +106,19 @@ Source = "https://github.com/shaileshpandit141/djresttoolkit"
|
|
109
106
|
Issues = "https://github.com/shaileshpandit141/djresttoolkit/issues"
|
110
107
|
License = "https://github.com/shaileshpandit141/djresttoolkit/blob/main/LICENSE"
|
111
108
|
|
112
|
-
# ===================
|
113
|
-
# Tool Configurations
|
114
|
-
# ===================
|
115
109
|
|
110
|
+
# ==========================
|
111
|
+
# Add pyright Configurations
|
112
|
+
# ==========================
|
113
|
+
[tool.pyright]
|
114
|
+
typeCheckingMode = "strict"
|
115
|
+
reportMissingTypeStubs = false
|
116
|
+
reportUnknownMemberType = false
|
117
|
+
reportIncompatibleVariableOverride = false
|
118
|
+
|
119
|
+
# =========================================
|
116
120
|
# Disable ruff S101 rule for test casecases
|
117
|
-
#
|
121
|
+
# =========================================
|
118
122
|
[tool.ruff.lint.per-file-ignores]
|
119
123
|
"tests/*.py" = ["S101"]
|
120
124
|
|
{djresttoolkit-1.0.0 → djresttoolkit-1.2.0}/src/djresttoolkit/pagination/_paginated_data_builder.py
RENAMED
@@ -1,11 +1,13 @@
|
|
1
1
|
import logging
|
2
2
|
from typing import Any
|
3
3
|
|
4
|
-
from django.db.models import QuerySet
|
4
|
+
from django.db.models import Model, QuerySet
|
5
5
|
from rest_framework.exceptions import NotFound
|
6
6
|
from rest_framework.request import Request
|
7
7
|
from rest_framework.serializers import BaseSerializer
|
8
|
-
|
8
|
+
|
9
|
+
from djresttoolkit.serializers import EnhancedModelSerializer
|
10
|
+
|
9
11
|
from ._page_number_pagination import PageNumberPagination
|
10
12
|
|
11
13
|
# Get logger from logging.
|
@@ -18,7 +20,7 @@ class PaginatedDataBuilder[T: Model]:
|
|
18
20
|
def __init__(
|
19
21
|
self,
|
20
22
|
request: Request,
|
21
|
-
serializer_class: type[BaseSerializer[T]],
|
23
|
+
serializer_class: type[BaseSerializer[T] | EnhancedModelSerializer[T]],
|
22
24
|
queryset: QuerySet[T],
|
23
25
|
) -> None:
|
24
26
|
"""Initilize the PaginatedDataBuilder class."""
|
@@ -49,7 +51,7 @@ class PaginatedDataBuilder[T: Model]:
|
|
49
51
|
)
|
50
52
|
|
51
53
|
# Construct the paginated response
|
52
|
-
paginated_data = {
|
54
|
+
paginated_data: dict[str, Any] = {
|
53
55
|
"page": {
|
54
56
|
"current": paginator.page.number, # type: ignore
|
55
57
|
"total": paginator.page.paginator.num_pages, # type: ignore
|
@@ -0,0 +1,59 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from copy import deepcopy
|
4
|
+
from typing import Any
|
5
|
+
|
6
|
+
from django.db.models import Field as DjangoField
|
7
|
+
from django.db.models import Model
|
8
|
+
from rest_framework.serializers import Field as DrfField
|
9
|
+
from rest_framework.serializers import ModelSerializer
|
10
|
+
from rest_framework.utils.model_meta import RelationInfo
|
11
|
+
|
12
|
+
|
13
|
+
class EnhancedModelSerializer[T: Model](ModelSerializer[Model]):
|
14
|
+
"""
|
15
|
+
A DRF ModelSerializer that automatically applies Django model field
|
16
|
+
`error_messages` unless explicitly overridden in the serializer.
|
17
|
+
"""
|
18
|
+
|
19
|
+
def _merge_error_messages(
|
20
|
+
self,
|
21
|
+
field_kwargs: dict[str, Any],
|
22
|
+
model_field: DjangoField[Any, Any] | None,
|
23
|
+
) -> dict[str, Any]:
|
24
|
+
"""Safely merge model field error_messages with serializer kwargs."""
|
25
|
+
model_errors: dict[str, str] | None = getattr(
|
26
|
+
model_field, "error_messages", None
|
27
|
+
)
|
28
|
+
if model_errors:
|
29
|
+
existing: dict[str, str] = field_kwargs.get("error_messages", {})
|
30
|
+
field_kwargs["error_messages"] = {**deepcopy(model_errors), **existing}
|
31
|
+
return field_kwargs
|
32
|
+
|
33
|
+
def build_standard_field(
|
34
|
+
self,
|
35
|
+
field_name: str,
|
36
|
+
model_field: DjangoField[Any, Any],
|
37
|
+
) -> tuple[type[DrfField[Any, Any, Any, Any]], dict[str, Any]]:
|
38
|
+
field_class, field_kwargs = super().build_standard_field( # type: ignore
|
39
|
+
field_name,
|
40
|
+
model_field,
|
41
|
+
)
|
42
|
+
return field_class, self._merge_error_messages(
|
43
|
+
field_kwargs,
|
44
|
+
model_field,
|
45
|
+
) # type: ignore
|
46
|
+
|
47
|
+
def build_relational_field(
|
48
|
+
self,
|
49
|
+
field_name: str,
|
50
|
+
relation_info: RelationInfo,
|
51
|
+
) -> tuple[type[DrfField[Any, Any, Any, Any]], dict[str, Any]]:
|
52
|
+
field_class, field_kwargs = super().build_relational_field( # type: ignore
|
53
|
+
field_name,
|
54
|
+
relation_info,
|
55
|
+
)
|
56
|
+
return field_class, self._merge_error_messages(
|
57
|
+
field_kwargs,
|
58
|
+
relation_info.model_field, # type: ignore
|
59
|
+
)
|
@@ -0,0 +1,59 @@
|
|
1
|
+
from typing import Any
|
2
|
+
|
3
|
+
from django.core.exceptions import ImproperlyConfigured
|
4
|
+
from django.db.models import Model, QuerySet
|
5
|
+
from django.http import Http404
|
6
|
+
|
7
|
+
|
8
|
+
class RetrieveObjectMixin[T: Model]:
|
9
|
+
"""
|
10
|
+
Retrieve a single model object by filters.
|
11
|
+
|
12
|
+
Requires the `queryset` attribute to be set in the class that inherits this mixin.
|
13
|
+
|
14
|
+
Raises `Http404` when the object is missing.
|
15
|
+
|
16
|
+
This works in both Django views and DRF views.
|
17
|
+
|
18
|
+
Example:
|
19
|
+
```
|
20
|
+
class MyView(RetrieveModelMixin[Book], APIView):
|
21
|
+
queryset = Book.objects.all()
|
22
|
+
|
23
|
+
def get(self, request, *args, **kwargs):
|
24
|
+
obj = self.get_object(id=1)
|
25
|
+
return JsonResponse(obj.to_dict())
|
26
|
+
```
|
27
|
+
"""
|
28
|
+
|
29
|
+
queryset: QuerySet[T] | None = None
|
30
|
+
|
31
|
+
def get_object(self, **filters: Any) -> T:
|
32
|
+
"""Retrieve a model object based on provided filters."""
|
33
|
+
|
34
|
+
if self.queryset is None:
|
35
|
+
raise ImproperlyConfigured(
|
36
|
+
"Queryset attribute is not set in the class.",
|
37
|
+
)
|
38
|
+
|
39
|
+
try:
|
40
|
+
return self.queryset.get(**filters)
|
41
|
+
except self.queryset.model.DoesNotExist:
|
42
|
+
raise Http404(self.not_found_detail())
|
43
|
+
|
44
|
+
def not_found_detail(self) -> dict[str, str] | str:
|
45
|
+
"""
|
46
|
+
Hook for customizing the 404 message.
|
47
|
+
Can be overridden per view.
|
48
|
+
"""
|
49
|
+
|
50
|
+
if self.queryset is None:
|
51
|
+
raise ImproperlyConfigured(
|
52
|
+
"Queryset attribute is not set in the class.",
|
53
|
+
)
|
54
|
+
|
55
|
+
verbose_name = self.queryset.model._meta.verbose_name
|
56
|
+
model_name = (
|
57
|
+
verbose_name.title() if verbose_name else self.queryset.model.__name__
|
58
|
+
)
|
59
|
+
return f"The requested {model_name} was not found."
|
File without changes
|
@@ -1,54 +0,0 @@
|
|
1
|
-
from typing import Literal
|
2
|
-
|
3
|
-
from pydantic import BaseModel
|
4
|
-
|
5
|
-
from src.djresttoolkit.envconfig import EnvBaseSettings
|
6
|
-
|
7
|
-
|
8
|
-
class DatabaseConfig(BaseModel):
|
9
|
-
engine: str
|
10
|
-
name: str
|
11
|
-
user: str
|
12
|
-
password: str
|
13
|
-
host: str
|
14
|
-
port: int
|
15
|
-
|
16
|
-
|
17
|
-
class RedisConfig(BaseModel):
|
18
|
-
cache_location: str
|
19
|
-
|
20
|
-
|
21
|
-
class EmailConfig(BaseModel):
|
22
|
-
backend: str
|
23
|
-
host: str
|
24
|
-
port: int
|
25
|
-
use_tls: bool
|
26
|
-
use_ssl: bool
|
27
|
-
host_user: str
|
28
|
-
host_password: str
|
29
|
-
default_from_email: str
|
30
|
-
|
31
|
-
|
32
|
-
class GoogleOAuth2Config(BaseModel):
|
33
|
-
client_id: str
|
34
|
-
client_secret: str
|
35
|
-
redirect_url: str
|
36
|
-
|
37
|
-
|
38
|
-
class EnvSettings(EnvBaseSettings["EnvSettings"]):
|
39
|
-
"""Env Settings class to handle env loads."""
|
40
|
-
|
41
|
-
secret_key: str
|
42
|
-
host: str
|
43
|
-
port: int
|
44
|
-
environ: Literal["dev", "prod"]
|
45
|
-
allowed_hosts: list[str]
|
46
|
-
cors_allowed_origins: list[str]
|
47
|
-
database: DatabaseConfig
|
48
|
-
redis: RedisConfig
|
49
|
-
email: EmailConfig
|
50
|
-
google: GoogleOAuth2Config
|
51
|
-
|
52
|
-
|
53
|
-
env_settings = EnvSettings.load(warning=False)
|
54
|
-
|
File without changes
|
File without changes
|
@@ -1,41 +0,0 @@
|
|
1
|
-
from typing import Any
|
2
|
-
from django.db.models import Model, QuerySet
|
3
|
-
|
4
|
-
|
5
|
-
class QuerysetNotDefinedError(Exception):
|
6
|
-
"""Exception raised when the `queryset` attribute is not set in the class."""
|
7
|
-
|
8
|
-
pass
|
9
|
-
|
10
|
-
|
11
|
-
class RetrieveObjectMixin[T: Model]:
|
12
|
-
"""
|
13
|
-
Mixin to provide a method for retrieving a single model object by filters.
|
14
|
-
|
15
|
-
Requires the `queryset` attribute to be set in the class that inherits this mixin.
|
16
|
-
|
17
|
-
Example:
|
18
|
-
```
|
19
|
-
class MyView(RetrieveModelMixin[Book], APIView):
|
20
|
-
queryset = Book.objects.all()
|
21
|
-
|
22
|
-
def get(self, request, *args, **kwargs):
|
23
|
-
obj = self.get_object(id=1)
|
24
|
-
return JsonResponse(obj.to_dict())
|
25
|
-
```
|
26
|
-
"""
|
27
|
-
|
28
|
-
queryset: QuerySet[T] | None = None
|
29
|
-
|
30
|
-
def get_object(self, **filters: Any) -> T | None:
|
31
|
-
"""Retrieve a model object based on provided filters."""
|
32
|
-
|
33
|
-
if self.queryset is None:
|
34
|
-
raise QuerysetNotDefinedError(
|
35
|
-
"Queryset attribute is not set in the class.",
|
36
|
-
)
|
37
|
-
|
38
|
-
try:
|
39
|
-
return self.queryset.get(**filters)
|
40
|
-
except self.queryset.model.DoesNotExist:
|
41
|
-
return None
|
File without changes
|