fix(MR-16): Use cache for FileField and ImageField (#17)

* Remove casting to list in _get_form_data

* Use cache for most fields and admin form for m2m files

* MR comments/clean up

* Cache should obey exclude and fields

* Some more tests and docs

* Only use cache for image files

* Even more tests and handle save as new

* fix test

* More tests

* minor refactor

* Improve test coverage

* Add no cover for some places

* V0.2.3.dev7

* Adding tests for fieldsets

* Added cache timeout

* Added another test for an edge case

* Fix issue with ManagementForm tampered with

* Update cache to only set when form is_multipart

* Even more testing changes

* Update based on comments on MR and clean up a bit

* make test names better

Co-authored-by: Thu Trang Pham <thu@joinmodernhealth.com>
This commit is contained in:
Thu Trang Pham
2021-02-27 15:39:01 -08:00
committed by GitHub
parent cc36492bfe
commit 06d3e1a208
31 changed files with 3017 additions and 116 deletions
@@ -0,0 +1,307 @@
from unittest import mock
from django.core.cache import cache
from admin_confirm.tests.helpers import AdminConfirmTestCase
from tests.market.admin import ShoppingMallAdmin
from tests.market.models import GeneralManager, ShoppingMall, Town
from tests.factories import ShopFactory
from admin_confirm.constants import CACHE_KEYS, CONFIRMATION_RECEIVED
@mock.patch.object(ShoppingMallAdmin, "inlines", [])
class TestAdminOptions(AdminConfirmTestCase):
@mock.patch.object(ShoppingMallAdmin, "confirmation_fields", ["name"])
@mock.patch.object(ShoppingMallAdmin, "fields", ["name", "town"])
def test_change_model_with_m2m_field_without_input_for_m2m_field_should_work(self):
gm = GeneralManager.objects.create(name="gm")
shops = [ShopFactory() for i in range(3)]
town = Town.objects.create(name="town")
mall = ShoppingMall.objects.create(name="mall", general_manager=gm, town=town)
mall.shops.set(shops)
# new values
gm2 = GeneralManager.objects.create(name="gm2")
shops2 = [ShopFactory() for i in range(3)]
town2 = Town.objects.create(name="town2")
data = {
"id": mall.id,
"name": "name",
"town": town2.id,
"_confirm_change": True,
"_continue": True,
}
response = self.client.post(
f"/admin/market/shoppingmall/{mall.id}/change/", data=data
)
# Should be shown confirmation page
self._assertSubmitHtml(
rendered_content=response.rendered_content, save_action="_continue"
)
# Should not have cached the unsaved obj
cached_item = cache.get(CACHE_KEYS["object"])
self.assertIsNone(cached_item)
# Should not have saved changes yet
self.assertEqual(ShoppingMall.objects.count(), 1)
mall.refresh_from_db()
self.assertEqual(mall.name, "mall")
self.assertEqual(mall.general_manager, gm)
self.assertEqual(mall.town, town)
for shop in mall.shops.all():
self.assertIn(shop, shops)
# Click "Yes, I'm Sure"
confirmation_received_data = data
del confirmation_received_data["_confirm_change"]
response = self.client.post(
f"/admin/market/shoppingmall/{mall.id}/change/",
data=confirmation_received_data,
)
# Should not have redirected to changelist
self.assertEqual(response.url, f"/admin/market/shoppingmall/{mall.id}/change/")
# Should have saved obj
self.assertEqual(ShoppingMall.objects.count(), 1)
saved_item = ShoppingMall.objects.all().first()
# should have updated fields that were in form
self.assertEqual(saved_item.name, data["name"])
self.assertEqual(saved_item.town, town2)
# should have presevered the fields that are not in form
self.assertEqual(saved_item.general_manager, gm)
for shop in saved_item.shops.all():
self.assertIn(shop, shops)
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
@mock.patch.object(ShoppingMallAdmin, "confirmation_fields", ["name"])
@mock.patch.object(ShoppingMallAdmin, "exclude", ["shops"])
def test_when_m2m_field_in_exclude_changes_to_field_should_not_be_saved(self):
gm = GeneralManager.objects.create(name="gm")
shops = [ShopFactory() for i in range(3)]
town = Town.objects.create(name="town")
mall = ShoppingMall.objects.create(name="mall", general_manager=gm, town=town)
mall.shops.set(shops)
# new values
gm2 = GeneralManager.objects.create(name="gm2")
shops2 = [ShopFactory() for i in range(3)]
town2 = Town.objects.create(name="town2")
data = {
"id": mall.id,
"name": "name",
"general_manager": gm2.id,
"shops": [1],
"town": town2.id,
"_confirm_change": True,
"_continue": True,
}
response = self.client.post(
f"/admin/market/shoppingmall/{mall.id}/change/", data=data
)
# Should be shown confirmation page
self._assertSubmitHtml(
rendered_content=response.rendered_content, save_action="_continue"
)
# Should not have cached the unsaved obj
cached_item = cache.get(CACHE_KEYS["object"])
self.assertIsNone(cached_item)
# Should not have saved changes yet
self.assertEqual(ShoppingMall.objects.count(), 1)
mall.refresh_from_db()
self.assertEqual(mall.name, "mall")
self.assertEqual(mall.general_manager, gm)
self.assertEqual(mall.town, town)
for shop in mall.shops.all():
self.assertIn(shop, shops)
# Click "Yes, I'm Sure"
confirmation_received_data = data
del confirmation_received_data["_confirm_change"]
response = self.client.post(
f"/admin/market/shoppingmall/{mall.id}/change/",
data=confirmation_received_data,
)
# Should not have redirected to changelist
self.assertEqual(response.url, f"/admin/market/shoppingmall/{mall.id}/change/")
# Should have saved obj
self.assertEqual(ShoppingMall.objects.count(), 1)
saved_item = ShoppingMall.objects.all().first()
# should have updated fields that were in form
self.assertEqual(saved_item.name, data["name"])
self.assertEqual(saved_item.town, town2)
self.assertEqual(saved_item.general_manager, gm2)
# should have presevered the fields that are not in form (exclude)
for shop in saved_item.shops.all():
self.assertIn(shop, shops)
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
@mock.patch.object(ShoppingMallAdmin, "confirmation_fields", ["name"])
@mock.patch.object(ShoppingMallAdmin, "exclude", ["shops", "name"])
@mock.patch.object(ShoppingMallAdmin, "inlines", [])
def test_if_confirmation_fields_in_exclude_should_not_trigger_confirmation(self):
gm = GeneralManager.objects.create(name="gm")
shops = [ShopFactory() for i in range(3)]
town = Town.objects.create(name="town")
mall = ShoppingMall.objects.create(name="mall", general_manager=gm, town=town)
mall.shops.set(shops)
# new values
gm2 = GeneralManager.objects.create(name="gm2")
shops2 = [ShopFactory() for i in range(3)]
town2 = Town.objects.create(name="town2")
data = {
"id": mall.id,
"name": "name",
"general_manager": gm2.id,
"shops": [1],
"town": town2.id,
"_confirm_change": True,
"_continue": True,
}
response = self.client.post(
f"/admin/market/shoppingmall/{mall.id}/change/", data=data
)
# Should not be shown confirmation page
# SInce we used "Save and Continue", should show change page
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, f"/admin/market/shoppingmall/{mall.id}/change/")
# Should have saved the non excluded fields
mall.refresh_from_db()
for shop in shops:
self.assertIn(shop, mall.shops.all())
self.assertEqual(mall.name, "mall")
# Should have saved other fields
self.assertEqual(mall.town, town2)
self.assertEqual(mall.general_manager, gm2)
@mock.patch.object(ShoppingMallAdmin, "confirmation_fields", ["name"])
@mock.patch.object(ShoppingMallAdmin, "readonly_fields", ["shops", "name"])
@mock.patch.object(ShoppingMallAdmin, "inlines", [])
def test_if_confirmation_fields_in_readonly_should_not_trigger_confirmation(self):
gm = GeneralManager.objects.create(name="gm")
shops = [ShopFactory() for i in range(3)]
town = Town.objects.create(name="town")
mall = ShoppingMall.objects.create(name="mall", general_manager=gm, town=town)
mall.shops.set(shops)
# new values
gm2 = GeneralManager.objects.create(name="gm2")
shops2 = [ShopFactory() for i in range(3)]
town2 = Town.objects.create(name="town2")
data = {
"id": mall.id,
"name": "name",
"general_manager": gm2.id,
"shops": [1],
"town": town2.id,
"_confirm_change": True,
"_continue": True,
}
response = self.client.post(
f"/admin/market/shoppingmall/{mall.id}/change/", data=data
)
# Should not be shown confirmation page
# SInce we used "Save and Continue", should show change page
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, f"/admin/market/shoppingmall/{mall.id}/change/")
# Should have saved the non excluded fields
mall.refresh_from_db()
for shop in shops:
self.assertIn(shop, mall.shops.all())
self.assertEqual(mall.name, "mall")
# Should have saved other fields
self.assertEqual(mall.town, town2)
self.assertEqual(mall.general_manager, gm2)
@mock.patch.object(ShoppingMallAdmin, "confirmation_fields", ["name"])
@mock.patch.object(ShoppingMallAdmin, "readonly_fields", ["shops"])
@mock.patch.object(ShoppingMallAdmin, "inlines", [])
def test_readonly_fields_should_not_change(self):
gm = GeneralManager.objects.create(name="gm")
shops = [ShopFactory() for i in range(3)]
town = Town.objects.create(name="town")
mall = ShoppingMall.objects.create(name="mall", general_manager=gm, town=town)
mall.shops.set(shops)
# new values
gm2 = GeneralManager.objects.create(name="gm2")
shops2 = [ShopFactory() for i in range(3)]
town2 = Town.objects.create(name="town2")
data = {
"id": mall.id,
"name": "name",
"general_manager": gm2.id,
"shops": [1],
"town": town2.id,
"_confirm_change": True,
"_continue": True,
}
response = self.client.post(
f"/admin/market/shoppingmall/{mall.id}/change/", data=data
)
# Should be shown confirmation page
self._assertSubmitHtml(
rendered_content=response.rendered_content, save_action="_continue"
)
# Should not have cached the unsaved obj
cached_item = cache.get(CACHE_KEYS["object"])
self.assertIsNone(cached_item)
# Should not have saved changes yet
self.assertEqual(ShoppingMall.objects.count(), 1)
mall.refresh_from_db()
self.assertEqual(mall.name, "mall")
self.assertEqual(mall.general_manager, gm)
self.assertEqual(mall.town, town)
for shop in mall.shops.all():
self.assertIn(shop, shops)
# Click "Yes, I'm Sure"
confirmation_received_data = data
del confirmation_received_data["_confirm_change"]
response = self.client.post(
f"/admin/market/shoppingmall/{mall.id}/change/",
data=confirmation_received_data,
)
# Should not have redirected to changelist
self.assertEqual(response.url, f"/admin/market/shoppingmall/{mall.id}/change/")
# Should have saved obj
self.assertEqual(ShoppingMall.objects.count(), 1)
saved_item = ShoppingMall.objects.all().first()
# should have updated fields that were in form
self.assertEqual(saved_item.name, data["name"])
self.assertEqual(saved_item.town, town2)
self.assertEqual(saved_item.general_manager, gm2)
# should have presevered the fields that are not in form (exclude)
for shop in saved_item.shops.all():
self.assertIn(shop, shops)
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
@@ -0,0 +1,280 @@
from django.test import TestCase, RequestFactory
from django.contrib.admin.sites import AdminSite
from django.contrib.auth.models import Permission, User
from django.urls import reverse
from tests.market.admin import ShopAdmin
from tests.market.models import Shop
class TestConfirmActions(TestCase):
@classmethod
def setUpTestData(cls):
cls.superuser = User.objects.create_superuser(
username="super", email="super@email.org", password="pass"
)
def setUp(self):
self.client.force_login(self.superuser)
self.factory = RequestFactory()
def test_get_changelist_should_not_be_affected(self):
response = self.client.get(reverse("admin:market_shop_changelist"))
self.assertIsNotNone(response)
self.assertNotIn("Confirm Action", response.rendered_content)
def test_action_without_confirmation(self):
post_params = {
"action": ["show_message_no_confirmation"],
"select_across": ["0"],
"index": ["0"],
"_selected_action": ["3", "2", "1"],
}
response = self.client.post(
reverse("admin:market_shop_changelist"),
data=post_params,
follow=True, # Follow the redirect to get content
)
self.assertIsNotNone(response)
self.assertEqual(response.status_code, 200)
# Should not use confirmaiton page
self.assertNotIn("action_confirmation", response.template_name)
# The action was to show user a message
self.assertIn("You selected without confirmation", response.rendered_content)
def test_action_with_confirmation_should_show_confirmation_page(self):
post_params = {
"action": ["show_message"],
"select_across": ["0"],
"index": ["0"],
"_selected_action": ["3", "2", "1"],
}
response = self.client.post(
reverse("admin:market_shop_changelist"),
data=post_params,
follow=True, # Follow the redirect to get content
)
self.assertIsNotNone(response)
self.assertEqual(response.status_code, 200)
# Should use confirmaiton page
self.assertEqual(
response.template_name,
[
"admin/market/shop/action_confirmation.html",
"admin/market/action_confirmation.html",
"admin/action_confirmation.html",
],
)
# The action was to show user a message, and should not happen yet
self.assertNotIn("You selected", response.rendered_content)
def test_no_permissions_in_database_for_action_with_confirmation(self):
"""
Django would not show the action in changelist action selector
If the user doesn't have permissions, but this doesn't prevent
user from calling post with the params to perform the action.
If the permissions are denied because of Permission in the database,
Django would redirect to the changelist.
"""
# Create a user without permissions for action
user = User.objects.create_user(
username="user",
email="user@email.org",
password="pass",
is_active=True,
is_staff=True,
is_superuser=False,
)
# Give user permissions to ShopAdmin change, add, view but not delete
for permission in Permission.objects.filter(
codename__in=["change_shop", "view_shop", "add_shop"]
):
user.user_permissions.add(permission)
self.client.force_login(user)
post_params = {
"action": ["show_message"],
"select_across": ["0"],
"index": ["0"],
"_selected_action": ["3", "2", "1"],
}
response = self.client.post(
reverse("admin:market_shop_changelist"),
data=post_params,
follow=True, # Follow the redirect to get content
)
self.assertIsNotNone(response)
self.assertEqual(response.status_code, 200)
# Should not use confirmaiton page
self.assertEqual(
response.template_name,
[
"admin/market/shop/change_list.html",
"admin/market/change_list.html",
"admin/change_list.html",
],
)
# The action was to show user a message, and should not happen
self.assertNotIn("You selected", response.rendered_content)
# Django won't show the action as an option to you
self.assertIn("No action selected", response.rendered_content)
def test_no_permissions_in_code_non_superuser_for_action_with_confirmation(self):
"""
Django would not show the action in changelist action selector
If the user doesn't have permissions, but this doesn't prevent
user from calling post with the params to perform the action.
If the permissions are denied because of Permission in the database,
Django would redirect to the changelist.
It should also respect the has_xxx_permission methods
"""
# Create a user without permissions for action
user = User.objects.create_user(
username="user",
email="user@email.org",
password="pass",
is_active=True,
is_staff=True,
is_superuser=False,
)
# Give user permissions to ShopAdmin change, add, view and delete
for permission in Permission.objects.filter(
codename__in=["change_shop", "view_shop", "add_shop", "delete_shop"]
):
user.user_permissions.add(permission)
self.client.force_login(user)
# ShopAdmin has defined:
# def has_delete_permission(self, request, obj=None):
# return request.user.is_superuser
post_params = {
"action": ["show_message"],
"select_across": ["0"],
"index": ["0"],
"_selected_action": ["3", "2", "1"],
}
response = self.client.post(
reverse("admin:market_shop_changelist"),
data=post_params,
follow=True, # Follow the redirect to get content
)
self.assertIsNotNone(response)
self.assertEqual(response.status_code, 200)
# Should not use confirmaiton page
self.assertEqual(
response.template_name,
[
"admin/market/shop/change_list.html",
"admin/market/change_list.html",
"admin/change_list.html",
],
)
# The action was to show user a message, and should not happen yet
self.assertNotIn("You selected", response.rendered_content)
# Django won't show the action as an option to you
self.assertIn("No action selected", response.rendered_content)
def test_no_permissions_in_code_superuser_for_action_with_confirmation(self):
"""
Django would not show the action in changelist action selector
If the user doesn't have permissions, but this doesn't prevent
user from calling post with the params to perform the action.
When permissions are denied from a change in code
(ie has_xxx_permission in ModelAdmin), Django should still
redirect to changelist. This should be true even if the user is
a superuser.
"""
# ShopAdmin has defined:
# def has_delete_permission(self, request, obj=None):
# return request.user.is_superuser
ShopAdmin.has_delete_permission = lambda self, request, obj=None: False
post_params = {
"action": ["show_message"],
"select_across": ["0"],
"index": ["0"],
"_selected_action": ["3", "2", "1"],
}
response = self.client.post(
reverse("admin:market_shop_changelist"),
data=post_params,
follow=True, # Follow the redirect to get content
)
self.assertIsNotNone(response)
self.assertEqual(response.status_code, 200)
# Should not use confirmaiton page
self.assertEqual(
response.template_name,
[
"admin/market/shop/change_list.html",
"admin/market/change_list.html",
"admin/change_list.html",
],
)
# The action was to show user a message, and should not happen yet
self.assertNotIn("You selected", response.rendered_content)
# Django won't show the action as an option to you
self.assertIn("No action selected", response.rendered_content)
# Remove our modification for ShopAdmin
ShopAdmin.has_delete_permission = (
lambda self, request, obj=None: request.user.is_superuser
)
def test_confirm_action_submit_button_should_perform_action(self):
"""
The submit button should have param "_confirm_action"
Simulate calling the post request that the button would
"""
post_params = {
"_confirm_action": ["Yes, I'm sure"],
"action": ["show_message"],
"_selected_action": ["3", "2", "1"],
}
response = self.client.post(
reverse("admin:market_shop_changelist"),
data=post_params,
follow=True, # Follow the redirect to get content
)
self.assertIsNotNone(response)
self.assertEqual(response.status_code, 200)
# Should not use confirmaiton page, since we clicked Yes, I'm sure
self.assertEqual(
response.template_name,
[
"admin/market/shop/change_list.html",
"admin/market/change_list.html",
"admin/change_list.html",
],
)
# The action was to show user a message, and should happen
self.assertIn("You selected", response.rendered_content)
def test_should_use_action_confirmation_template_if_set(self):
expected_template = "market/admin/my_custom_template.html"
ShopAdmin.action_confirmation_template = expected_template
admin = ShopAdmin(Shop, AdminSite())
actual_template = admin.render_action_confirmation(
self.factory.request(), context={}
).template_name
self.assertEqual(expected_template, actual_template)
# Clear our setting to not affect other tests
ShopAdmin.action_confirmation_template = None
@@ -0,0 +1,346 @@
from unittest import mock
from django.contrib.auth.models import User
from django.contrib.admin.sites import AdminSite
from django.contrib.admin.options import TO_FIELD_VAR
from django.http import HttpResponseForbidden, HttpResponseBadRequest
from django.urls import reverse
from admin_confirm.tests.helpers import AdminConfirmTestCase
from tests.market.admin import ItemAdmin, InventoryAdmin, ShoppingMallAdmin
from tests.market.models import Item, Inventory, ShoppingMall
from tests.factories import ItemFactory, ShopFactory, InventoryFactory
@mock.patch.object(ShoppingMallAdmin, "inlines", [])
class TestConfirmChangeAndAdd(AdminConfirmTestCase):
def test_get_add_without_confirm_add(self):
ItemAdmin.confirm_add = False
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)
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)
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)
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)
# Redirects to item changelist and item is added
self.assertEqual(response.status_code, 302)
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,
"_continue": 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",
]
self.assertEqual(response.template_name, expected_templates)
form_data = {"shop": str(shop.id), "item": str(item.id), "quantity": str(5)}
self._assertSimpleFieldFormHtml(
rendered_content=response.rendered_content, fields=form_data
)
self._assertSubmitHtml(
rendered_content=response.rendered_content, save_action="_continue"
)
# Should not have been added yet
self.assertEqual(Inventory.objects.count(), 0)
def test_post_change_with_confirm_change_shoppingmall_name(self):
# When testing found that even though name was in confirmation_fields
# When only name changed, `form.is_valid` = False, and thus didn't trigger
# confirmation page previously, even though it should have
mall = ShoppingMall.objects.create(name="name")
data = {
"id": mall.id,
"name": "new name",
"_confirm_change": True,
"csrfmiddlewaretoken": "fake token",
"_save": True,
}
response = self.client.post(
f"/admin/market/shoppingmall/{mall.id}/change/", data
)
# Ensure not redirected (confirmation page does not redirect)
self.assertEqual(response.status_code, 200)
expected_templates = [
"admin/market/shoppingmall/change_confirmation.html",
"admin/market/change_confirmation.html",
"admin/change_confirmation.html",
]
self.assertEqual(response.template_name, expected_templates)
self._assertSubmitHtml(rendered_content=response.rendered_content)
# Hasn't changed item yet
mall.refresh_from_db()
self.assertEqual(mall.name, "name")
def test_post_change_with_confirm_change(self):
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",
]
self.assertEqual(response.template_name, expected_templates)
form_data = {
"name": "name",
"price": str(2.0),
# "id": str(item.id),
"currency": Item.VALID_CURRENCIES[0][0],
}
self._assertSimpleFieldFormHtml(
rendered_content=response.rendered_content, fields=form_data
)
self._assertSubmitHtml(
rendered_content=response.rendered_content, multipart_form=True
)
# Hasn't changed item yet
item.refresh_from_db()
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)
# Redirects to changelist
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, "/admin/market/shop/")
# Shop has changed
shop.refresh_from_db()
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"]
ItemAdmin.confirmation_fields = None
ItemAdmin.fields = expected_fields
admin = ItemAdmin(Item, AdminSite())
actual_fields = admin.get_confirmation_fields(self.factory.request())
for field in expected_fields:
self.assertIn(field, actual_fields)
def test_get_confirmation_fields_default_should_only_include_fields_shown_on_admin(
self,
):
admin_fields = ["name", "price"]
ItemAdmin.confirmation_fields = None
ItemAdmin.fields = admin_fields
admin = ItemAdmin(Item, AdminSite())
actual_fields = admin.get_confirmation_fields(self.factory.request())
for field in admin_fields:
self.assertIn(field, actual_fields)
def test_get_confirmation_fields_if_set(self):
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"
ItemAdmin.change_confirmation_template = expected_template
admin = ItemAdmin(Item, AdminSite())
actual_template = admin.render_change_confirmation(
self.factory.request(), context={}
).template_name
self.assertEqual(expected_template, actual_template)
# Clear our setting to not affect other tests
ItemAdmin.change_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 errors on form
self.assertEqual(response.status_code, 200)
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)
@@ -0,0 +1,195 @@
from unittest import mock
from admin_confirm.admin import AdminConfirmMixin
from django.urls import reverse
from admin_confirm.tests.helpers import AdminConfirmTestCase
from tests.market.admin import ShoppingMallAdmin
from tests.market.models import ShoppingMall
from tests.factories import ShopFactory
@mock.patch.object(ShoppingMallAdmin, "inlines", [])
class TestConfirmChangeAndAddM2MField(AdminConfirmTestCase):
def test_post_add_without_confirm_add_m2m(self):
shops = [ShopFactory() for i in range(3)]
data = {"name": "name", "shops": [s.id for s in shops]}
response = self.client.post(reverse("admin:market_shoppingmall_add"), data)
# Redirects to changelist and is added
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, "/admin/market/shoppingmall/")
self.assertEqual(ShoppingMall.objects.count(), 1)
self.assertEqual(ShoppingMall.objects.all().first().shops.count(), 3)
def test_post_add_with_confirm_add_m2m(self):
ShoppingMallAdmin.confirmation_fields = ["shops"]
shops = [ShopFactory() for i in range(3)]
data = {
"name": "name",
"shops": [s.id for s in shops],
"_confirm_add": True,
"_save": True,
}
response = self.client.post(reverse("admin:market_shoppingmall_add"), data)
# Ensure not redirected (confirmation page does not redirect)
self.assertEqual(response.status_code, 200)
expected_templates = [
"admin/market/shoppingmall/change_confirmation.html",
"admin/market/change_confirmation.html",
"admin/change_confirmation.html",
]
self.assertEqual(response.template_name, expected_templates)
self._assertManyToManyFormHtml(
rendered_content=response.rendered_content,
options=shops,
selected_ids=data["shops"],
)
self._assertSubmitHtml(rendered_content=response.rendered_content)
# Should not have been added yet
self.assertEqual(ShoppingMall.objects.count(), 0)
# Confirmation page would not have the _confirm_add sent on submit
del data["_confirm_add"]
# Selecting to "Yes, I'm sure" on the confirmation page
# Would post to the same endpoint
response = self.client.post(reverse("admin:market_shoppingmall_add"), data)
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, "/admin/market/shoppingmall/")
self.assertEqual(ShoppingMall.objects.count(), 1)
self.assertEqual(ShoppingMall.objects.all().first().shops.count(), 3)
def test_m2m_field_post_change_with_confirm_change(self):
shops = [ShopFactory() for i in range(10)]
shopping_mall = ShoppingMall.objects.create(name="My Mall")
shopping_mall.shops.set(shops)
# Currently ShoppingMall configured with confirmation_fields = ['name']
data = {
"name": "Not My Mall",
"shops": [1, 2],
"id": shopping_mall.id,
"_confirm_change": True,
"csrfmiddlewaretoken": "fake token",
"_continue": True,
}
response = self.client.post(
f"/admin/market/shoppingmall/{shopping_mall.id}/change/", data
)
# Ensure not redirected (confirmation page does not redirect)
self.assertEqual(response.status_code, 200)
expected_templates = [
"admin/market/shoppingmall/change_confirmation.html",
"admin/market/change_confirmation.html",
"admin/change_confirmation.html",
]
self.assertEqual(response.template_name, expected_templates)
# Should show two lists for the m2m current and modified values
self.assertEqual(response.rendered_content.count("<ul>"), 2)
self._assertManyToManyFormHtml(
rendered_content=response.rendered_content,
options=shops,
selected_ids=data["shops"],
)
self._assertSubmitHtml(
rendered_content=response.rendered_content, save_action="_continue"
)
# Hasn't changed item yet
shopping_mall.refresh_from_db()
self.assertEqual(shopping_mall.name, "My Mall")
# Selecting to "Yes, I'm sure" on the confirmation page
# Would post to the same endpoint
del data["_confirm_change"]
response = self.client.post(
f"/admin/market/shoppingmall/{shopping_mall.id}/change/", data
)
# will show the change page for this shopping_mall
self.assertEqual(response.status_code, 302)
self.assertEqual(
response.url, f"/admin/market/shoppingmall/{shopping_mall.id}/change/"
)
# Should not be the confirmation page, we already confirmed change
self.assertNotEqual(response.templates, expected_templates)
self.assertEqual(ShoppingMall.objects.count(), 1)
self.assertEqual(ShoppingMall.objects.all().first().shops.count(), 2)
def test_m2m_field_post_change_with_confirm_change_multiple_selected(self):
shops = [ShopFactory() for i in range(10)]
shopping_mall = ShoppingMall.objects.create(name="My Mall")
shopping_mall.shops.set(shops)
# Currently ShoppingMall configured with confirmation_fields = ['name']
data = {
"name": "Not My Mall",
"shops": [1, 2, 3],
"id": shopping_mall.id,
"_confirm_change": True,
"csrfmiddlewaretoken": "fake token",
"_save": True,
}
response = self.client.post(
f"/admin/market/shoppingmall/{shopping_mall.id}/change/", data
)
# Ensure not redirected (confirmation page does not redirect)
self.assertEqual(response.status_code, 200)
expected_templates = [
"admin/market/shoppingmall/change_confirmation.html",
"admin/market/change_confirmation.html",
"admin/change_confirmation.html",
]
self.assertEqual(response.template_name, expected_templates)
# Hasn't changed item yet
shopping_mall.refresh_from_db()
self.assertEqual(shopping_mall.name, "My Mall")
def test_post_change_without_confirm_change_m2m_value(self):
# make the m2m the confirmation_field
ShoppingMallAdmin.confirmation_fields = ["shops"]
shops = [ShopFactory() for i in range(3)]
shopping_mall = ShoppingMall.objects.create(name="name")
shopping_mall.shops.set(shops)
assert shopping_mall.shops.count() == 3
data = {"name": "name", "id": str(shopping_mall.id), "shops": ["1"]}
response = self.client.post(
f"/admin/market/shoppingmall/{shopping_mall.id}/change/", data
)
# Redirects to changelist
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, "/admin/market/shoppingmall/")
# Shop has changed
shopping_mall.refresh_from_db()
self.assertEqual(shopping_mall.shops.count(), 1)
def test_form_invalid_m2m_value(self):
ShoppingMallAdmin.confirmation_fields = ["shops"]
shopping_mall = ShoppingMall.objects.create(name="name")
data = {
"id": shopping_mall.id,
"name": "name",
"shops": [1, 2, 3], # These shops don't exist
"_confirm_change": True,
"csrfmiddlewaretoken": "fake token",
}
response = self.client.post(
f"/admin/market/shoppingmall/{shopping_mall.id}/change/", data
)
# Form invalid should show errors on form
self.assertEqual(response.status_code, 200)
self.assertIsNotNone(response.context_data.get("errors"))
self.assertTrue(
str(response.context_data["errors"][0][0]).startswith(
"Select a valid choice."
)
)
# Should not have updated inventory
shopping_mall.refresh_from_db()
self.assertEqual(shopping_mall.shops.count(), 0)
@@ -0,0 +1,477 @@
from unittest import mock
from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.cache import cache
from django.urls import reverse
from admin_confirm.tests.helpers import AdminConfirmTestCase
from tests.market.admin import ItemAdmin, ShoppingMallAdmin
from tests.market.models import GeneralManager, Item, ShoppingMall, Town
from tests.factories import ItemFactory, ShopFactory
from admin_confirm.constants import CACHE_KEYS, CONFIRMATION_RECEIVED
@mock.patch.object(ShoppingMallAdmin, "inlines", [])
class TestConfirmSaveActions(AdminConfirmTestCase):
def test_simple_add_with_save(self):
# Load the Add Item Page
ItemAdmin.confirm_add = True
response = self.client.get(reverse("admin:market_item_add"))
# Should be asked for confirmation
self.assertTrue(response.context_data.get("confirm_add"))
self.assertIn("_confirm_add", response.rendered_content)
# Click "Save"
data = {
"name": "name",
"price": 2.0,
"currency": Item.VALID_CURRENCIES[0][0],
"_confirm_add": True,
"_save": True,
}
response = self.client.post(reverse("admin:market_item_add"), data=data)
# Should be shown confirmation page
self._assertSubmitHtml(
rendered_content=response.rendered_content,
save_action="_save",
multipart_form=True,
)
# Should have cached the unsaved item
cached_item = cache.get(CACHE_KEYS["object"])
self.assertIsNotNone(cached_item)
self.assertIsNone(cached_item.id)
self.assertEqual(cached_item.name, data["name"])
self.assertEqual(cached_item.price, data["price"])
self.assertEqual(cached_item.currency, data["currency"])
# Should not have saved the item yet
self.assertEqual(Item.objects.count(), 0)
# Click "Yes, I'm Sure"
del data["_confirm_add"]
data[CONFIRMATION_RECEIVED] = True
response = self.client.post(reverse("admin:market_item_add"), data=data)
# Should have redirected to changelist
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, "/admin/market/item/")
# Should have saved item
self.assertEqual(Item.objects.count(), 1)
saved_item = Item.objects.all().first()
self.assertEqual(saved_item.name, data["name"])
self.assertEqual(saved_item.price, data["price"])
self.assertEqual(saved_item.currency, data["currency"])
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
def test_simple_change_with_continue(self):
item = ItemFactory(name="Not name")
# Load the Change Item Page
ItemAdmin.confirm_change = True
response = self.client.get(f"/admin/market/item/{item.id}/change/")
# Should be asked for confirmation
self.assertTrue(response.context_data.get("confirm_change"))
self.assertIn("_confirm_change", response.rendered_content)
# Click "Save And Continue"
data = {
"id": item.id,
"name": "name",
"price": 2.0,
"currency": Item.VALID_CURRENCIES[0][0],
"_confirm_change": True,
"_continue": True,
}
response = self.client.post(f"/admin/market/item/{item.id}/change/", data=data)
# Should be shown confirmation page
self._assertSubmitHtml(
rendered_content=response.rendered_content,
save_action="_continue",
multipart_form=True,
)
# Should have cached the unsaved item
cached_item = cache.get(CACHE_KEYS["object"])
self.assertIsNotNone(cached_item)
self.assertIsNone(cached_item.id)
self.assertEqual(cached_item.name, data["name"])
self.assertEqual(cached_item.price, data["price"])
self.assertEqual(cached_item.currency, data["currency"])
# Should not have saved the changes yet
self.assertEqual(Item.objects.count(), 1)
item.refresh_from_db()
self.assertEqual(item.name, "Not name")
# Click "Yes, I'm Sure"
del data["_confirm_change"]
data[CONFIRMATION_RECEIVED] = True
response = self.client.post(f"/admin/market/item/{item.id}/change/", data=data)
# Should not have redirected to changelist
self.assertEqual(response.url, f"/admin/market/item/{item.id}/change/")
# Should have saved item
self.assertEqual(Item.objects.count(), 1)
saved_item = Item.objects.all().first()
self.assertEqual(saved_item.name, data["name"])
self.assertEqual(saved_item.price, data["price"])
self.assertEqual(saved_item.currency, data["currency"])
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
def test_file_and_image_add_addanother(self):
# Load the Add Item Page
ItemAdmin.confirm_add = True
response = self.client.get(reverse("admin:market_item_add"))
# Should be asked for confirmation
self.assertTrue(response.context_data.get("confirm_add"))
self.assertIn("_confirm_add", response.rendered_content)
# Select files
image_path = "screenshot.png"
f = SimpleUploadedFile(
name="test_file.jpg",
content=open(image_path, "rb").read(),
content_type="image/jpeg",
)
i = SimpleUploadedFile(
name="test_image.jpg",
content=open(image_path, "rb").read(),
content_type="image/jpeg",
)
# Click "Save"
data = {
"name": "name",
"price": 2.0,
"currency": Item.VALID_CURRENCIES[0][0],
"file": f,
"image": i,
"_confirm_add": True,
"_addanother": True,
}
response = self.client.post(reverse("admin:market_item_add"), data=data)
# Should be shown confirmation page
self._assertSubmitHtml(
rendered_content=response.rendered_content,
save_action="_addanother",
multipart_form=True,
)
# Should have cached the unsaved item
cached_item = cache.get(CACHE_KEYS["object"])
self.assertIsNotNone(cached_item)
self.assertIsNone(cached_item.id)
self.assertEqual(cached_item.name, data["name"])
self.assertEqual(cached_item.price, data["price"])
self.assertEqual(cached_item.currency, data["currency"])
self.assertEqual(cached_item.file, data["file"])
self.assertEqual(cached_item.image, data["image"])
# Should not have saved the item yet
self.assertEqual(Item.objects.count(), 0)
# Click "Yes, I'm Sure"
confirmation_data = data.copy()
del confirmation_data["_confirm_add"]
del confirmation_data["image"]
del confirmation_data["file"]
confirmation_data[CONFIRMATION_RECEIVED] = True
response = self.client.post(
reverse("admin:market_item_add"), data=confirmation_data
)
# Should have redirected to changelist
self.assertEqual(response.status_code, 302)
# Should show add page since "add another" was selected
self.assertEqual(response.url, "/admin/market/item/add/")
# Should have saved item
self.assertEqual(Item.objects.count(), 1)
saved_item = Item.objects.all().first()
self.assertEqual(saved_item.name, data["name"])
self.assertEqual(saved_item.price, data["price"])
self.assertEqual(saved_item.currency, data["currency"])
self.assertEqual(saved_item.file, data["file"])
self.assertEqual(saved_item.image, data["image"])
self.assertEqual(saved_item.file.name, "test_file.jpg")
self.assertEqual(saved_item.image.name, "test_image.jpg")
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
def test_file_and_image_change_with_saveasnew(self):
item = ItemFactory(name="Not name")
# Select files
image_path = "screenshot.png"
f = SimpleUploadedFile(
name="test_file.jpg",
content=open(image_path, "rb").read(),
content_type="image/jpeg",
)
i = SimpleUploadedFile(
name="test_image.jpg",
content=open(image_path, "rb").read(),
content_type="image/jpeg",
)
item.file = f
item.image = i
item.save()
# Load the Change Item Page
ItemAdmin.confirm_change = True
ItemAdmin.fields = ["name", "price", "file", "image", "currency"]
ItemAdmin.save_as = True
ItemAdmin.save_as_continue = True
response = self.client.get(f"/admin/market/item/{item.id}/change/")
# Should be asked for confirmation
self.assertTrue(response.context_data.get("confirm_change"))
self.assertIn("_confirm_change", response.rendered_content)
# Upload new image and remove file
i2 = SimpleUploadedFile(
name="test_image2.jpg",
content=open(image_path, "rb").read(),
content_type="image/jpeg",
)
# Click "Save And Continue"
data = {
"id": item.id,
"name": "name",
"price": 2.0,
"image": i2,
"file": "",
"file-clear": "on",
"currency": Item.VALID_CURRENCIES[0][0],
"_confirm_change": True,
"_saveasnew": True,
}
response = self.client.post(f"/admin/market/item/{item.id}/change/", data=data)
# Should be shown confirmation page
self._assertSubmitHtml(
rendered_content=response.rendered_content,
save_action="_saveasnew",
multipart_form=True,
)
# Should have cached the unsaved item
cached_item = cache.get(CACHE_KEYS["object"])
self.assertIsNotNone(cached_item)
self.assertIsNone(cached_item.id)
self.assertEqual(cached_item.name, data["name"])
self.assertEqual(cached_item.price, data["price"])
self.assertEqual(cached_item.currency, data["currency"])
self.assertFalse(cached_item.file.name)
self.assertEqual(cached_item.image, i2)
# Should not have saved the changes yet
self.assertEqual(Item.objects.count(), 1)
item.refresh_from_db()
self.assertEqual(item.name, "Not name")
self.assertIsNotNone(item.file)
self.assertIsNotNone(item.image)
# Click "Yes, I'm Sure"
del data["_confirm_change"]
data["image"] = ""
data[CONFIRMATION_RECEIVED] = True
response = self.client.post(f"/admin/market/item/{item.id}/change/", data=data)
# Should not have redirected to changelist
self.assertEqual(response.url, f"/admin/market/item/{item.id + 1}/change/")
# Should not have changed existing item
item.refresh_from_db()
self.assertEqual(item.name, "Not name")
self.assertEqual(item.file.name.count("test_file"), 1)
self.assertEqual(item.image.name.count("test_image2"), 0)
self.assertEqual(item.image.name.count("test_image"), 1)
# Should have saved new item
self.assertEqual(Item.objects.count(), 2)
new_item = Item.objects.filter(id=item.id + 1).first()
self.assertIsNotNone(new_item)
self.assertEqual(new_item.name, data["name"])
self.assertEqual(new_item.price, data["price"])
self.assertEqual(new_item.currency, data["currency"])
self.assertFalse(new_item.file)
self.assertEqual(new_item.image, i2)
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
def test_relations_add(self):
gm = GeneralManager.objects.create(name="gm")
shops = [ShopFactory() for i in range(3)]
town = Town.objects.create(name="town")
# Load the Add ShoppingMall Page
ShoppingMallAdmin.confirm_add = True
response = self.client.get(reverse("admin:market_shoppingmall_add"))
# Should be asked for confirmation
self.assertTrue(response.context_data.get("confirm_add"))
self.assertIn("_confirm_add", response.rendered_content)
# Click "Save"
data = {
"name": "name",
"shops": [s.id for s in shops],
"general_manager": gm.id,
"town": town.id,
"_confirm_add": True,
"_save": True,
}
response = self.client.post(reverse("admin:market_shoppingmall_add"), data=data)
# Should be shown confirmation page
self._assertManyToManyFormHtml(
rendered_content=response.rendered_content,
options=shops,
selected_ids=data["shops"],
)
self._assertSubmitHtml(
rendered_content=response.rendered_content, save_action="_save"
)
# Should not have cached the unsaved object
cached_item = cache.get(CACHE_KEYS["object"])
self.assertIsNone(cached_item)
# Click "Yes, I'm Sure"
confirmation_received_data = data
del confirmation_received_data["_confirm_add"]
confirmation_received_data[CONFIRMATION_RECEIVED] = True
response = self.client.post(
reverse("admin:market_shoppingmall_add"), data=confirmation_received_data
)
# Should have redirected to changelist
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, "/admin/market/shoppingmall/")
# Should have saved object
self.assertEqual(ShoppingMall.objects.count(), 1)
saved_item = ShoppingMall.objects.all().first()
self.assertEqual(saved_item.name, data["name"])
self.assertEqual(saved_item.general_manager, gm)
self.assertEqual(saved_item.town, town)
for shop in saved_item.shops.all():
self.assertIn(shop, shops)
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
def test_relation_change_with_saveasnew(self):
gm = GeneralManager.objects.create(name="gm")
shops = [ShopFactory() for i in range(3)]
town = Town.objects.create(name="town")
mall = ShoppingMall.objects.create(name="mall", general_manager=gm, town=town)
mall.shops.set(shops)
# new values
gm2 = GeneralManager.objects.create(name="gm2")
shops2 = [ShopFactory() for i in range(3)]
town2 = Town.objects.create(name="town2")
# Load the Change ShoppingMall Page
ShoppingMallAdmin.confirm_change = True
response = self.client.get(f"/admin/market/shoppingmall/{mall.id}/change/")
# Should be asked for confirmation
self.assertTrue(response.context_data.get("confirm_change"))
self.assertIn("_confirm_change", response.rendered_content)
# Click "Save"
data = {
"id": mall.id,
"name": "name",
"shops": [s.id for s in shops2],
"general_manager": gm2.id,
"town": town2.id,
"_confirm_change": True,
"_saveasnew": True,
}
response = self.client.post(
f"/admin/market/shoppingmall/{mall.id}/change/", data=data
)
# Should be shown confirmation page
self._assertManyToManyFormHtml(
rendered_content=response.rendered_content,
options=shops,
selected_ids=data["shops"],
)
self._assertSubmitHtml(
rendered_content=response.rendered_content, save_action="_saveasnew"
)
# Should not have cached the unsaved obj
cached_item = cache.get(CACHE_KEYS["object"])
self.assertIsNone(cached_item)
# Should not have saved changes yet
self.assertEqual(ShoppingMall.objects.count(), 1)
mall.refresh_from_db()
self.assertEqual(mall.name, "mall")
self.assertEqual(mall.general_manager, gm)
self.assertEqual(mall.town, town)
for shop in mall.shops.all():
self.assertIn(shop, shops)
# Click "Yes, I'm Sure"
confirmation_received_data = data.copy()
del confirmation_received_data["_confirm_change"]
response = self.client.post(
f"/admin/market/shoppingmall/{mall.id}/change/",
data=confirmation_received_data,
)
# Should not have redirected to changelist
self.assertEqual(
response.url, f"/admin/market/shoppingmall/{mall.id + 1}/change/"
)
# Should have saved obj
self.assertEqual(ShoppingMall.objects.count(), 2)
# Should not have changed old obj
mall.refresh_from_db()
self.assertEqual(mall.name, "mall")
self.assertEqual(mall.general_manager, gm)
self.assertEqual(mall.town, town)
for shop in mall.shops.all():
self.assertIn(shop, shops)
# Should have created new obj
saved_item = ShoppingMall.objects.filter(id=mall.id + 1).first()
self.assertEqual(saved_item.name, data["name"])
self.assertEqual(saved_item.general_manager, gm2)
self.assertEqual(saved_item.town, town2)
for shop in saved_item.shops.all():
self.assertIn(shop, shops2)
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
@@ -0,0 +1,458 @@
from unittest import mock
from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.cache import cache
from django.urls import reverse
from admin_confirm.tests.helpers import AdminConfirmTestCase
from tests.market.admin import ItemAdmin, ShoppingMallAdmin
from tests.market.models import GeneralManager, Item, ShoppingMall, Town
from tests.factories import ItemFactory, ShopFactory
from admin_confirm.constants import CACHE_KEYS, CONFIRMATION_RECEIVED
@mock.patch.object(ShoppingMallAdmin, "inlines", [])
class TestConfirmationCache(AdminConfirmTestCase):
def test_simple_add(self):
# Load the Add Item Page
ItemAdmin.confirm_add = True
response = self.client.get(reverse("admin:market_item_add"))
# Should be asked for confirmation
self.assertTrue(response.context_data.get("confirm_add"))
self.assertIn("_confirm_add", response.rendered_content)
# Click "Save"
data = {
"name": "name",
"price": 2.0,
"currency": Item.VALID_CURRENCIES[0][0],
"_confirm_add": True,
"_save": True,
}
response = self.client.post(reverse("admin:market_item_add"), data=data)
# Should be shown confirmation page
self._assertSubmitHtml(
rendered_content=response.rendered_content,
save_action="_save",
multipart_form=True,
)
# Should have cached the unsaved item
cached_item = cache.get(CACHE_KEYS["object"])
self.assertIsNotNone(cached_item)
self.assertIsNone(cached_item.id)
self.assertEqual(cached_item.name, data["name"])
self.assertEqual(cached_item.price, data["price"])
self.assertEqual(cached_item.currency, data["currency"])
# Should not have saved the item yet
self.assertEqual(Item.objects.count(), 0)
# Click "Yes, I'm Sure"
del data["_confirm_add"]
data[CONFIRMATION_RECEIVED] = True
response = self.client.post(reverse("admin:market_item_add"), data=data)
# Should have redirected to changelist
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, "/admin/market/item/")
# Should have saved item
self.assertEqual(Item.objects.count(), 1)
saved_item = Item.objects.all().first()
self.assertEqual(saved_item.name, data["name"])
self.assertEqual(saved_item.price, data["price"])
self.assertEqual(saved_item.currency, data["currency"])
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
def test_simple_change(self):
item = ItemFactory(name="Not name")
# Load the Change Item Page
ItemAdmin.confirm_change = True
response = self.client.get(f"/admin/market/item/{item.id}/change/")
# Should be asked for confirmation
self.assertTrue(response.context_data.get("confirm_change"))
self.assertIn("_confirm_change", response.rendered_content)
# Click "Save And Continue"
data = {
"id": item.id,
"name": "name",
"price": 2.0,
"currency": Item.VALID_CURRENCIES[0][0],
"_confirm_change": True,
"_continue": True,
}
response = self.client.post(f"/admin/market/item/{item.id}/change/", data=data)
# Should be shown confirmation page
self._assertSubmitHtml(
rendered_content=response.rendered_content,
save_action="_continue",
multipart_form=True,
)
# Should have cached the unsaved item
cached_item = cache.get(CACHE_KEYS["object"])
self.assertIsNotNone(cached_item)
self.assertIsNone(cached_item.id)
self.assertEqual(cached_item.name, data["name"])
self.assertEqual(cached_item.price, data["price"])
self.assertEqual(cached_item.currency, data["currency"])
# Should not have saved the changes yet
self.assertEqual(Item.objects.count(), 1)
item.refresh_from_db()
self.assertEqual(item.name, "Not name")
# Click "Yes, I'm Sure"
del data["_confirm_change"]
data[CONFIRMATION_RECEIVED] = True
response = self.client.post(f"/admin/market/item/{item.id}/change/", data=data)
# Should not have redirected to changelist
self.assertEqual(response.url, f"/admin/market/item/{item.id}/change/")
# Should have saved item
self.assertEqual(Item.objects.count(), 1)
saved_item = Item.objects.all().first()
self.assertEqual(saved_item.name, data["name"])
self.assertEqual(saved_item.price, data["price"])
self.assertEqual(saved_item.currency, data["currency"])
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
def test_file_and_image_add(self):
# Load the Add Item Page
ItemAdmin.confirm_add = True
response = self.client.get(reverse("admin:market_item_add"))
# Should be asked for confirmation
self.assertTrue(response.context_data.get("confirm_add"))
self.assertIn("_confirm_add", response.rendered_content)
# Select files
image_path = "screenshot.png"
f = SimpleUploadedFile(
name="test_file.jpg",
content=open(image_path, "rb").read(),
content_type="image/jpeg",
)
i = SimpleUploadedFile(
name="test_image.jpg",
content=open(image_path, "rb").read(),
content_type="image/jpeg",
)
# Click "Save"
data = {
"name": "name",
"price": 2.0,
"currency": Item.VALID_CURRENCIES[0][0],
"file": f,
"image": i,
"_confirm_add": True,
"_save": True,
}
response = self.client.post(reverse("admin:market_item_add"), data=data)
# Should be shown confirmation page
self._assertSubmitHtml(
rendered_content=response.rendered_content,
save_action="_save",
multipart_form=True,
)
# Should have cached the unsaved item
cached_item = cache.get(CACHE_KEYS["object"])
self.assertIsNotNone(cached_item)
self.assertIsNone(cached_item.id)
self.assertEqual(cached_item.name, data["name"])
self.assertEqual(cached_item.price, data["price"])
self.assertEqual(cached_item.currency, data["currency"])
self.assertEqual(cached_item.file, data["file"])
self.assertEqual(cached_item.image, data["image"])
# Should not have saved the item yet
self.assertEqual(Item.objects.count(), 0)
# Click "Yes, I'm Sure"
confirmation_data = data.copy()
del confirmation_data["_confirm_add"]
del confirmation_data["image"]
del confirmation_data["file"]
confirmation_data[CONFIRMATION_RECEIVED] = True
response = self.client.post(
reverse("admin:market_item_add"), data=confirmation_data
)
# Should have redirected to changelist
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, "/admin/market/item/")
# Should have saved item
self.assertEqual(Item.objects.count(), 1)
saved_item = Item.objects.all().first()
self.assertEqual(saved_item.name, data["name"])
self.assertEqual(saved_item.price, data["price"])
self.assertEqual(saved_item.currency, data["currency"])
self.assertEqual(saved_item.file, data["file"])
self.assertEqual(saved_item.image, data["image"])
self.assertEqual(saved_item.file.name, "test_file.jpg")
self.assertEqual(saved_item.image.name, "test_image.jpg")
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
def test_file_and_image_change(self):
item = ItemFactory(name="Not name")
# Select files
image_path = "screenshot.png"
f = SimpleUploadedFile(
name="test_file.jpg",
content=open(image_path, "rb").read(),
content_type="image/jpeg",
)
i = SimpleUploadedFile(
name="test_image.jpg",
content=open(image_path, "rb").read(),
content_type="image/jpeg",
)
item.file = f
item.image = i
item.save()
# Load the Change Item Page
ItemAdmin.confirm_change = True
ItemAdmin.fields = ["name", "price", "file", "image", "currency"]
response = self.client.get(f"/admin/market/item/{item.id}/change/")
# Should be asked for confirmation
self.assertTrue(response.context_data.get("confirm_change"))
self.assertIn("_confirm_change", response.rendered_content)
# Upload new image and remove file
i2 = SimpleUploadedFile(
name="test_image2.jpg",
content=open(image_path, "rb").read(),
content_type="image/jpeg",
)
# Click "Save And Continue"
data = {
"id": item.id,
"name": "name",
"price": 2.0,
"image": i2,
"file": "",
"file-clear": "on",
"currency": Item.VALID_CURRENCIES[0][0],
"_confirm_change": True,
"_continue": True,
}
response = self.client.post(f"/admin/market/item/{item.id}/change/", data=data)
# Should be shown confirmation page
self._assertSubmitHtml(
rendered_content=response.rendered_content,
save_action="_continue",
multipart_form=True,
)
# Should have cached the unsaved item
cached_item = cache.get(CACHE_KEYS["object"])
self.assertIsNotNone(cached_item)
self.assertIsNone(cached_item.id)
self.assertEqual(cached_item.name, data["name"])
self.assertEqual(cached_item.price, data["price"])
self.assertEqual(cached_item.currency, data["currency"])
self.assertFalse(cached_item.file.name)
self.assertEqual(cached_item.image, i2)
# Should not have saved the changes yet
self.assertEqual(Item.objects.count(), 1)
item.refresh_from_db()
self.assertEqual(item.name, "Not name")
self.assertIsNotNone(item.file)
self.assertIsNotNone(item.image)
# Click "Yes, I'm Sure"
del data["_confirm_change"]
data["image"] = ""
data[CONFIRMATION_RECEIVED] = True
response = self.client.post(f"/admin/market/item/{item.id}/change/", data=data)
# Should not have redirected to changelist
self.assertEqual(response.url, f"/admin/market/item/{item.id}/change/")
# Should have saved item
self.assertEqual(Item.objects.count(), 1)
saved_item = Item.objects.all().first()
self.assertEqual(saved_item.name, data["name"])
self.assertEqual(saved_item.price, data["price"])
self.assertEqual(saved_item.currency, data["currency"])
self.assertFalse(saved_item.file)
self.assertEqual(saved_item.image, i2)
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
def test_relations_add(self):
gm = GeneralManager.objects.create(name="gm")
shops = [ShopFactory() for i in range(3)]
town = Town.objects.create(name="town")
# Load the Add ShoppingMall Page
ShoppingMallAdmin.confirm_add = True
response = self.client.get(reverse("admin:market_shoppingmall_add"))
# Should be asked for confirmation
self.assertTrue(response.context_data.get("confirm_add"))
self.assertIn("_confirm_add", response.rendered_content)
# Click "Save"
data = {
"name": "name",
"shops": [s.id for s in shops],
"general_manager": gm.id,
"town": town.id,
"_confirm_add": True,
"_save": True,
}
response = self.client.post(reverse("admin:market_shoppingmall_add"), data=data)
# Should be shown confirmation page
self._assertManyToManyFormHtml(
rendered_content=response.rendered_content,
options=shops,
selected_ids=data["shops"],
)
self._assertSubmitHtml(
rendered_content=response.rendered_content, save_action="_save"
)
# Should not have cached the unsaved object
cached_item = cache.get(CACHE_KEYS["object"])
self.assertIsNone(cached_item)
# Should not have saved the object yet
self.assertEqual(ShoppingMall.objects.count(), 0)
# Click "Yes, I'm Sure"
confirmation_received_data = data
del confirmation_received_data["_confirm_add"]
response = self.client.post(
reverse("admin:market_shoppingmall_add"), data=confirmation_received_data
)
# Should have redirected to changelist
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, "/admin/market/shoppingmall/")
# Should have saved object
self.assertEqual(ShoppingMall.objects.count(), 1)
saved_item = ShoppingMall.objects.all().first()
self.assertEqual(saved_item.name, data["name"])
self.assertEqual(saved_item.general_manager, gm)
self.assertEqual(saved_item.town, town)
for shop in saved_item.shops.all():
self.assertIn(shop, shops)
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
def test_relation_change(self):
gm = GeneralManager.objects.create(name="gm")
shops = [ShopFactory() for i in range(3)]
town = Town.objects.create(name="town")
mall = ShoppingMall.objects.create(name="mall", general_manager=gm, town=town)
mall.shops.set(shops)
# new values
gm2 = GeneralManager.objects.create(name="gm2")
shops2 = [ShopFactory() for i in range(3)]
town2 = Town.objects.create(name="town2")
# Load the Change ShoppingMall Page
ShoppingMallAdmin.confirm_change = True
response = self.client.get(f"/admin/market/shoppingmall/{mall.id}/change/")
# Should be asked for confirmation
self.assertTrue(response.context_data.get("confirm_change"))
self.assertIn("_confirm_change", response.rendered_content)
# Click "Save"
data = {
"id": mall.id,
"name": "name",
"shops": [s.id for s in shops2],
"general_manager": gm2.id,
"town": town2.id,
"_confirm_change": True,
"_continue": True,
}
response = self.client.post(
f"/admin/market/shoppingmall/{mall.id}/change/", data=data
)
# Should be shown confirmation page
self._assertManyToManyFormHtml(
rendered_content=response.rendered_content,
options=shops,
selected_ids=data["shops"],
)
self._assertSubmitHtml(
rendered_content=response.rendered_content, save_action="_continue"
)
# Should not have cached the unsaved obj
cached_item = cache.get(CACHE_KEYS["object"])
self.assertIsNone(cached_item)
# Should not have saved changes yet
self.assertEqual(ShoppingMall.objects.count(), 1)
mall.refresh_from_db()
self.assertEqual(mall.name, "mall")
self.assertEqual(mall.general_manager, gm)
self.assertEqual(mall.town, town)
for shop in mall.shops.all():
self.assertIn(shop, shops)
# Click "Yes, I'm Sure"
confirmation_received_data = data
del confirmation_received_data["_confirm_change"]
confirmation_received_data[CONFIRMATION_RECEIVED] = True
response = self.client.post(
f"/admin/market/shoppingmall/{mall.id}/change/",
data=confirmation_received_data,
)
# Should not have redirected to changelist
self.assertEqual(response.url, f"/admin/market/shoppingmall/{mall.id}/change/")
# Should have saved obj
self.assertEqual(ShoppingMall.objects.count(), 1)
saved_item = ShoppingMall.objects.all().first()
self.assertEqual(saved_item.name, data["name"])
self.assertEqual(saved_item.general_manager, gm2)
self.assertEqual(saved_item.town, town2)
for shop in saved_item.shops.all():
self.assertIn(shop, shops2)
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
+169
View File
@@ -0,0 +1,169 @@
"""
Tests ModelAdmin with fieldsets custom configured through one of the possible methods
Ensures that AdminConfirmMixin works correctly when implimenting class alters default fieldsets
Test Matrix
method: `.fieldsets =`, `def get_fieldsets()`
action: change, add
fieldset: simple, with readonly fields, with custom fields
"""
import pytest
from importlib import reload
from tests.market.admin import item_admin
from django.contrib.auth.models import User
from django.contrib.admin import AdminSite
from django.test.client import RequestFactory
from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.cache import cache
from admin_confirm.constants import CACHE_KEYS, CONFIRM_CHANGE, CONFIRMATION_RECEIVED
from tests.market.models import Item
from tests.factories import ItemFactory
def fs_simple(admin):
return (
(None, {"fields": ("name", "price", "image")}),
(
"Advanced options",
{
"classes": ("collapse",),
"fields": ("currency", "file"),
},
),
)
def fs_w_readonly(admin):
admin.readonly_fields = ["description", "image"]
return fs_simple(admin)
def fs_w_custom(admin):
admin.one = lambda self, obj: "ReadOnly"
admin.two = lambda self, obj: "ReadOnly"
admin.three = lambda self, obj: "ReadOnly"
admin.readonly_fields = ["one", "two", "three"]
return (
(None, {"fields": ("name", "price", "image", "one")}),
(
"Advanced options",
{
"classes": ("collapse",),
"fields": ("currency", "two", "file"),
},
),
("More Info", {"fields": ("three", "description")}),
)
def set_fieldsets(admin, fieldset):
admin.fieldsets = fieldset
def override_get_fieldsets(admin, fieldset):
admin.get_fieldsets = lambda self, request, obj=None: fieldset
methods = [set_fieldsets, override_get_fieldsets]
actions = ["_confirm_add", "_confirm_change"]
fieldsets = [fs_simple, fs_w_readonly, fs_w_custom]
param_matrix = []
for method in methods:
for fieldset in fieldsets:
for action in actions:
param_matrix.append((method, fieldset, action))
@pytest.mark.django_db()
@pytest.mark.parametrize("method,get_fieldset,action", param_matrix)
def test_fieldsets(client, method, get_fieldset, action):
reload(item_admin)
admin = item_admin.ItemAdmin
fs = get_fieldset(admin)
# set fieldsets via one of the methods
method(admin, fs)
admin_instance = admin(admin_site=AdminSite(), model=Item)
request = RequestFactory().request
assert admin_instance.get_fieldsets(request) == fs
user = User.objects.create_superuser(
username="super", email="super@email.org", password="pass"
)
client.force_login(user)
url = "/admin/market/item/add/"
image_path = "screenshot.png"
f2 = SimpleUploadedFile(
name="new_file.jpg",
content=open(image_path, "rb").read(),
content_type="image/jpeg",
)
i2 = SimpleUploadedFile(
name="new_image.jpg",
content=open(image_path, "rb").read(),
content_type="image/jpeg",
)
data = {
"name": "new name",
"price": 2,
"currency": "USD",
"image": i2,
"file": f2,
action: True,
"_save": True,
}
for f in admin.readonly_fields:
if f in data.keys():
del data[f]
if action == CONFIRM_CHANGE:
url = "/admin/market/item/1/change/"
f = SimpleUploadedFile(
name="old_file.jpg",
content=open(image_path, "rb").read(),
content_type="image/jpeg",
)
i = SimpleUploadedFile(
name="old_image.jpg",
content=open(image_path, "rb").read(),
content_type="image/jpeg",
)
item = ItemFactory(name="old name", price=1, currency="CAD", file=f, image=i)
data["id"] = item.id
cache_item = Item()
for f in ["name", "price", "currency", "image", "file"]:
if f not in admin.readonly_fields:
setattr(cache_item, f, data[f])
cache.set(CACHE_KEYS["object"], cache_item)
cache.set(CACHE_KEYS["post"], data)
# Click "Yes, I'm Sure"
del data[action]
data[CONFIRMATION_RECEIVED] = True
response = client.post(url, data=data)
# Should have redirected to changelist
# assert response.status_code == 302
assert response.url == "/admin/market/item/"
# Should have saved item
assert Item.objects.count() == 1
saved_item = Item.objects.all().first()
for f in ["name", "price", "currency"]:
if f not in admin.readonly_fields:
assert getattr(saved_item, f) == data[f]
if "file" not in admin.readonly_fields:
assert "new_file" in saved_item.file.name
if "image" not in admin.readonly_fields:
assert "new_image" in saved_item.image.name
reload(item_admin)
+967
View File
@@ -0,0 +1,967 @@
"""
Ensure that files are saved during confirmation
Without file changes, Django is relied on
With file changes, we cache the object, save it with
the files if new, or add files to existing obj and save
Then send the rest of the changes to Django to handle
This is arguably the most we fiddle with the Django request
Thus we should test it extensively
"""
import time
from unittest import mock
from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.cache import cache
from admin_confirm.tests.helpers import AdminConfirmTestCase
from admin_confirm.constants import CACHE_KEYS, CONFIRMATION_RECEIVED
from tests.market.admin import ItemAdmin
from tests.market.models import Item, Shop
from tests.factories import ItemFactory, ShopFactory
class TestFileCache(AdminConfirmTestCase):
def setUp(self):
# Load the Change Item Page
ItemAdmin.confirm_change = True
ItemAdmin.fields = ["name", "price", "file", "image", "currency"]
ItemAdmin.save_as = True
ItemAdmin.save_as_continue = True
self.image_path = "screenshot.png"
f = SimpleUploadedFile(
name="test_file.jpg",
content=open(self.image_path, "rb").read(),
content_type="image/jpeg",
)
i = SimpleUploadedFile(
name="test_image.jpg",
content=open(self.image_path, "rb").read(),
content_type="image/jpeg",
)
self.item = ItemFactory(name="Not name", file=f, image=i)
return super().setUp()
def test_save_as_continue_true_should_not_redirect_to_changelist(self):
item = self.item
# Load the Change Item Page
ItemAdmin.save_as_continue = True
# Upload new image and remove file
i2 = SimpleUploadedFile(
name="test_image2.jpg",
content=open(self.image_path, "rb").read(),
content_type="image/jpeg",
)
# Request.POST
data = {
"id": item.id,
"name": "name",
"price": 2.0,
"file": "",
"file-clear": "on",
"currency": Item.VALID_CURRENCIES[0][0],
"_confirm_change": True,
"_saveasnew": True,
}
# Set cache
cache_item = Item(
name=data["name"],
price=data["price"],
currency=data["currency"],
image=i2,
)
cache.set(CACHE_KEYS["object"], cache_item)
cache.set(CACHE_KEYS["post"], data)
# Click "Yes, I'm Sure"
del data["_confirm_change"]
data[CONFIRMATION_RECEIVED] = True
with mock.patch.object(ItemAdmin, "message_user") as message_user:
response = self.client.post(
f"/admin/market/item/{self.item.id}/change/", data=data
)
# Should show message to user with correct obj and path
message_user.assert_called_once()
message = message_user.call_args[0][1]
self.assertIn("/admin/market/item/2/change/", message)
self.assertIn(data["name"], message)
self.assertIn("You may edit it again below.", message)
# Should not have redirected to changelist
self.assertEqual(response.url, f"/admin/market/item/{self.item.id + 1}/change/")
# Should not have changed existing item
item.refresh_from_db()
self.assertEqual(item.name, "Not name")
self.assertEqual(item.file.name.count("test_file"), 1)
self.assertEqual(item.image.name.count("test_image2"), 0)
self.assertEqual(item.image.name.count("test_image"), 1)
# Should have saved new item
self.assertEqual(Item.objects.count(), 2)
new_item = Item.objects.filter(id=item.id + 1).first()
self.assertIsNotNone(new_item)
self.assertEqual(new_item.name, data["name"])
self.assertEqual(new_item.price, data["price"])
self.assertEqual(new_item.currency, data["currency"])
self.assertFalse(new_item.file)
self.assertEqual(new_item.image.name.count("test_image2"), 1)
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
def test_save_as_continue_false_should_redirect_to_changelist(self):
item = self.item
# Load the Change Item Page
ItemAdmin.save_as_continue = False
# Upload new image and remove file
i2 = SimpleUploadedFile(
name="test_image2.jpg",
content=open(self.image_path, "rb").read(),
content_type="image/jpeg",
)
# Request.POST
data = {
"id": item.id,
"name": "name",
"price": 2.0,
"file": "",
"file-clear": "on",
"currency": Item.VALID_CURRENCIES[0][0],
"_confirm_change": True,
"_saveasnew": True,
}
# Set cache
cache_item = Item(
name=data["name"],
price=data["price"],
currency=data["currency"],
image=i2,
)
cache.set(CACHE_KEYS["object"], cache_item)
cache.set(CACHE_KEYS["post"], data)
# Click "Yes, I'm Sure"
del data["_confirm_change"]
data[CONFIRMATION_RECEIVED] = True
with mock.patch.object(ItemAdmin, "message_user") as message_user:
response = self.client.post(
f"/admin/market/item/{self.item.id}/change/", data=data
)
# Should show message to user with correct obj and path
message_user.assert_called_once()
message = message_user.call_args[0][1]
self.assertIn("/admin/market/item/2/change/", message)
self.assertIn(data["name"], message)
self.assertNotIn("You may edit it again below.", message)
# Should have redirected to changelist
self.assertEqual(response.url, f"/admin/market/item/")
# Should not have changed existing item
item.refresh_from_db()
self.assertEqual(item.name, "Not name")
self.assertEqual(item.file.name.count("test_file"), 1)
self.assertEqual(item.image.name.count("test_image2"), 0)
self.assertEqual(item.image.name.count("test_image"), 1)
# Should have saved new item
self.assertEqual(Item.objects.count(), 2)
new_item = Item.objects.filter(id=item.id + 1).first()
self.assertIsNotNone(new_item)
self.assertEqual(new_item.name, data["name"])
self.assertEqual(new_item.price, data["price"])
self.assertEqual(new_item.currency, data["currency"])
self.assertFalse(new_item.file)
self.assertEqual(new_item.image.name.count("test_image2"), 1)
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
def test_saveasnew_without_any_file_changes_should_save_new_instance_without_files(
self,
):
item = self.item
# Request.POST
data = {
"id": item.id,
"name": "name",
"price": 2.0,
"file": "",
"image": "",
"currency": Item.VALID_CURRENCIES[0][0],
"_confirm_change": True,
"_saveasnew": True,
}
# Set cache
cache_item = Item(
name=data["name"],
price=data["price"],
currency=data["currency"],
)
cache.set(CACHE_KEYS["object"], cache_item)
cache.set(CACHE_KEYS["post"], data)
# Click "Yes, I'm Sure"
del data["_confirm_change"]
data[CONFIRMATION_RECEIVED] = True
with mock.patch.object(ItemAdmin, "message_user") as message_user:
response = self.client.post(
f"/admin/market/item/{self.item.id}/change/", data=data
)
# Should show message to user with correct obj and path
message_user.assert_called_once()
message = message_user.call_args[0][1]
self.assertIn("/admin/market/item/2/change/", message)
self.assertIn(data["name"], message)
self.assertIn("You may edit it again below.", message)
# Should not have redirected to changelist
self.assertEqual(response.url, f"/admin/market/item/{self.item.id + 1}/change/")
# Should not have changed existing item
item.refresh_from_db()
self.assertEqual(item.name, "Not name")
self.assertEqual(item.file.name.count("test_file"), 1)
self.assertEqual(item.image.name.count("test_image2"), 0)
self.assertEqual(item.image.name.count("test_image"), 1)
# Should have saved new item
self.assertEqual(Item.objects.count(), 2)
new_item = Item.objects.filter(id=item.id + 1).first()
self.assertIsNotNone(new_item)
self.assertEqual(new_item.name, data["name"])
self.assertEqual(new_item.price, data["price"])
self.assertEqual(new_item.currency, data["currency"])
# In Django (by default), the save as new does not transfer over the files
self.assertFalse(new_item.file)
self.assertFalse(new_item.image)
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
def test_add_with_upload_file_should_save_new_instance_with_files(self):
# Request.POST
data = {
"name": "name",
"price": 2.0,
"image": "",
"currency": Item.VALID_CURRENCIES[0][0],
"_confirm_add": True,
"_save": True,
}
# Upload new file
f2 = SimpleUploadedFile(
name="test_file2.jpg",
content=open(self.image_path, "rb").read(),
content_type="image/jpeg",
)
# Set cache
cache_item = Item(
name=data["name"], price=data["price"], currency=data["currency"], file=f2
)
cache.set(CACHE_KEYS["object"], cache_item)
cache.set(CACHE_KEYS["post"], data)
# Click "Yes, I'm Sure"
del data["_confirm_add"]
data[CONFIRMATION_RECEIVED] = True
with mock.patch.object(ItemAdmin, "message_user") as message_user:
response = self.client.post(f"/admin/market/item/add/", data=data)
# Should show message to user with correct obj and path
message_user.assert_called_once()
message = message_user.call_args[0][1]
self.assertIn("/admin/market/item/2/change/", message)
self.assertIn(data["name"], message)
self.assertNotIn("You may edit it again below.", message)
# Should not have redirected to changelist
self.assertEqual(response.url, f"/admin/market/item/")
# Should not have changed existing item
self.item.refresh_from_db()
self.assertEqual(self.item.name, "Not name")
self.assertEqual(self.item.file.name.count("test_file"), 1)
self.assertEqual(self.item.image.name.count("test_image2"), 0)
self.assertEqual(self.item.image.name.count("test_image"), 1)
# Should have saved new item
self.assertEqual(Item.objects.count(), 2)
new_item = Item.objects.filter(id=self.item.id + 1).first()
self.assertIsNotNone(new_item)
self.assertEqual(new_item.name, data["name"])
self.assertEqual(new_item.price, data["price"])
self.assertEqual(new_item.currency, data["currency"])
self.assertIn("test_file2", new_item.file.name)
self.assertFalse(new_item.image)
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
def test_add_without_cached_post_should_save_new_instance_with_file(self):
# Request.POST
data = {
"name": "name",
"price": 2.0,
"image": "",
"currency": Item.VALID_CURRENCIES[0][0],
"_confirm_add": True,
"_save": True,
}
# Upload new file
f2 = SimpleUploadedFile(
name="test_file2.jpg",
content=open(self.image_path, "rb").read(),
content_type="image/jpeg",
)
# Set cache
cache_item = Item(
name=data["name"], price=data["price"], currency=data["currency"], file=f2
)
cache.set(CACHE_KEYS["object"], cache_item)
# Make sure there's no post cached post
cache.delete(CACHE_KEYS["post"])
# Click "Yes, I'm Sure"
del data["_confirm_add"]
data[CONFIRMATION_RECEIVED] = True
with mock.patch.object(ItemAdmin, "message_user") as message_user:
response = self.client.post(f"/admin/market/item/add/", data=data)
# Should show message to user with correct obj and path
message_user.assert_called_once()
message = message_user.call_args[0][1]
self.assertIn("/admin/market/item/2/change/", message)
self.assertIn(data["name"], message)
self.assertNotIn("You may edit it again below.", message)
# Should not have redirected to changelist
self.assertEqual(response.url, f"/admin/market/item/")
# Should not have changed existing item
self.item.refresh_from_db()
self.assertEqual(self.item.name, "Not name")
self.assertEqual(self.item.file.name.count("test_file"), 1)
self.assertEqual(self.item.image.name.count("test_image2"), 0)
self.assertEqual(self.item.image.name.count("test_image"), 1)
# Should have saved new item
self.assertEqual(Item.objects.count(), 2)
new_item = Item.objects.filter(id=self.item.id + 1).first()
self.assertIsNotNone(new_item)
self.assertEqual(new_item.name, data["name"])
self.assertEqual(new_item.price, data["price"])
self.assertEqual(new_item.currency, data["currency"])
self.assertFalse(new_item.image)
# Able to save the cached file since cached object was there even though cached post was not
self.assertIn("test_file2", new_item.file.name)
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
def test_add_without_cached_object_should_save_new_instance_but_not_have_file(self):
# Request.POST
data = {
"name": "name",
"price": 2.0,
"image": "",
"currency": Item.VALID_CURRENCIES[0][0],
"_confirm_add": True,
"_save": True,
}
# Upload new file
f2 = SimpleUploadedFile(
name="test_file2.jpg",
content=open(self.image_path, "rb").read(),
content_type="image/jpeg",
)
# Set cache
cache_item = Item(
name=data["name"], price=data["price"], currency=data["currency"], file=f2
)
# Make sure there's no post cached obj
cache.delete(CACHE_KEYS["object"])
cache.set(CACHE_KEYS["post"], data)
# Click "Yes, I'm Sure"
del data["_confirm_add"]
data[CONFIRMATION_RECEIVED] = True
with mock.patch.object(ItemAdmin, "message_user") as message_user:
response = self.client.post(f"/admin/market/item/add/", data=data)
# Should show message to user with correct obj and path
message_user.assert_called_once()
message = message_user.call_args[0][1]
self.assertIn("/admin/market/item/2/change/", message)
self.assertIn(data["name"], message)
self.assertNotIn("You may edit it again below.", message)
# Should not have redirected to changelist
self.assertEqual(response.url, f"/admin/market/item/")
# Should not have changed existing item
self.item.refresh_from_db()
self.assertEqual(self.item.name, "Not name")
self.assertEqual(self.item.file.name.count("test_file"), 1)
self.assertEqual(self.item.image.name.count("test_image2"), 0)
self.assertEqual(self.item.image.name.count("test_image"), 1)
# Should have saved new item
self.assertEqual(Item.objects.count(), 2)
new_item = Item.objects.filter(id=self.item.id + 1).first()
self.assertIsNotNone(new_item)
self.assertEqual(new_item.name, data["name"])
self.assertEqual(new_item.price, data["price"])
self.assertEqual(new_item.currency, data["currency"])
self.assertFalse(new_item.image)
# FAILED to save the file, because cached item was not there
self.assertFalse(new_item.file)
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
def test_add_without_any_cache_should_save_new_instance_but_not_have_file(self):
# Request.POST
data = {
"name": "name",
"price": 2.0,
"image": "",
"currency": Item.VALID_CURRENCIES[0][0],
"_confirm_add": True,
"_save": True,
}
# Upload new file
f2 = SimpleUploadedFile(
name="test_file2.jpg",
content=open(self.image_path, "rb").read(),
content_type="image/jpeg",
)
# Set cache
cache_item = Item(
name=data["name"], price=data["price"], currency=data["currency"], file=f2
)
# Make sure there's no cache
cache.delete(CACHE_KEYS["object"])
cache.delete(CACHE_KEYS["post"])
# Click "Yes, I'm Sure"
del data["_confirm_add"]
data[CONFIRMATION_RECEIVED] = True
with mock.patch.object(ItemAdmin, "message_user") as message_user:
response = self.client.post(f"/admin/market/item/add/", data=data)
# Should show message to user with correct obj and path
message_user.assert_called_once()
message = message_user.call_args[0][1]
self.assertIn("/admin/market/item/2/change/", message)
self.assertIn(data["name"], message)
self.assertNotIn("You may edit it again below.", message)
# Should not have redirected to changelist
self.assertEqual(response.url, f"/admin/market/item/")
# Should not have changed existing item
self.item.refresh_from_db()
self.assertEqual(self.item.name, "Not name")
self.assertEqual(self.item.file.name.count("test_file"), 1)
self.assertEqual(self.item.image.name.count("test_image2"), 0)
self.assertEqual(self.item.image.name.count("test_image"), 1)
# Should have saved new item
self.assertEqual(Item.objects.count(), 2)
new_item = Item.objects.filter(id=self.item.id + 1).first()
self.assertIsNotNone(new_item)
self.assertEqual(new_item.name, data["name"])
self.assertEqual(new_item.price, data["price"])
self.assertEqual(new_item.currency, data["currency"])
self.assertFalse(new_item.image)
# FAILED to save the file, because cached item was not there
self.assertFalse(new_item.file)
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
def test_change_without_cached_post_should_save_file_changes(self):
item = self.item
# Load the Change Item Page
ItemAdmin.save_as_continue = False
# Upload new image and remove file
i2 = SimpleUploadedFile(
name="test_image2.jpg",
content=open(self.image_path, "rb").read(),
content_type="image/jpeg",
)
# Request.POST
data = {
"id": item.id,
"name": "name",
"price": 2.0,
"image": i2,
"file": "",
"file-clear": "on",
"currency": Item.VALID_CURRENCIES[0][0],
"_confirm_change": True,
"_saveasnew": True,
}
# Set cache
cache_item = Item(
name=data["name"],
price=data["price"],
currency=data["currency"],
image=i2,
)
cache.set(CACHE_KEYS["object"], cache_item)
# Ensure no cached post
cache.delete(CACHE_KEYS["post"])
# Click "Yes, I'm Sure"
del data["_confirm_change"]
# Image would have been in FILES and not in POST
del data["image"]
data[CONFIRMATION_RECEIVED] = True
with mock.patch.object(ItemAdmin, "message_user") as message_user:
response = self.client.post(
f"/admin/market/item/{self.item.id}/change/", data=data
)
# Should show message to user with correct obj and path
message_user.assert_called_once()
message = message_user.call_args[0][1]
self.assertIn("/admin/market/item/2/change/", message)
self.assertIn(data["name"], message)
self.assertNotIn("You may edit it again below.", message)
# Should have redirected to changelist
self.assertEqual(response.url, f"/admin/market/item/")
# Should not have changed existing item
item.refresh_from_db()
self.assertEqual(item.name, "Not name")
self.assertEqual(item.file.name.count("test_file"), 1)
self.assertEqual(item.image.name.count("test_image2"), 0)
self.assertEqual(item.image.name.count("test_image"), 1)
# Should have saved new item
self.assertEqual(Item.objects.count(), 2)
new_item = Item.objects.filter(id=item.id + 1).first()
self.assertIsNotNone(new_item)
self.assertEqual(new_item.name, data["name"])
self.assertEqual(new_item.price, data["price"])
self.assertEqual(new_item.currency, data["currency"])
# Should have cleared `file` since clear was selected
self.assertFalse(new_item.file)
# Saved cached file from cached obj even if cached post was missing
self.assertIn("test_image2", new_item.image.name)
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
def test_change_without_cached_object_should_save_but_without_file_changes(self):
item = self.item
# Load the Change Item Page
ItemAdmin.save_as_continue = False
# Upload new image and remove file
i2 = SimpleUploadedFile(
name="test_image2.jpg",
content=open(self.image_path, "rb").read(),
content_type="image/jpeg",
)
# Request.POST
data = {
"id": item.id,
"name": "name",
"price": 2.0,
"file": "",
"file-clear": "on",
"currency": Item.VALID_CURRENCIES[0][0],
"_confirm_change": True,
"_saveasnew": True,
}
# Set cache
cache_item = Item(
name=data["name"],
price=data["price"],
currency=data["currency"],
image=i2,
)
# Ensure no cached obj
cache.delete(CACHE_KEYS["object"])
cache.set(CACHE_KEYS["post"], data)
# Click "Yes, I'm Sure"
del data["_confirm_change"]
data[CONFIRMATION_RECEIVED] = True
with mock.patch.object(ItemAdmin, "message_user") as message_user:
response = self.client.post(
f"/admin/market/item/{self.item.id}/change/", data=data
)
# Should show message to user with correct obj and path
message_user.assert_called_once()
message = message_user.call_args[0][1]
self.assertIn("/admin/market/item/2/change/", message)
self.assertIn(data["name"], message)
self.assertNotIn("You may edit it again below.", message)
# Should have redirected to changelist
self.assertEqual(response.url, f"/admin/market/item/")
# Should not have changed existing item
item.refresh_from_db()
self.assertEqual(item.name, "Not name")
self.assertEqual(item.file.name.count("test_file"), 1)
self.assertEqual(item.image.name.count("test_image2"), 0)
self.assertEqual(item.image.name.count("test_image"), 1)
# Should have saved new item
self.assertEqual(Item.objects.count(), 2)
new_item = Item.objects.filter(id=item.id + 1).first()
self.assertIsNotNone(new_item)
self.assertEqual(new_item.name, data["name"])
self.assertEqual(new_item.price, data["price"])
self.assertEqual(new_item.currency, data["currency"])
self.assertFalse(new_item.file)
# FAILED to save image
self.assertFalse(new_item.image)
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
def test_change_without_any_cache_should_save_but_not_have_file_changes(self):
item = self.item
# Load the Change Item Page
ItemAdmin.save_as_continue = False
# Upload new image and remove file
i2 = SimpleUploadedFile(
name="test_image2.jpg",
content=open(self.image_path, "rb").read(),
content_type="image/jpeg",
)
# Request.POST
data = {
"id": item.id,
"name": "name",
"price": 2.0,
"file": "",
"file-clear": "on",
"currency": Item.VALID_CURRENCIES[0][0],
"_confirm_change": True,
"_saveasnew": True,
}
# Set cache
cache_item = Item(
name=data["name"],
price=data["price"],
currency=data["currency"],
image=i2,
)
# Ensure no cache
cache.delete(CACHE_KEYS["object"])
cache.delete(CACHE_KEYS["post"])
# Click "Yes, I'm Sure"
del data["_confirm_change"]
data[CONFIRMATION_RECEIVED] = True
with mock.patch.object(ItemAdmin, "message_user") as message_user:
response = self.client.post(
f"/admin/market/item/{self.item.id}/change/", data=data
)
# Should show message to user with correct obj and path
message_user.assert_called_once()
message = message_user.call_args[0][1]
self.assertIn("/admin/market/item/2/change/", message)
self.assertIn(data["name"], message)
self.assertNotIn("You may edit it again below.", message)
# Should have redirected to changelist
self.assertEqual(response.url, f"/admin/market/item/")
# Should not have changed existing item
item.refresh_from_db()
self.assertEqual(item.name, "Not name")
self.assertEqual(item.file.name.count("test_file"), 1)
self.assertEqual(item.image.name.count("test_image2"), 0)
self.assertEqual(item.image.name.count("test_image"), 1)
# Should have saved new item
self.assertEqual(Item.objects.count(), 2)
new_item = Item.objects.filter(id=item.id + 1).first()
self.assertIsNotNone(new_item)
self.assertEqual(new_item.name, data["name"])
self.assertEqual(new_item.price, data["price"])
self.assertEqual(new_item.currency, data["currency"])
self.assertFalse(new_item.file)
# FAILED to save image
self.assertFalse(new_item.image)
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
def test_change_without_changing_file_should_save_changes(self):
item = self.item
# Load the Change Item Page
ItemAdmin.save_as_continue = False
# Request.POST
data = {
"id": item.id,
"name": "name",
"price": 2.0,
"file": "",
"image": "",
"file-clear": "on",
"currency": Item.VALID_CURRENCIES[0][0],
"_confirm_change": True,
"_save": True,
}
# Set cache
cache_item = Item(
name=data["name"],
price=data["price"],
currency=data["currency"],
)
cache.get(CACHE_KEYS["object"], cache_item)
cache.get(CACHE_KEYS["post"], data)
# Click "Yes, I'm Sure"
del data["_confirm_change"]
data[CONFIRMATION_RECEIVED] = True
with mock.patch.object(ItemAdmin, "message_user") as message_user:
response = self.client.post(
f"/admin/market/item/{self.item.id}/change/", data=data
)
# Should show message to user with correct obj and path
message_user.assert_called_once()
message = message_user.call_args[0][1]
self.assertIn("/admin/market/item/1/change/", message)
self.assertIn(data["name"], message)
self.assertNotIn("You may edit it again below.", message)
# Should have redirected to changelist
self.assertEqual(response.url, f"/admin/market/item/")
# Should have changed existing item
self.assertEqual(Item.objects.count(), 1)
item.refresh_from_db()
self.assertEqual(item.name, "name")
# Should have cleared if requested
self.assertFalse(item.file.name)
self.assertEqual(item.image.name.count("test_image2"), 0)
self.assertEqual(item.image.name.count("test_image"), 1)
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
@mock.patch("admin_confirm.admin.CACHE_TIMEOUT", 1)
def test_old_cache_should_not_be_used(self):
item = self.item
# Upload new image and remove file
i2 = SimpleUploadedFile(
name="test_image2.jpg",
content=open(self.image_path, "rb").read(),
content_type="image/jpeg",
)
# Click "Save And Continue"
data = {
"id": item.id,
"name": "name",
"price": 2.0,
"image": i2,
"file": "",
"file-clear": "on",
"currency": Item.VALID_CURRENCIES[0][0],
"_confirm_change": True,
"_continue": True,
}
response = self.client.post(f"/admin/market/item/{item.id}/change/", data=data)
# Should be shown confirmation page
self._assertSubmitHtml(
rendered_content=response.rendered_content,
save_action="_continue",
multipart_form=True,
)
# Should have cached the unsaved item
cached_item = cache.get(CACHE_KEYS["object"])
self.assertIsNotNone(cached_item)
self.assertIsNone(cached_item.id)
self.assertEqual(cached_item.name, data["name"])
self.assertEqual(cached_item.price, data["price"])
self.assertEqual(cached_item.currency, data["currency"])
self.assertFalse(cached_item.file.name)
self.assertEqual(cached_item.image, i2)
# Should not have saved the changes yet
self.assertEqual(Item.objects.count(), 1)
item.refresh_from_db()
self.assertEqual(item.name, "Not name")
self.assertIsNotNone(item.file)
self.assertIsNotNone(item.image)
# Wait for cache to time out
time.sleep(1)
# Check that it did time out
cached_item = cache.get(CACHE_KEYS["object"])
self.assertIsNone(cached_item)
# Click "Yes, I'm Sure"
del data["_confirm_change"]
data["image"] = ""
data[CONFIRMATION_RECEIVED] = True
response = self.client.post(f"/admin/market/item/{item.id}/change/", data=data)
# Should not have redirected to changelist
self.assertEqual(response.url, f"/admin/market/item/{item.id}/change/")
# Should have saved item
self.assertEqual(Item.objects.count(), 1)
saved_item = Item.objects.all().first()
self.assertEqual(saved_item.name, data["name"])
self.assertEqual(saved_item.price, data["price"])
self.assertEqual(saved_item.currency, data["currency"])
self.assertFalse(saved_item.file)
# SHOULD not have saved image since it was in the old cache
self.assertNotIn("test_image2", saved_item.image)
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
def test_cache_with_incorrect_model_should_not_be_used(self):
item = self.item
# Load the Change Item Page
ItemAdmin.save_as_continue = False
# Upload new image and remove file
i2 = SimpleUploadedFile(
name="test_image2.jpg",
content=open(self.image_path, "rb").read(),
content_type="image/jpeg",
)
# Request.POST
data = {
"id": item.id,
"name": "name",
"price": 2.0,
"file": "",
"file-clear": "on",
"currency": Item.VALID_CURRENCIES[0][0],
"_confirm_change": True,
"_save": True,
}
# Set cache to incorrect model
cache_obj = Shop(name="ShopName")
cache.set(CACHE_KEYS["object"], cache_obj)
cache.set(CACHE_KEYS["post"], data)
# Click "Yes, I'm Sure"
del data["_confirm_change"]
data[CONFIRMATION_RECEIVED] = True
with mock.patch.object(ItemAdmin, "message_user") as message_user:
response = self.client.post(
f"/admin/market/item/{self.item.id}/change/", data=data
)
# Should show message to user with correct obj and path
message_user.assert_called_once()
message = message_user.call_args[0][1]
self.assertIn("/admin/market/item/1/change/", message)
self.assertIn(data["name"], message)
self.assertNotIn("You may edit it again below.", message)
# Should have redirected to changelist
self.assertEqual(response.url, f"/admin/market/item/")
# Should have changed existing item
self.assertEqual(Item.objects.count(), 1)
item.refresh_from_db()
self.assertEqual(item.name, "name")
# Should have cleared if requested
self.assertFalse(item.file.name)
self.assertEqual(item.image.name.count("test_image2"), 0)
self.assertEqual(item.image.name.count("test_image"), 1)
# Should have cleared cache
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))
def test_form_without_files_should_not_use_cache(self):
cache.delete_many(CACHE_KEYS.values())
shop = ShopFactory()
# Click "Save And Continue"
data = {
"id": shop.id,
"name": "name",
"_confirm_change": True,
"_continue": True,
}
response = self.client.post(f"/admin/market/shop/{shop.id}/change/", data=data)
# Should be shown confirmation page
self._assertSubmitHtml(
rendered_content=response.rendered_content, save_action="_continue"
)
# Should not have set cache since not multipart form
for key in CACHE_KEYS.values():
self.assertIsNone(cache.get(key))