feat(ISSUE-3): travis and coveralls (#10)

* feat(ISSUE-8): ISSUE-8: ManyToManyField causes error on confirmations

* feat(ISSUE-8): Update some readme and remove print statements

* feat(ISSUE-8): Generate new version of package

* feat(ISSUE-3): Adding .travis.yml

* feat(ISSUE-3): Adding coveralls

* feat(ISSUE-3): Trying github actions

* feat(ISSUE-3): remove travis

* feat(ISSUE-3): Change python versions to test

* feat(ISSUE-3): Some refactoring and trying tox

* feat(ISSUE-3): Try action matrix

* feat(ISSUE-3): Some more refactors

* feat(ISSUE-3): Fix tests

* feat(ISSUE-3): Refactor/fix tests

* feat(ISSUE-3): Remove tox

* feat(ISSUE-3): Adding pypi version badge to readme

* feat(ISSUE-3): Update readme again

Co-authored-by: Thu Trang Pham <thu@joinmodernhealth.com>
main
Thu Trang Pham 2021-02-19 21:28:17 -08:00 committed by GitHub
parent 375b3d0917
commit 9a9dfa75e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 93 additions and 65 deletions

35
.github/workflows/test.yml vendored 100644
View File

@ -0,0 +1,35 @@
name: Tests
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.6, 3.7, 3.8, 3.9]
django-version: [2.2, 3.0]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install Django ${{ matrix.django-version }}
run: |
pip install django==${{ matrix.django-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
run: |
make test

View File

@ -1,4 +1,7 @@
{ {
"python.formatting.provider": "black", "python.formatting.provider": "black",
"editor.formatOnSave": true "editor.formatOnSave": true,
"python.linting.flake8Enabled": true,
"python.analysis.extraPaths": [],
"python.languageServer": "Pylance" // use MS's fast new Python language server,
} }

View File

@ -20,15 +20,9 @@ package:
python3 setup.py sdist bdist_wheel python3 setup.py sdist bdist_wheel
upload-testpypi: upload-testpypi:
ifndef VERSION
$(error VERSION is not set)
endif
python3 -m twine upload --repository testpypi dist/django_admin_confirm-$(VERSION)* python3 -m twine upload --repository testpypi dist/django_admin_confirm-$(VERSION)*
i-have-tested-with-testpypi-and-am-ready-to-release: i-have-tested-with-testpypi-and-am-ready-to-release:
ifndef VERSION
$(error VERSION is not set)
endif
python3 -m twine upload --repository pypi dist/django_admin_confirm-$(VERSION)* python3 -m twine upload --repository pypi dist/django_admin_confirm-$(VERSION)*
install-testpypi: install-testpypi:

View File

@ -1,6 +1,6 @@
# Django Admin Confirm # Django Admin Confirm
![coverage](https://raw.githubusercontent.com/TrangPham/django-admin-confirm/main/coverage.svg) [![PyPI](https://img.shields.io/pypi/v/django-admin-confirm?color=blue)](https://pypi.org/project/django-admin-confirm/) ![Tests Status](https://github.com/TrangPham/django-admin-confirm/actions/workflows/.github/workflows/test.yml/badge.svg) [![Coverage Status](https://coveralls.io/repos/github/TrangPham/django-admin-confirm/badge.svg)](https://coveralls.io/github/TrangPham/django-admin-confirm)
AdminConfirmMixin is a mixin for ModelAdmin to add confirmations to change, add and actions. AdminConfirmMixin is a mixin for ModelAdmin to add confirmations to change, add and actions.

View File

@ -1 +1,2 @@
from .admin import AdminConfirmMixin __all__ = ["admin"]
from .admin import AdminConfirmMixin # noqa

View File

@ -1,3 +1,4 @@
from typing import Dict
from django.contrib.admin.exceptions import DisallowedModelAdminToField from django.contrib.admin.exceptions import DisallowedModelAdminToField
from django.contrib.admin.utils import flatten_fieldsets, unquote from django.contrib.admin.utils import flatten_fieldsets, unquote
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
@ -5,8 +6,12 @@ from django.template.response import TemplateResponse
from django.contrib.admin.options import TO_FIELD_VAR from django.contrib.admin.options import TO_FIELD_VAR
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django.contrib.admin import helpers from django.contrib.admin import helpers
from django.db.models import Model
from django.forms import ModelForm
from admin_confirm.utils import snake_to_title_case from admin_confirm.utils import snake_to_title_case
SAVE_ACTIONS = ["_save", "_saveasnew", "_addanother", "_continue"]
class AdminConfirmMixin: class AdminConfirmMixin:
# Should we ask for confirmation for changes? # Should we ask for confirmation for changes?
@ -92,6 +97,40 @@ class AdminConfirmMixin:
} }
return super().changeform_view(request, object_id, form_url, extra_context) return super().changeform_view(request, object_id, form_url, extra_context)
def _get_changed_data(
self, form: ModelForm, model: Model, obj: object, add: bool
) -> Dict:
"""
Given a form, detect the changes on the form from the default values (if add) or
from the database values of the object (model instance)
form - Submitted form that is attempting to alter the obj
model - the model class of the obj
obj - instance of model which is being altered
add - are we attempting to add the obj or does it already exist in the database
Returns a dictionary of the fields and their changed values if any
"""
changed_data = {}
if form.is_valid():
if add:
for name, new_value in form.cleaned_data.items():
# Don't consider default values as changed for adding
default_value = model._meta.get_field(name).get_default()
if new_value is not None and new_value != default_value:
# Show what the default value is
changed_data[name] = [str(default_value), new_value]
else:
# Parse the changed data - Note that using form.changed_data would not work because initial is not set
for name, new_value in form.cleaned_data.items():
# Since the form considers initial as the value first shown in the form
# It could be incorrect when user hits save, and then hits "No, go back to edit"
obj.refresh_from_db()
initial_value = getattr(obj, name)
if initial_value != new_value:
changed_data[name] = [initial_value, new_value]
return changed_data
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 super()._changeform_view # This code is taken from super()._changeform_view
to_field = request.POST.get(TO_FIELD_VAR, request.GET.get(TO_FIELD_VAR)) to_field = request.POST.get(TO_FIELD_VAR, request.GET.get(TO_FIELD_VAR))
@ -125,24 +164,7 @@ class AdminConfirmMixin:
form = ModelForm(request.POST, request.FILES, obj) form = ModelForm(request.POST, request.FILES, obj)
# End code from super()._changeform_view # End code from super()._changeform_view
changed_data = {} changed_data = self._get_changed_data(form, model, obj, add)
if form.is_valid():
if add:
for name, new_value in form.cleaned_data.items():
# Don't consider default values as changed for adding
default_value = model._meta.get_field(name).get_default()
if new_value is not None and new_value != default_value:
# Show what the default value is
changed_data[name] = [str(default_value), new_value]
else:
# Parse the changed data - Note that using form.changed_data would not work because initial is not set
for name, new_value in form.cleaned_data.items():
# Since the form considers initial as the value first shown in the form
# It could be incorrect when user hits save, and then hits "No, go back to edit"
obj.refresh_from_db()
initial_value = getattr(obj, name)
if initial_value != new_value:
changed_data[name] = [initial_value, new_value]
changed_confirmation_fields = set( changed_confirmation_fields = set(
self.get_confirmation_fields(request, obj) self.get_confirmation_fields(request, obj)
@ -155,18 +177,17 @@ class AdminConfirmMixin:
form_data = {} form_data = {}
# Parse the original save action from request # Parse the original save action from request
save_action = None save_action = None
for key in request.POST: for key, value in request.POST.items():
if key in ["_save", "_saveasnew", "_addanother", "_continue"]: if key in SAVE_ACTIONS:
save_action = key save_action = key
continue
if key.startswith("_") or key == "csrfmiddlewaretoken": if key.startswith("_") or key == "csrfmiddlewaretoken":
continue continue
form_data[key] = request.POST.get(key)
if add: form_data[key] = value
title_action = _("adding")
else: title_action = _("adding") if add else _("changing")
title_action = _("changing")
context = { context = {
**self.admin_site.each_context(request), **self.admin_site.each_context(request),

View File

@ -1,7 +1,6 @@
from django.test import TestCase, RequestFactory from django.test import TestCase, RequestFactory
from django.contrib.admin.sites import AdminSite from django.contrib.admin.sites import AdminSite
from django.contrib.auth.models import Permission, User from django.contrib.auth.models import Permission, User
from django.contrib.admin.options import TO_FIELD_VAR
from django.urls import reverse from django.urls import reverse

View File

@ -3,8 +3,8 @@ factory-boy~=3.0.1
django-admin-confirm~=0.2.2 django-admin-confirm~=0.2.2
coverage~=5.4 coverage~=5.4
pytest~=6.2.2 pytest~=6.2.2
tox~=3.21.4
pytest-django~=4.1.0 pytest-django~=4.1.0
coverage-badge~=1.0.1 coverage-badge~=1.0.1
readme-renderer~=28.0 readme-renderer~=28.0
twine~=3.3.0 twine~=3.3.0
coveralls~=3.0.0

View File

@ -6,7 +6,7 @@ README = open(os.path.join(here, "README.md")).read()
setup( setup(
name="django-admin-confirm", name="django-admin-confirm",
version="0.2.2", version="0.2.3",
packages=["admin_confirm"], packages=["admin_confirm"],
description="Adds confirmation to Django Admin changes, additions and actions", description="Adds confirmation to Django Admin changes, additions and actions",
long_description_content_type="text/markdown", long_description_content_type="text/markdown",

View File

@ -1 +1 @@
from .local import * from .local import * # noqa

25
tox.ini
View File

@ -1,25 +0,0 @@
# tox (https://tox.readthedocs.io/) is a tool for running tests
# in multiple virtualenvs. This configuration file will run the
# test suite on all supported python versions. To use it, "pip install tox"
# and then run "tox" from this directory.
[pytest]
DJANGO_SETTINGS_MODULE=tests.test_project.settings
addopts = --doctest-modules -ra -l --tb=short --show-capture=log --color=yes
testpaths = admin_confirm
[tox]
envlist =
{py38, py39, py3}-dj{31,30,22,19,17}-postgres
[testenv]
whitelist_externals = pytest
deps =
djmaster: https://github.com/django/django/archive/master.tar.gz
dj31: Django>=3.1,<3.2
dj30: Django>=3.0,<3.1
dj22: Django>=2.2,<2.3
dj19: Django>=1.9,<2.2
dj17: Django>=1.7,<1.9
commands =
pytest