Formatted with black and reached 100% coverage

This commit is contained in:
Thu Trang Pham
2020-11-08 09:51:49 -08:00
parent c586100098
commit a95383cfa2
17 changed files with 465 additions and 179 deletions
+35 -25
View File
@@ -52,21 +52,23 @@ class AdminConfirmMixin:
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" in request.POST):
return self._change_confirmation_view(request, object_id, form_url, extra_context)
if (not object_id and "_confirm_add" in request.POST) or (
object_id and "_confirm_change" in request.POST
):
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
"confirm_add": self.confirm_add,
"confirm_change": self.confirm_change,
}
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 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))
if to_field and not self.to_field_allowed(request, to_field):
raise DisallowedModelAdminToField(
"The field %s cannot be referenced." % to_field
@@ -83,7 +85,6 @@ class AdminConfirmMixin:
obj = None
else:
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)
@@ -105,21 +106,30 @@ class AdminConfirmMixin:
# End code from super()._changeform_view
changed_data = {}
if add:
for name in form.changed_data:
new_value = new_object.__getattribute__(name)
if new_value is not None:
changed_data[name] = [None, new_value]
else:
# Parse the changed data - Note that using form.changed_data would not work because 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 form_validated:
if add:
for name in form.changed_data:
new_value = getattr(new_object, name)
# Don't consider default values as changed for adding
if (
new_value is not None
and new_value != model._meta.get_field(name).default
):
changed_data[name] = [None, new_value]
else:
# Parse the changed data - Note that using form.changed_data would not work because initial is not set
for name, field in form.fields.items():
initial_value = getattr(obj, name)
new_value = getattr(new_object, name)
if (
field.has_changed(initial_value, new_value)
and initial_value != new_value
):
changed_data[name] = [initial_value, new_value]
changed_confirmation_fields = 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())
if not bool(changed_confirmation_fields):
# No confirmation required for changed fields, continue to save
return super()._changeform_view(request, object_id, form_url, extra_context)
@@ -132,14 +142,14 @@ class AdminConfirmMixin:
if key in ["_save", "_saveasnew", "_addanother", "_continue"]:
save_action = key
if key.startswith("_") or key == 'csrfmiddlewaretoken':
if key.startswith("_") or key == "csrfmiddlewaretoken":
continue
form_data[key] = request.POST.get(key)
if add:
title_action = _('adding')
title_action = _("adding")
else:
title_action = _('changing')
title_action = _("changing")
context = {
**self.admin_site.each_context(request),
+225 -57
View File
@@ -1,135 +1,303 @@
from django.test import TestCase, RequestFactory
from django.contrib.auth.models import User
from django.contrib.admin.sites import AdminSite
from tests.market.admin import ItemAdmin
from tests.market.models import Item, Inventory
from django.contrib.admin.options import TO_FIELD_VAR
from django.http import HttpResponseForbidden, HttpResponseBadRequest
from django.urls import reverse
from tests.factories import ItemFactory, ShopFactory
from tests.market.admin import ItemAdmin, InventoryAdmin
from tests.market.models import Item, Inventory
from tests.factories import ItemFactory, ShopFactory, InventoryFactory
class TestAdminConfirmMixin(TestCase):
@classmethod
def setUpTestData(cls):
cls.superuser = User.objects.create_superuser(
username='super', email='super@email.org', password='pass')
username="super", email="super@email.org", password="pass"
)
def setUp(self):
self.client.force_login(self.superuser)
self.factory = RequestFactory()
def test_get_add_without_confirm_add(self):
response = self.client.get(reverse('admin:market_item_add'))
self.assertFalse(response.context_data.get('confirm_add'))
self.assertNotIn('_confirm_add', response.rendered_content)
response = self.client.get(reverse("admin:market_item_add"))
self.assertFalse(response.context_data.get("confirm_add"))
self.assertNotIn("_confirm_add", response.rendered_content)
def test_get_add_with_confirm_add(self):
response = self.client.get(reverse('admin:market_inventory_add'))
self.assertTrue(response.context_data.get('confirm_add'))
self.assertIn('_confirm_add', response.rendered_content)
response = self.client.get(reverse("admin:market_inventory_add"))
self.assertTrue(response.context_data.get("confirm_add"))
self.assertIn("_confirm_add", response.rendered_content)
def test_get_change_without_confirm_change(self):
response = self.client.get(reverse('admin:market_shop_add'))
self.assertFalse(response.context_data.get('confirm_change'))
self.assertNotIn('_confirm_change', response.rendered_content)
response = self.client.get(reverse("admin:market_shop_add"))
self.assertFalse(response.context_data.get("confirm_change"))
self.assertNotIn("_confirm_change", response.rendered_content)
def test_get_change_with_confirm_change(self):
response = self.client.get(reverse('admin:market_inventory_add'))
self.assertTrue(response.context_data.get('confirm_change'))
self.assertIn('_confirm_change', response.rendered_content)
response = self.client.get(reverse("admin:market_inventory_add"))
self.assertTrue(response.context_data.get("confirm_change"))
self.assertIn("_confirm_change", response.rendered_content)
def test_post_add_without_confirm_add(self):
data = {'name': 'name', 'price': 2.0,
'currency': Item.VALID_CURRENCIES[0]}
response = self.client.post(reverse('admin:market_item_add'), data)
data = {"name": "name", "price": 2.0, "currency": Item.VALID_CURRENCIES[0]}
response = self.client.post(reverse("admin:market_item_add"), data)
# Redirects to item changelist and item is added
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, '/admin/market/item/')
self.assertEqual(response.url, "/admin/market/item/")
self.assertEqual(Item.objects.count(), 1)
def test_post_add_with_confirm_add(self):
item = ItemFactory()
shop = ShopFactory()
data = {'shop': shop.id, 'item': item.id,
'quantity': 5, '_confirm_add': True}
response = self.client.post(
reverse('admin:market_inventory_add'), data)
data = {"shop": shop.id, "item": item.id, "quantity": 5, "_confirm_add": True}
response = self.client.post(reverse("admin:market_inventory_add"), data)
# Ensure not redirected (confirmation page does not redirect)
self.assertEqual(response.status_code, 200)
expected_templates = [
'admin/market/inventory/change_confirmation.html',
'admin/market/change_confirmation.html',
'admin/change_confirmation.html'
"admin/market/inventory/change_confirmation.html",
"admin/market/change_confirmation.html",
"admin/change_confirmation.html",
]
self.assertEqual(response.template_name, expected_templates)
form_data = {'shop': str(shop.id), 'item': str(
item.id), 'quantity': str(5)}
self.assertEqual(
response.context_data['form_data'], form_data)
form_data = {"shop": str(shop.id), "item": str(item.id), "quantity": str(5)}
self.assertEqual(response.context_data["form_data"], form_data)
for k, v in form_data.items():
self.assertIn(
f'<input type="hidden" name="{ k }" value="{ v }">', response.rendered_content)
f'<input type="hidden" name="{ k }" value="{ v }">',
response.rendered_content,
)
# Should not have been added yet
self.assertEqual(Inventory.objects.count(), 0)
def test_post_change_with_confirm_change(self):
item = ItemFactory(name='item')
data = {'name': 'name', 'price': 2.0,
'currency': Item.VALID_CURRENCIES[0], '_confirm_change': True}
response = self.client.post(
f'/admin/market/item/{item.id}/change/', data)
item = ItemFactory(name="item")
data = {
"name": "name",
"price": 2.0,
"currency": Item.VALID_CURRENCIES[0],
"id": item.id,
"_confirm_change": True,
"csrfmiddlewaretoken": "fake token",
"_save": True,
}
response = self.client.post(f"/admin/market/item/{item.id}/change/", data)
# Ensure not redirected (confirmation page does not redirect)
self.assertEqual(response.status_code, 200)
expected_templates = [
'admin/market/item/change_confirmation.html',
'admin/market/change_confirmation.html',
'admin/change_confirmation.html'
"admin/market/item/change_confirmation.html",
"admin/market/change_confirmation.html",
"admin/change_confirmation.html",
]
self.assertEqual(response.template_name, expected_templates)
form_data = {'name': 'name', 'price': str(2.0),
'currency': Item.VALID_CURRENCIES[0][0]}
self.assertEqual(
response.context_data['form_data'], form_data)
form_data = {
"name": "name",
"price": str(2.0),
"id": str(item.id),
"currency": Item.VALID_CURRENCIES[0][0],
}
self.assertEqual(response.context_data["form_data"], form_data)
for k, v in form_data.items():
self.assertIn(
f'<input type="hidden" name="{ k }" value="{ v }">', response.rendered_content)
f'<input type="hidden" name="{ k }" value="{ v }">',
response.rendered_content,
)
# Hasn't changed item yet
item.refresh_from_db()
self.assertEqual(item.name, 'item')
self.assertEqual(item.name, "item")
def test_post_change_without_confirm_change(self):
shop = ShopFactory(name='bob')
data = {'name': 'sally'}
response = self.client.post(
f'/admin/market/shop/{shop.id}/change/', data)
shop = ShopFactory(name="bob")
data = {"name": "sally"}
response = self.client.post(f"/admin/market/shop/{shop.id}/change/", data)
# Redirects to changelist
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, '/admin/market/shop/')
self.assertEqual(response.url, "/admin/market/shop/")
# Shop has changed
shop.refresh_from_db()
self.assertEqual(shop.name, 'sally')
self.assertEqual(shop.name, "sally")
def test_get_confirmation_fields_should_default_if_not_set(self):
expected_fields = [f.name for f in Item._meta.fields if f.name != 'id']
expected_fields = [f.name for f in Item._meta.fields if f.name != "id"]
ItemAdmin.confirmation_fields = None
admin = ItemAdmin(Item, AdminSite())
actual_fields = admin.get_confirmation_fields(self.factory.request())
self.assertEqual(expected_fields, actual_fields)
def test_get_confirmation_fields_if_set(self):
expected_fields = ['name', 'currency']
expected_fields = ["name", "currency"]
ItemAdmin.confirmation_fields = expected_fields
admin = ItemAdmin(Item, AdminSite())
actual_fields = admin.get_confirmation_fields(self.factory.request())
self.assertEqual(expected_fields, actual_fields)
def test_custom_template(self):
expected_template = 'market/admin/my_custom_template.html'
expected_template = "market/admin/my_custom_template.html"
ItemAdmin.confirmation_template = expected_template
admin = ItemAdmin(Item, AdminSite())
actual_template = admin.render_change_confirmation(
self.factory.request(), context={}).template_name
self.factory.request(), context={}
).template_name
self.assertEqual(expected_template, actual_template)
ItemAdmin.confirmation_template = None
def test_form_invalid(self):
self.assertEqual(InventoryAdmin.confirmation_fields, ["quantity"])
inventory = InventoryFactory(quantity=1)
data = {
"quantity": 1,
"shop": "Invalid value",
"item": "Invalid value",
"id": inventory.id,
"_confirm_change": True,
"csrfmiddlewaretoken": "fake token",
}
response = self.client.post(
f"/admin/market/inventory/{inventory.id}/change/", data
)
# Form invalid should show erros on form
self.assertEqual(response.status_code, 200)
print(response.rendered_content)
self.assertIsNotNone(response.context_data.get("errors"))
self.assertEqual(
response.context_data["errors"][0],
["Select a valid choice. That choice is not one of the available choices."],
)
# Should not have updated inventory
inventory.refresh_from_db()
self.assertEqual(inventory.quantity, 1)
def test_confirmation_fields_set_with_confirm_change(self):
self.assertEqual(InventoryAdmin.confirmation_fields, ["quantity"])
inventory = InventoryFactory()
another_shop = ShopFactory()
data = {
"quantity": inventory.quantity,
"id": inventory.id,
"item": inventory.item.id,
"shop": another_shop.id,
"_confirm_change": True,
"csrfmiddlewaretoken": "fake token",
}
response = self.client.post(
f"/admin/market/inventory/{inventory.id}/change/", data
)
# Should not have shown confirmation page since shop did not change
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, reverse("admin:market_inventory_changelist"))
# Should have updated inventory
inventory.refresh_from_db()
self.assertEqual(inventory.shop, another_shop)
def test_confirmation_fields_set_with_confirm_add(self):
self.assertEqual(InventoryAdmin.confirmation_fields, ["quantity"])
item = ItemFactory()
shop = ShopFactory()
# Don't set quantity - let it default
data = {"shop": shop.id, "item": item.id, "_confirm_add": True}
response = self.client.post(reverse("admin:market_inventory_add"), data)
# No confirmation needed
self.assertEqual(response.status_code, 302)
# Should have been added
self.assertEqual(Inventory.objects.count(), 1)
new_inventory = Inventory.objects.all().first()
self.assertEqual(new_inventory.shop, shop)
self.assertEqual(new_inventory.item, item)
self.assertEqual(
new_inventory.quantity, Inventory._meta.get_field("quantity").default
)
def test_no_change_permissions(self):
user = User.objects.create_user(username="user", is_staff=True)
self.client.force_login(user)
inventory = InventoryFactory()
data = {
"quantity": 1000,
"id": inventory.id,
"item": inventory.item.id,
"shop": inventory.shop.id,
"_confirm_change": True,
"csrfmiddlewaretoken": "fake token",
}
response = self.client.post(
f"/admin/market/inventory/{inventory.id}/change/", data
)
self.assertEqual(response.status_code, 403)
self.assertTrue(isinstance(response, HttpResponseForbidden))
old_quantity = inventory.quantity
inventory.refresh_from_db()
self.assertEqual(inventory.quantity, old_quantity)
def test_no_add_permissions(self):
user = User.objects.create_user(username="user", is_staff=True)
self.client.force_login(user)
item = ItemFactory()
shop = ShopFactory()
data = {"shop": shop.id, "item": item.id, "quantity": 5, "_confirm_add": True}
response = self.client.post(reverse("admin:market_inventory_add"), data)
# Ensure not redirected (confirmation page does not redirect)
self.assertEqual(response.status_code, 403)
self.assertTrue(isinstance(response, HttpResponseForbidden))
# Should not have been added
self.assertEqual(Inventory.objects.count(), 0)
def test_obj_not_found(self):
inventory = InventoryFactory()
data = {
"quantity": 1000,
"id": 100,
"item": inventory.item.id,
"shop": inventory.shop.id,
"_confirm_change": True,
"csrfmiddlewaretoken": "fake token",
}
response = self.client.post("/admin/market/inventory/100/change/", data)
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, "/admin/")
self.assertEqual(response.reason_phrase, "Found")
old_quantity = inventory.quantity
inventory.refresh_from_db()
self.assertEqual(inventory.quantity, old_quantity)
self.assertEqual(Inventory.objects.count(), 1)
def test_handles_to_field_not_allowed(self):
item = ItemFactory()
shop = ShopFactory()
data = {
"shop": shop.id,
"item": item.id,
"quantity": 5,
"_confirm_add": True,
TO_FIELD_VAR: "shop",
}
response = self.client.post(reverse("admin:market_inventory_add"), data)
# Ensure not redirected (confirmation page does not redirect)
self.assertEqual(response.status_code, 400)
self.assertTrue(isinstance(response, HttpResponseBadRequest))
self.assertEqual(response.reason_phrase, "Bad Request")
self.assertEqual(
response.context.get("exception_value"),
"The field shop cannot be referenced.",
)
# Should not have been added
self.assertEqual(Inventory.objects.count(), 0)