Messy but working save confirmation on change page
parent
220e9d8b41
commit
48c8e9bd10
|
|
@ -29,3 +29,6 @@ docs/_build/
|
||||||
|
|
||||||
# pycharm
|
# pycharm
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
|
# Database
|
||||||
|
db.sqlite3
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
include *.rst
|
||||||
|
recursive-include admin-confirm/*
|
||||||
|
|
@ -0,0 +1,128 @@
|
||||||
|
|
||||||
|
from django.contrib.admin.exceptions import DisallowedModelAdminToField
|
||||||
|
from django.contrib.admin.utils import flatten_fieldsets, quote, unquote
|
||||||
|
from django.core.exceptions import PermissionDenied
|
||||||
|
from django.template.response import SimpleTemplateResponse, TemplateResponse
|
||||||
|
from django.contrib.admin.options import TO_FIELD_VAR, IS_POPUP_VAR
|
||||||
|
from django.utils.translation import gettext as _, ngettext
|
||||||
|
|
||||||
|
|
||||||
|
class AdminConfirmMixin(object):
|
||||||
|
"""Generic AdminConfirm Mixin"""
|
||||||
|
|
||||||
|
change_needs_confirmation = False
|
||||||
|
|
||||||
|
# Custom templates (designed to be over-ridden in subclasses)
|
||||||
|
change_confirmation_template = None
|
||||||
|
|
||||||
|
def render_change_confirmation(self, request, context):
|
||||||
|
opts = self.model._meta
|
||||||
|
app_label = opts.app_label
|
||||||
|
|
||||||
|
request.current_app = self.admin_site.name
|
||||||
|
context.update(
|
||||||
|
media=self.media,
|
||||||
|
)
|
||||||
|
|
||||||
|
return TemplateResponse(
|
||||||
|
request,
|
||||||
|
self.change_confirmation_template
|
||||||
|
or [
|
||||||
|
"admin/{}/{}/change_confirmation.html".format(
|
||||||
|
app_label, opts.model_name
|
||||||
|
),
|
||||||
|
"admin/{}/change_confirmation.html".format(app_label),
|
||||||
|
"admin/change_confirmation.html",
|
||||||
|
],
|
||||||
|
context,
|
||||||
|
)
|
||||||
|
|
||||||
|
def change_view(self, request, object_id=None, form_url="", extra_context=None):
|
||||||
|
self.message_user(request, f"{request.POST}")
|
||||||
|
if request.method == "POST" and request.POST.get("_change_needs_confirmation"):
|
||||||
|
self.message_user(request, "Needs confirmation was inside the request")
|
||||||
|
return self._change_confirmation_view(
|
||||||
|
request, object_id, form_url, extra_context
|
||||||
|
)
|
||||||
|
|
||||||
|
extra_context = {
|
||||||
|
**(extra_context or {}),
|
||||||
|
'change_needs_confirmation': self.change_needs_confirmation
|
||||||
|
}
|
||||||
|
return super().change_view(request, object_id, form_url, extra_context)
|
||||||
|
|
||||||
|
def _change_confirmation_view(self, request, object_id, form_url, extra_context):
|
||||||
|
# Do we need any of this code?
|
||||||
|
to_field = request.POST.get(
|
||||||
|
TO_FIELD_VAR, request.GET.get(TO_FIELD_VAR)
|
||||||
|
)
|
||||||
|
if to_field and not self.to_field_allowed(request, to_field):
|
||||||
|
raise DisallowedModelAdminToField(
|
||||||
|
"The field %s cannot be referenced." % to_field
|
||||||
|
)
|
||||||
|
|
||||||
|
model = self.model
|
||||||
|
opts = model._meta
|
||||||
|
|
||||||
|
add = object_id is None
|
||||||
|
|
||||||
|
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 not self.has_view_or_change_permission(request, obj):
|
||||||
|
raise PermissionDenied
|
||||||
|
|
||||||
|
fieldsets = self.get_fieldsets(request, obj)
|
||||||
|
ModelForm = self.get_form(
|
||||||
|
request, obj, change=not add, fields=flatten_fieldsets(fieldsets)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Should we be validating the data here? Or just pass it to super?
|
||||||
|
|
||||||
|
form = ModelForm(request.POST, request.FILES, obj)
|
||||||
|
form_validated = form.is_valid()
|
||||||
|
if form_validated:
|
||||||
|
new_object = self.save_form(request, form, change=not add)
|
||||||
|
else:
|
||||||
|
new_object = form.instance
|
||||||
|
|
||||||
|
# End code copied from Django sourcecode
|
||||||
|
if add:
|
||||||
|
title = _("Add %s")
|
||||||
|
elif self.has_change_permission(request, obj):
|
||||||
|
title = _("Change %s")
|
||||||
|
|
||||||
|
# Parse the original save action from request
|
||||||
|
save_action = None
|
||||||
|
for action in ["_save", "_saveasnew", "_addanother", "_continue"]:
|
||||||
|
if action in request.POST:
|
||||||
|
save_action = action
|
||||||
|
break
|
||||||
|
|
||||||
|
form_data = {}
|
||||||
|
for key in request.POST:
|
||||||
|
if key.startswith("_") or key == 'csrfmiddlewaretoken':
|
||||||
|
continue
|
||||||
|
|
||||||
|
form_data[key] = request.POST.get(key)
|
||||||
|
# { k: v for k, v in request.POST.\\ if not(k.startswith('_') or k == 'csrfmiddlewaretoken')}
|
||||||
|
|
||||||
|
context = {
|
||||||
|
**self.admin_site.each_context(request),
|
||||||
|
"title": title % opts.verbose_name,
|
||||||
|
"subtitle": str(obj),
|
||||||
|
"object_name": str(obj),
|
||||||
|
"object_id": object_id,
|
||||||
|
"original": obj,
|
||||||
|
"new_object": new_object,
|
||||||
|
"app_label": opts.app_label,
|
||||||
|
"model_name": opts.model_name,
|
||||||
|
"opts": opts,
|
||||||
|
"preserved_filters": self.get_preserved_filters(request),
|
||||||
|
"form_data": form_data,
|
||||||
|
"submit_name": save_action,
|
||||||
|
**(extra_context or {}),
|
||||||
|
}
|
||||||
|
return self.render_change_confirmation(request, context)
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
# import json
|
||||||
|
#
|
||||||
|
# from django import template
|
||||||
|
# from django.template.context import Context
|
||||||
|
#
|
||||||
|
# from django.contrib.admin.templatetags import admin_modify
|
||||||
|
#
|
||||||
|
# from django.contrib.admin.templatetags.base import InclusionAdminNode
|
||||||
|
#
|
||||||
|
# register = template.Library()
|
||||||
|
#
|
||||||
|
# def submit_row(context):
|
||||||
|
# ctx = admin_modify.submit_row(context)
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# @register.tag(name='submit_row')
|
||||||
|
# def submit_row_tag(parser, token):
|
||||||
|
# return InclusionAdminNode(parser, token, func=submit_row, template_name='submit_line.html')
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
{% extends "admin/base_site.html" %}
|
||||||
|
{% load i18n admin_urls static %}
|
||||||
|
|
||||||
|
{% block extrahead %}
|
||||||
|
{{ block.super }}
|
||||||
|
{{ media }}
|
||||||
|
<script src="{% static 'admin/js/cancel.js' %}" async></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} change-confirmation{% endblock %}
|
||||||
|
|
||||||
|
{% block breadcrumbs %}
|
||||||
|
<div class="breadcrumbs">
|
||||||
|
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
|
||||||
|
› <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
|
||||||
|
› <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
|
||||||
|
› <a href="{% url opts|admin_urlname:'change' object_id|admin_urlquote %}">{{ object|truncatewords:"18" }}</a>
|
||||||
|
› {% trans 'Change Confirmation' %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<p>{% blocktrans with escaped_object=object %}Are you sure you want to change the {{ object_name }} "{{ escaped_object }}"?{% endblocktrans %}</p>
|
||||||
|
<form method="post" action="{% url opts|admin_urlname:'change' object_id|admin_urlquote %}">{% csrf_token %}
|
||||||
|
<div>
|
||||||
|
{% for key, value in form_data.items %}
|
||||||
|
<input type="hidden" name="{{ key }}" value="{{ value }}">
|
||||||
|
{% endfor %}
|
||||||
|
{# <input type="hidden" name="post" value="yes">#}
|
||||||
|
{% if is_popup %}<input type="hidden" name="{{ is_popup_var }}" value="1">{% endif %}
|
||||||
|
{% if to_field %}<input type="hidden" name="{{ to_field_var }}" value="{{ to_field }}">{% endif %}
|
||||||
|
<input type="submit" value="{% trans 'Yes, I’m sure' %}" name="{{ submit_name }}">
|
||||||
|
<a href="#" class="button cancel-link">{% trans "No, continue to edit" %}</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
{% extends 'admin/submit_line.html' %}
|
||||||
|
{% load i18n admin_urls %}
|
||||||
|
|
||||||
|
{% block submit-row %}
|
||||||
|
<input hidden name="_change_needs_confirmation" value="{{ change_needs_confirmation }}" />
|
||||||
|
{{ block.super }}
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
import os
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
here = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
README = open(os.path.join(here, 'README.rst')).read()
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name='django-admin-confirm',
|
||||||
|
version='0.1',
|
||||||
|
packages=['admin_confirm'],
|
||||||
|
description='Adds confirmation to Django Admin change',
|
||||||
|
long_description=README,
|
||||||
|
author='Thu Trang Pham',
|
||||||
|
author_email='thuutrangpham@gmail.com',
|
||||||
|
url='https://github.com/trangpham/django-admin-confirm/',
|
||||||
|
license='Apache 2.0',
|
||||||
|
install_requires=[
|
||||||
|
'Django>=1.7',
|
||||||
|
]
|
||||||
|
)
|
||||||
BIN
tests/db.sqlite3
BIN
tests/db.sqlite3
Binary file not shown.
|
|
@ -1,16 +0,0 @@
|
||||||
"""
|
|
||||||
ASGI config for testproject project.
|
|
||||||
|
|
||||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
|
||||||
|
|
||||||
For more information on this file, see
|
|
||||||
https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
from django.core.asgi import get_asgi_application
|
|
||||||
|
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testproject.settings')
|
|
||||||
|
|
||||||
application = get_asgi_application()
|
|
||||||
|
|
@ -13,7 +13,7 @@ https://docs.djangoproject.com/en/3.0/ref/settings/
|
||||||
import os
|
import os
|
||||||
|
|
||||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
|
||||||
# Quick-start development settings - unsuitable for production
|
# Quick-start development settings - unsuitable for production
|
||||||
|
|
@ -31,6 +31,8 @@ ALLOWED_HOSTS = ['127.0.0.1']
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
|
'admin_confirm',
|
||||||
|
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
|
|
@ -38,9 +40,7 @@ INSTALLED_APPS = [
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
|
|
||||||
'admin_confirm',
|
'market',
|
||||||
|
|
||||||
'tests.market',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
|
@ -53,7 +53,7 @@ MIDDLEWARE = [
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
]
|
]
|
||||||
|
|
||||||
ROOT_URLCONF = 'tests.testproject.urls'
|
ROOT_URLCONF = 'testproject.urls'
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
|
|
@ -71,7 +71,7 @@ TEMPLATES = [
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
WSGI_APPLICATION = 'tests.testproject.wsgi.application'
|
WSGI_APPLICATION = 'testproject.wsgi.application'
|
||||||
|
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue