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:
@@ -0,0 +1,100 @@
|
||||
# Testing Documentation/Notes
|
||||
|
||||
[](https://coveralls.io/github/TrangPham/django-admin-confirm)
|
||||
|
||||
Hello, friend! You have found the list of test cases that this package can benefit from.
|
||||
|
||||
You seem concerned about the stability and reliability of this package. You're probably wondering if you should include it in your production codebase. Well, although I have tried very hard to get 100% code coverage, there are so many permutations of ModelAdmins in the wild. And I'm only one person.
|
||||
|
||||
So if you want to include this package in your production codebase, be aware that AdminConfirmMixin works best with simple unmodified ModelAdmins.
|
||||
|
||||
## Save Options
|
||||
|
||||
- [x] Save
|
||||
- [x] Conitnue
|
||||
- [x] Save As New
|
||||
- [x] Add another
|
||||
|
||||
### Field types
|
||||
|
||||
- [x] CharField
|
||||
- [x] PositiveIntegerField
|
||||
- [x] DecimalField
|
||||
- [x] TextField
|
||||
- [x] ImageField
|
||||
- [x] FileField
|
||||
- [x] ManyToManyField
|
||||
- [x] OneToOneField
|
||||
- [x] ForeignKey
|
||||
|
||||
- [x] Custom Readonly fields
|
||||
|
||||
### Options
|
||||
|
||||
- [x] .exclude
|
||||
- [x] .fields
|
||||
- [x] .readonly_fields
|
||||
- [x] Actions
|
||||
|
||||
### Options to test
|
||||
|
||||
- [x] ModelAdmin.fieldsets
|
||||
- [ ] ModelAdmin.form
|
||||
- [ ] ModelAdmin.raw_id_fields
|
||||
- [ ] ModelAdmin.radio_fields
|
||||
- [ ] ModelAdmin.autocomplete_fields
|
||||
- [ ] ModelAdmin.prepopulated_fields
|
||||
|
||||
## ModelAdmin form template overrides?
|
||||
|
||||
https://docs.djangoproject.com/en/3.1/ref/contrib/admin/#custom-template-options
|
||||
(Maybe??? IDK this is esoteric)
|
||||
|
||||
## Function overrides to test
|
||||
|
||||
- [ ] .save_model()
|
||||
- [ ] .get_readonly_fields()
|
||||
- [ ] .get_fields()
|
||||
- [ ] .get_excludes()
|
||||
- [ ] .get_form()
|
||||
- [ ] .get_autocomplete_fields()
|
||||
- [ ] .get_prepopulated_fields()
|
||||
- [x] .get_fieldsets()
|
||||
- [ ] ModelAdmin.formfield_for_manytomany()
|
||||
- [ ] ModelAdmin.formfield_for_foreignkey()
|
||||
- [ ] ModelAdmin.formfield_for_choice_field()
|
||||
- [ ] ModelAdmin.get_changeform_initial_data()
|
||||
|
||||
## Inline instance support??
|
||||
|
||||
Confirmation on inline changes is not a current feature of this project.
|
||||
|
||||
Confirmation on add/change of ModelAdmin that includes inlines needs to be tested. Use AdminConfirmMixin with ModelAdmin containing inlines at your own risk.
|
||||
|
||||
- [ ] .inlines
|
||||
- [ ] .get_inline_instances()
|
||||
- [ ] .get_inlines() (New in Django 3.0)
|
||||
- [ ] .get_formsets_with_inlines()
|
||||
|
||||
#### Options for inlines
|
||||
|
||||
- [ ] classes of inlines: Tabular, Stacked, etc
|
||||
- [ ] extra
|
||||
- [ ] action on the inline: add or change
|
||||
- [ ] clicking add another on the inline
|
||||
|
||||
## IDK if we want to support these
|
||||
|
||||
- [ ] .get_changelist_form()
|
||||
- [ ] ModelAdmin.list_editable
|
||||
- [ ] ModelAdmin.changelist_view()
|
||||
|
||||
- [ ] ModelAdmin.add_view(request, form_url='', extra_context=None)
|
||||
- [ ] ModelAdmin.change_view(request, object_id, form_url='', extra_context=None)
|
||||
|
||||
## More tests for these?
|
||||
|
||||
Note: Currently the code always calls super().\_changeform_view(), which would ensure permissions correct as well
|
||||
|
||||
- [x] ModelAdmin.has_add_permission
|
||||
- [x] ModelAdmin.has_change_permission
|
||||
@@ -1,8 +1,13 @@
|
||||
from django.core.cache import cache
|
||||
from django.test import TestCase, RequestFactory
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
|
||||
class ConfirmAdminTestCase(TestCase):
|
||||
class AdminConfirmTestCase(TestCase):
|
||||
"""
|
||||
Helper TestCase class and common associated assertions
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.superuser = User.objects.create_superuser(
|
||||
@@ -10,6 +15,7 @@ class ConfirmAdminTestCase(TestCase):
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
cache.clear()
|
||||
self.client.force_login(self.superuser)
|
||||
self.factory = RequestFactory()
|
||||
|
||||
@@ -24,7 +30,9 @@ class ConfirmAdminTestCase(TestCase):
|
||||
# ManyToManyField should be embedded
|
||||
self.assertIn("related-widget-wrapper", rendered_content)
|
||||
|
||||
def _assertSubmitHtml(self, rendered_content, save_action="_save"):
|
||||
def _assertSubmitHtml(
|
||||
self, rendered_content, save_action="_save", multipart_form=False
|
||||
):
|
||||
# Submit should conserve the save action
|
||||
self.assertIn(
|
||||
f'<input type="submit" value="Yes, I’m sure" name="{save_action}">',
|
||||
@@ -34,6 +42,16 @@ class ConfirmAdminTestCase(TestCase):
|
||||
self.assertNotIn("_confirm_add", rendered_content)
|
||||
self.assertNotIn("_confirm_change", rendered_content)
|
||||
|
||||
confirmation_received_html = (
|
||||
'<input type="hidden" name=CONFIRMATION_RECEIVED value="True">'
|
||||
)
|
||||
|
||||
if multipart_form:
|
||||
# Should have _confirmation_received as a hidden field
|
||||
self.assertIn(confirmation_received_html, rendered_content)
|
||||
else:
|
||||
self.assertNotIn(confirmation_received_html, rendered_content)
|
||||
|
||||
def _assertSimpleFieldFormHtml(self, rendered_content, fields):
|
||||
for k, v in fields.items():
|
||||
self.assertIn(f'name="{k}"', rendered_content)
|
||||
|
||||
@@ -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))
|
||||
+54
-6
@@ -1,17 +1,20 @@
|
||||
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 ConfirmAdminTestCase
|
||||
from tests.market.admin import ItemAdmin, InventoryAdmin
|
||||
from tests.market.models import Item, Inventory
|
||||
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
|
||||
|
||||
|
||||
class TestConfirmChangeAndAdd(ConfirmAdminTestCase):
|
||||
@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)
|
||||
@@ -70,6 +73,36 @@ class TestConfirmChangeAndAdd(ConfirmAdminTestCase):
|
||||
# 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 = {
|
||||
@@ -100,7 +133,9 @@ class TestConfirmChangeAndAdd(ConfirmAdminTestCase):
|
||||
self._assertSimpleFieldFormHtml(
|
||||
rendered_content=response.rendered_content, fields=form_data
|
||||
)
|
||||
self._assertSubmitHtml(rendered_content=response.rendered_content)
|
||||
self._assertSubmitHtml(
|
||||
rendered_content=response.rendered_content, multipart_form=True
|
||||
)
|
||||
|
||||
# Hasn't changed item yet
|
||||
item.refresh_from_db()
|
||||
@@ -120,9 +155,22 @@ class TestConfirmChangeAndAdd(ConfirmAdminTestCase):
|
||||
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())
|
||||
self.assertEqual(expected_fields, actual_fields)
|
||||
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"]
|
||||
+8
-2
@@ -1,12 +1,15 @@
|
||||
from unittest import mock
|
||||
from admin_confirm.admin import AdminConfirmMixin
|
||||
from django.urls import reverse
|
||||
|
||||
from admin_confirm.tests.helpers import ConfirmAdminTestCase
|
||||
from admin_confirm.tests.helpers import AdminConfirmTestCase
|
||||
from tests.market.admin import ShoppingMallAdmin
|
||||
from tests.market.models import ShoppingMall
|
||||
from tests.factories import ShopFactory
|
||||
|
||||
|
||||
class TestConfirmChangeAndAddM2MField(ConfirmAdminTestCase):
|
||||
@mock.patch.object(ShoppingMallAdmin, "inlines", [])
|
||||
class TestConfirmChangeAndAddM2MField(AdminConfirmTestCase):
|
||||
def test_post_add_without_confirm_add_m2m(self):
|
||||
shops = [ShopFactory() for i in range(3)]
|
||||
|
||||
@@ -84,6 +87,9 @@ class TestConfirmChangeAndAddM2MField(ConfirmAdminTestCase):
|
||||
]
|
||||
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,
|
||||
@@ -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))
|
||||
@@ -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)
|
||||
@@ -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))
|
||||
Reference in New Issue
Block a user