Refactored and added pytest.ini file

main
Thu Trang Pham 2020-11-01 07:30:51 -08:00
parent 3c47e66ca1
commit a63d4292b1
9 changed files with 74 additions and 33 deletions

View File

@ -13,7 +13,10 @@ class AdminConfirmMixin(object):
# Should we ask for confirmation for changes? # Should we ask for confirmation for changes?
confirm_change = None 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 confirmation_fields = None
# Custom templates (designed to be over-ridden in subclasses) # Custom templates (designed to be over-ridden in subclasses)
@ -50,20 +53,20 @@ class AdminConfirmMixin(object):
context, context,
) )
def change_view(self, request, object_id=None, form_url="", extra_context=None): def changeform_view(self, request, object_id=None, form_url="", extra_context=None):
if request.method == "POST" and request.POST.get("_confirm_change"): if request.method == "POST":
return self._change_confirmation_view( if (not object_id and "_confirm_add" in request.POST) or (object_id and "_confirm_change"):
request, object_id, form_url, extra_context return self._change_confirmation_view(request, object_id, form_url, extra_context)
)
extra_context = { extra_context = {
**(extra_context or {}), **(extra_context or {}),
'confirm_add': self.confirm_add,
'confirm_change': self.confirm_change '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): 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 = request.POST.get(
TO_FIELD_VAR, request.GET.get(TO_FIELD_VAR) TO_FIELD_VAR, request.GET.get(TO_FIELD_VAR)
) )
@ -76,14 +79,20 @@ class AdminConfirmMixin(object):
opts = model._meta opts = model._meta
add = object_id is None 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: if obj is None:
return self._get_obj_does_not_exist_redirect(request, opts, object_id) return self._get_obj_does_not_exist_redirect(request, opts, object_id)
if not self.has_view_or_change_permission(request, obj): if not self.has_view_or_change_permission(request, obj):
raise PermissionDenied raise PermissionDenied
fieldsets = self.get_fieldsets(request, obj) fieldsets = self.get_fieldsets(request, obj)
ModelForm = self.get_form( ModelForm = self.get_form(
@ -97,17 +106,23 @@ class AdminConfirmMixin(object):
else: else:
new_object = form.instance new_object = form.instance
# Parse the changed data - Note that using form.changed_data would not work as initial is not set
changed_data = {} changed_data = {}
for name, field in form.fields.items(): if add:
initial_value = obj.__getattribute__(name) for name in form.changed_data:
new_value = new_object.__getattribute__(name) changed_data[name] = [None, new_object.__getattribute__(name)]
if field.has_changed(initial_value, new_value) and initial_value != new_value: else:
changed_data[name] = [initial_value, new_value] # 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 # 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 # Parse the original save action from request
save_action = None save_action = None
@ -140,6 +155,7 @@ class AdminConfirmMixin(object):
"opts": opts, "opts": opts,
"form_data": form_data, "form_data": form_data,
"changed_data": changed_data, "changed_data": changed_data,
"add": add,
"submit_name": save_action, "submit_name": save_action,
**(extra_context or {}), **(extra_context or {}),
} }

View File

@ -19,17 +19,35 @@
<div class="breadcrumbs"> <div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a> <a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a> &rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a> {% if not add %}
&rsaquo; <a href="{% url opts|admin_urlname:'change' object_id|admin_urlquote %}">{{ object_name|truncatewords:"18" }}</a> &rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
&rsaquo; {% trans 'Change Confirmation' %} &rsaquo; <a href="{% url opts|admin_urlname:'change' object_id|admin_urlquote %}">{{ object_name|truncatewords:"18" }}</a>
&rsaquo; {% trans 'Confirm change' %}
{% else %}
&rsaquo; {% trans 'Confirm add' %}
{% endif %}
</div> </div>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% if add %}
<p>{% blocktrans with escaped_object=object %}Are you sure you want to add the {{ model_name }}?{% endblocktrans %}</p>
{% if changed_data %}
<div class="changed-data">
<p><b>Confirm Values:</b></p>
<table>
{% for field, values in changed_data.items %}
<tr><th style="text-align: right">{{ field }}:</th><td>{{ values.1 }}</td></tr>
{% endfor %}
</table>
</div>
<form method="post" action="{% url opts|admin_urlname:'add'%}">{% csrf_token %}
{% endif %}
{% else %}
<p>{% blocktrans with escaped_object=object %}Are you sure you want to change the {{ model_name }} "{{ object_name }}"?{% endblocktrans %}</p> <p>{% blocktrans with escaped_object=object %}Are you sure you want to change the {{ model_name }} "{{ object_name }}"?{% endblocktrans %}</p>
{% if changed_data %} {% if changed_data %}
<div class="changed-data"> <div class="changed-data">
<p><b>Detected Changes:</b></p> <p><b>Confirm Values:</b></p>
<table> <table>
<tr><th>Field</th><th>Current Value</th><th>New Value</th></tr> <tr><th>Field</th><th>Current Value</th><th>New Value</th></tr>
{% for field, values in changed_data.items %} {% for field, values in changed_data.items %}
@ -39,6 +57,7 @@
</div> </div>
{% endif %} {% endif %}
<form method="post" action="{% url opts|admin_urlname:'change' object_id|admin_urlquote %}">{% csrf_token %} <form method="post" action="{% url opts|admin_urlname:'change' object_id|admin_urlquote %}">{% csrf_token %}
{% endif %}
<div> <div>
{% for key, value in form_data.items %} {% for key, value in form_data.items %}
<input type="hidden" name="{{ key }}" value="{{ value }}"> <input type="hidden" name="{{ key }}" value="{{ value }}">

View File

@ -3,7 +3,10 @@
{% block submit-row %} {% block submit-row %}
{% if confirm_change %} {% if confirm_change %}
<input hidden name="_confirm_change" value="{{ confirm_change }}" /> <input hidden name="_confirm_change" />
{% endif %}
{% if confirm_add %}
<input hidden name="_confirm_add" />
{% endif %} {% endif %}
{{ block.super }} {{ block.super }}
{% endblock %} {% endblock %}

4
pytest.ini 100644
View File

@ -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

View File

@ -5,7 +5,7 @@ import sys
def main(): def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testproject.settings') os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'test_project.settings')
try: try:
from django.core.management import execute_from_command_line from django.core.management import execute_from_command_line
except ImportError as exc: except ImportError as exc:

View File

@ -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. 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, ...) # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(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
# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
@ -40,7 +39,7 @@ INSTALLED_APPS = [
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'market', 'tests.market',
] ]
MIDDLEWARE = [ MIDDLEWARE = [
@ -53,7 +52,7 @@ MIDDLEWARE = [
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
] ]
ROOT_URLCONF = 'testproject.urls' ROOT_URLCONF = 'tests.test_project.urls'
TEMPLATES = [ TEMPLATES = [
{ {
@ -71,7 +70,7 @@ TEMPLATES = [
}, },
] ]
WSGI_APPLICATION = 'testproject.wsgi.application' WSGI_APPLICATION = 'tests.test_project.wsgi.application'
# Database # Database

View File

@ -2,6 +2,6 @@ import os
from django.core.wsgi import get_wsgi_application 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() application = get_wsgi_application()