diff --git a/admin_confirm/admin.py b/admin_confirm/admin.py index 9b4a0f5..7166e91 100644 --- a/admin_confirm/admin.py +++ b/admin_confirm/admin.py @@ -13,7 +13,10 @@ class AdminConfirmMixin(object): # Should we ask for confirmation for changes? confirm_change = None - # if confirm_change, which fields should we confirm for? + # Should we ask for confirmation for additions? + confirm_add = None + + # If asking for confirmation, which fields should we confirm for? confirmation_fields = None # Custom templates (designed to be over-ridden in subclasses) @@ -50,20 +53,20 @@ class AdminConfirmMixin(object): context, ) - def change_view(self, request, object_id=None, form_url="", extra_context=None): - if request.method == "POST" and request.POST.get("_confirm_change"): - return self._change_confirmation_view( - request, object_id, form_url, extra_context - ) + def changeform_view(self, request, object_id=None, form_url="", extra_context=None): + if request.method == "POST": + if (not object_id and "_confirm_add" in request.POST) or (object_id and "_confirm_change"): + return self._change_confirmation_view(request, object_id, form_url, extra_context) extra_context = { **(extra_context or {}), + 'confirm_add': self.confirm_add, 'confirm_change': self.confirm_change } - return super().change_view(request, object_id, form_url, extra_context) + return super().changeform_view(request, object_id, form_url, extra_context) def _change_confirmation_view(self, request, object_id, form_url, extra_context): - # This code is taken from __changeform_view + # This code is taken from super()._changeform_view to_field = request.POST.get( TO_FIELD_VAR, request.GET.get(TO_FIELD_VAR) ) @@ -76,14 +79,20 @@ class AdminConfirmMixin(object): opts = model._meta add = object_id is None + if add: + if not self.has_add_permission(request): + raise PermissionDenied - obj = self.get_object(request, unquote(object_id), to_field) + obj = None + else: + self.message_user(request, add) + obj = self.get_object(request, unquote(object_id), to_field) - if obj is None: - return self._get_obj_does_not_exist_redirect(request, opts, object_id) + if obj is None: + return self._get_obj_does_not_exist_redirect(request, opts, object_id) - if not self.has_view_or_change_permission(request, obj): - raise PermissionDenied + if not self.has_view_or_change_permission(request, obj): + raise PermissionDenied fieldsets = self.get_fieldsets(request, obj) ModelForm = self.get_form( @@ -97,17 +106,23 @@ class AdminConfirmMixin(object): else: new_object = form.instance - # Parse the changed data - Note that using form.changed_data would not work as initial is not set changed_data = {} - for name, field in form.fields.items(): - initial_value = obj.__getattribute__(name) - new_value = new_object.__getattribute__(name) - if field.has_changed(initial_value, new_value) and initial_value != new_value: - changed_data[name] = [initial_value, new_value] + if add: + for name in form.changed_data: + changed_data[name] = [None, new_object.__getattribute__(name)] + else: + # Parse the changed data - Note that using form.changed_data would not work as initial is not set + for name, field in form.fields.items(): + initial_value = obj.__getattribute__(name) + new_value = new_object.__getattribute__(name) + if field.has_changed(initial_value, new_value) and initial_value != new_value: + changed_data[name] = [initial_value, new_value] - if not bool(set(self.get_confirmation_fields(request, obj)) & set(changed_data.keys())): + changed_confirmation_fields = set(self.get_confirmation_fields(request, obj)) & set(changed_data.keys()) + self.message_user(request, changed_confirmation_fields) + if not bool(changed_confirmation_fields): # No confirmation required for changed fields, continue to save - return super().change_view(request, object_id, form_url, extra_context) + return super()._changeform_view(request, object_id, form_url, extra_context) # Parse the original save action from request save_action = None @@ -140,6 +155,7 @@ class AdminConfirmMixin(object): "opts": opts, "form_data": form_data, "changed_data": changed_data, + "add": add, "submit_name": save_action, **(extra_context or {}), } diff --git a/admin_confirm/templates/admin/change_confirmation.html b/admin_confirm/templates/admin/change_confirmation.html index 9aa21ba..9e6a183 100644 --- a/admin_confirm/templates/admin/change_confirmation.html +++ b/admin_confirm/templates/admin/change_confirmation.html @@ -19,17 +19,35 @@ {% endblock %} {% block content %} +{% if add %} +

{% blocktrans with escaped_object=object %}Are you sure you want to add the {{ model_name }}?{% endblocktrans %}

+ {% if changed_data %} +
+

Confirm Values:

+ + {% for field, values in changed_data.items %} + + {% endfor %} +
{{ field }}:{{ values.1 }}
+
+
{% csrf_token %} + {% endif %} +{% else %}

{% blocktrans with escaped_object=object %}Are you sure you want to change the {{ model_name }} "{{ object_name }}"?{% endblocktrans %}

{% if changed_data %}
-

Detected Changes:

+

Confirm Values:

{% for field, values in changed_data.items %} @@ -39,6 +57,7 @@ {% endif %} {% csrf_token %} +{% endif %}
{% for key, value in form_data.items %} diff --git a/admin_confirm/templates/admin/submit_line.html b/admin_confirm/templates/admin/submit_line.html index 76114de..e25abb1 100644 --- a/admin_confirm/templates/admin/submit_line.html +++ b/admin_confirm/templates/admin/submit_line.html @@ -3,7 +3,10 @@ {% block submit-row %} {% if confirm_change %} - + + {% endif %} + {% if confirm_add %} + {% endif %} {{ block.super }} {% endblock %} diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..228da49 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,4 @@ +[pytest] +DJANGO_SETTINGS_MODULE=tests.test_project.settings +addopts = --doctest-modules -ra -l --tb=short --show-capture=log --color=yes +testpaths = admin_confirm \ No newline at end of file diff --git a/tests/manage.py b/tests/manage.py index fb52456..2753b50 100755 --- a/tests/manage.py +++ b/tests/manage.py @@ -5,7 +5,7 @@ import sys def main(): - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testproject.settings') + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'test_project.settings') try: from django.core.management import execute_from_command_line except ImportError as exc: diff --git a/tests/testproject/__init__.py b/tests/test_project/__init__.py similarity index 100% rename from tests/testproject/__init__.py rename to tests/test_project/__init__.py diff --git a/tests/testproject/settings.py b/tests/test_project/settings.py similarity index 94% rename from tests/testproject/settings.py rename to tests/test_project/settings.py index c154f0b..385918e 100644 --- a/tests/testproject/settings.py +++ b/tests/test_project/settings.py @@ -1,5 +1,5 @@ """ -Django settings for testproject project. +Django settings for test_project project. Generated by 'django-admin startproject' using Django 3.0.10. @@ -15,7 +15,6 @@ import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/ @@ -40,7 +39,7 @@ INSTALLED_APPS = [ 'django.contrib.messages', 'django.contrib.staticfiles', - 'market', + 'tests.market', ] MIDDLEWARE = [ @@ -53,7 +52,7 @@ MIDDLEWARE = [ 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] -ROOT_URLCONF = 'testproject.urls' +ROOT_URLCONF = 'tests.test_project.urls' TEMPLATES = [ { @@ -71,7 +70,7 @@ TEMPLATES = [ }, ] -WSGI_APPLICATION = 'testproject.wsgi.application' +WSGI_APPLICATION = 'tests.test_project.wsgi.application' # Database diff --git a/tests/testproject/urls.py b/tests/test_project/urls.py similarity index 100% rename from tests/testproject/urls.py rename to tests/test_project/urls.py diff --git a/tests/testproject/wsgi.py b/tests/test_project/wsgi.py similarity index 57% rename from tests/testproject/wsgi.py rename to tests/test_project/wsgi.py index 8ad347a..1e6481c 100644 --- a/tests/testproject/wsgi.py +++ b/tests/test_project/wsgi.py @@ -2,6 +2,6 @@ import os from django.core.wsgi import get_wsgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testproject.settings') +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'test_project.settings') application = get_wsgi_application()
FieldCurrent ValueNew Value