8000 fix: Grouper models w/o must not assume language grouper (#8194) · django-cms/django-cms@5de81ff · GitHub
[go: up one dir, main page]

Skip to content

Commit 5de81ff

Browse files
fix: Grouper models w/o must not assume language grouper (#8194)
* fix: Grouper models w/o language grouper * Keep language field, but not as grouper * Update cms/tests/test_grouper_admin.py Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update cms/admin/utils.py Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update cms/tests/test_grouper_admin.py Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update cms/tests/test_grouper_admin.py Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update cms/tests/test_grouper_admin.py Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update cms/tests/test_grouper_admin.py Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Fix ruff issues --------- Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
1 parent fff11a1 commit 5de81ff

File tree

4 files changed

+231
-13
lines changed

4 files changed

+231
-13
lines changed

cms/admin/utils.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from cms.models.managers import ContentAdminManager
2424
from cms.toolbar.utils import get_object_preview_url
2525
from cms.utils import get_language_from_request
26-
from cms.utils.i18n import get_language_dict, get_language_tuple
26+
from cms.utils.i18n import get_language_dict, get_language_list, get_language_tuple
2727
from cms.utils.urlutils import admin_reverse, static_with_version
2828

2929

@@ -771,8 +771,9 @@ def update_labels(self, fields: typing.List[str]) -> None:
771771

772772
def clean(self) -> dict:
773773
if (
774-
self.cleaned_data.get(CONTENT_PREFIX + "language", None)
775-
not in get_language_dict()
774+
f"{CONTENT_PREFIX}language" in self.cleaned_data
775+
and self.cleaned_data[f"{CONTENT_PREFIX}language"]
776+
not in get_language_list()
776777
):
777778
raise ValidationError(
778779
_(

cms/test_utils/project/sampleapp/admin.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
GrouperModel,
88
Picture,
99
SampleAppConfig,
10+
SimpleGrouperModel,
1011
SomeEditableModel,
1112
)
1213

@@ -31,7 +32,15 @@ def can_change_content(self, request, content_obj):
3132
return getattr(self, "change_content", True)
3233

3334

35+
class SimpleGrouperAdmin(GrouperModelAdmin):
36+
list_display = ("category_name", "content__secret_greeting", "admin_list_actions")
37+
38+
def can_change_content(self, request, content_obj):
39+
return getattr(self, "change_content", True)
40+
41+
3442
admin.site.register(Category, CategoryAdmin)
3543
admin.site.register(SampleAppConfig)
3644
admin.site.register(SomeEditableModel, SomeEditableAdmin)
3745
admin.site.register(GrouperModel, GrouperAdmin)
46+
admin.site.register(SimpleGrouperModel, SimpleGrouperAdmin)

cms/test_utils/project/sampleapp/models.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,51 @@ class GrouperModelContent(models.Model):
5757
)
5858
)
5959

60+
region = models.TextField(
61+
default="world",
62+
max_length=10,
63+
choices=(
64+
("world", "World"),
65+
("americas", "Americas"),
66+
("europe", "Europe"),
67+
("africa", "Africa"),
68+
("asia", "Asia"),
69+
("australia", "Australia")
70+
)
71+
)
72+
73+
uptodate = models.BooleanField(
74+
verbose_name="Yes/No",
75+
default=False,
76+
)
77+
78+
secret_greeting = models.TextField(
79+
max_length=100,
80+
)
81+
82+
83+
class SimpleGrouperModel(models.Model):
84+
category_name = models.CharField(max_length=200, default="")
85+
86+
87+
class SimpleGrouperModelContent(models.Model):
88+
# grouper field name: snake case of GrouperModel
89+
simple_grouper_model = models.ForeignKey(
90+
SimpleGrouperModel,
91+
on_delete=models.CASCADE,
92+
)
93+
94+
language = models.TextField(
95+
default="en",
96+
choices=(
97+
("en", "English"),
98+
("de", "German"),
99+
("it", "Italian"),
100+
)
101+
)
102+
103+
104+
60105
region = models.TextField(
61106
default="world",
62107
max_length=10,

cms/tests/test_grouper_admin.py

Lines changed: 173 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
from cms.test_utils.project.sampleapp.models import (
88
GrouperModel,
99
GrouperModelContent,
10+
SimpleGrouperModel,
11+
SimpleGrouperModelContent,
1012
)
1113
from cms.test_utils.testcases import CMSTestCase
1214
from cms.test_utils.util.grouper import wo_content_permission
@@ -25,6 +27,8 @@ def setUp(self) -> None:
2527
self.changelist_url = admin_reverse("sampleapp_groupermodel_changelist")
2628
self.admin_user = self.get_superuser()
2729
self.admin = site._registry[GrouperModel]
30+
self.groupermodel = "groupermodel"
31+
self.grouper_model = "grouper_model"
2832

2933
def tearDown(self) -> None:
3034
self.grouper_instance.delete()
@@ -42,13 +46,44 @@ def createContentInstance(self, language="en"):
4246
return instance
4347

4448

45-
class ChangeListActionsTestCase(SetupMixin, CMSTestCase):
49+
class SimpleSetupMixin:
50+
"""Create one grouper object and retrieve the admin instance"""
51+
def setUp(self) -> None:
52+
self.grouper_instance = SimpleGrouperModel.objects.create(
53+
category_name="Grouper Category"
54+
)
55+
self.add_url = admin_reverse("sampleapp_simplegroupermodel_add")
56+
self.change_url = admin_reverse("sampleapp_simplegroupermodel_change", args=(self.grouper_instance.pk,))
57+
self.changelist_url = admin_reverse("sampleapp_simplegroupermodel_changelist")
58+
self.admin_user = self.get_superuser()
59+
self.admin = site._registry[SimpleGrouperModel]
60+
self.groupermodel = "simplegroupermodel"
61+
self.grouper_model = "simple_grouper_model"
62+
63+
def tearDown(self) -> None:
64+
self.grouper_instance.delete()
65+
self.admin.clear_content_cache() # The admin does this automatically for each new request.
66+
67+
def createContentInstance(self, language="en"):
68+
"""Creates a content instance with a random content for a language. The random content is returned
69+
to be able to check if it appears in forms etc."""
70+
71+
assert language == "en", "Only English is supported for SimpleGrouperModelContent"
72+
instance = SimpleGrouperModelContent.objects.create(
73+
simple_grouper_model=self.grouper_instance,
74+
secret_greeting=get_random_string(16),
75+
)
76+
self.admin.clear_content_cache() # The admin does this automatically for each new request.
77+
return instance
78+
79+
80+
class SimpleChangeListActionsTestCase(SimpleSetupMixin, CMSTestCase):
4681
def test_action_js_css(self):
4782
"""Are js and css files loaded?
4883
The js and css files are supposed to be arranged by the GrouperAdminMixin."""
4984
with self.login_user_context(self.admin_user):
5085
# Act
51-
response = self.client.get(self.changelist_url + "?language=en")
86+
response = self.client.get(f"{self.changelist_url}?", follow=True)
5287
# Assert
5388
self.assertContains(response, static("admin/js/jquery.init.js"))
5489
self.assertContains(response, static("cms/js/admin/actions.js"))
@@ -59,11 +94,11 @@ def test_add_action(self):
5994
The button is supposed to be arranged by the GrouperAdminMixin."""
6095
with self.login_user_context(self.admin_user):
6196
# Act
62-
response = self.client.get(self.changelist_url + "?language=en")
97+
response = self.client.get(f"{self.changelist_url}?language=en", follow=True)
6398
# Assert
6499
self.assertContains(response, 'class="cms-icon cms-icon-plus"')
65-
self.assertContains(response, f'href="/en/admin/sampleapp/groupermodel/{self.grouper_instance.pk}'
66-
f'/change/?language=en"')
100+
self.assertContains(response, f'href="/en/admin/sampleapp/{self.groupermodel}/{self.grouper_instance.pk}'
101+
f'/change/?')
67102
self.assertNotContains(response, 'class="cms-icon cms-icon-view"')
68103

69104
def test_change_action(self):
@@ -72,11 +107,11 @@ def test_change_action(self):
72107
self.createContentInstance("en")
73108
with self.login_user_context(self.admin_user):
74109
# Act
75-
response = self.client.get(self.changelist_url + "?language=en")
110+
response = self.client.get(f"{self.changelist_url}?language=en", follow=True)
76111
# Assert
77112
self.assertContains(response, 'class="cms-icon cms-icon-view"')
78-
self.assertContains(response, f'href="/en/admin/sampleapp/groupermodel/{self.grouper_instance.pk}'
79-
f'/change/?language=en"')
113+
self.assertContains(response, f'href="/en/admin/sampleapp/{self.groupermodel}/{self.grouper_instance.pk}'
114+
f'/change/?')
80115
self.assertContains(response, 'class="cms-icon cms-icon-view"')
81116

82117
def test_get_action(self):
@@ -104,6 +139,10 @@ def test_post_action(self):
104139
self.assertIn("cms-form-post-method", get_action)
105140

106141

142+
class ChangeListActionsTestCase(SetupMixin, SimpleChangeListActionsTestCase):
143+
pass
144+
145+
107146
class GrouperModelAdminTestCase(SetupMixin, CMSTestCase):
108147
def test_form_class_created(self):
109148
"""The form class has automatically been enhanced with the GrouperAdminFormMixin for
@@ -177,14 +216,57 @@ def test_with_content_only(self) -> None:
177216
self.assertContains(response, random_content[language])
178217

179218

219+
class SimpleGrouperChangeListTestCase(SimpleSetupMixin, CMSTestCase):
220+
def test_mixed_change_form(self):
221+
"""Change form contains input for both grouper and content objects"""
222+
# Arrange
223+
random_content = self.createContentInstance("en")
224+
with self.login_user_context(self.admin_user):
225+
# Act
226+
response = self.client.get(f"{self.change_url}?language=en", follow=True)
227+
# Assert
228+
# Contains relation to grouper as hidden input
229+
self.assertContains(
230+
response,
231+
'<input type="hidden" name="content__simple_grouper_model"',
232+
)
233+
# Contains grouper field with category (and its value)
234+
self.assertContains(
235+
response,
236+
'<input type="text" name="category_name" value="Grouper Category"',
237+
)
238+
# Contains content secret message as textarea
239+
self.assertContains(response, '<textarea name="content__secret_greeting"')
240+
self.assertContains(response, random_content.secret_greeting)
241+
242+
def test_empty_content(self) -> None:
243+
"""Without any content being created the changelist shows an empty content text"""
244+
with self.login_user_context(self.admin_user):
245+
# Act
246+
response = self.client.get(self.changelist_url)
247+
# Assert
248+
self.assertContains(response, "Empty content")
249+
250+
def test_with_content(self) -> None:
251+
"""Create one content object and see if it appears in the admin"""
252+
# Arrange
253+
random_content = self.createContentInstance()
254+
with self.login_user_context(self.admin_user):
255+
# Act
256+
response = self.client.get(self.changelist_url)
257+
# Assert
258+
self.assertContains(response, "Grouper Category")
259+
self.assertContains(response, random_content.secret_greeting)
260+
261+
180262
class GrouperChangeTestCase(SetupMixin, CMSTestCase):
181263
def test_mixed_change_form(self):
182264
"""Change form contains input for both grouper and content objects"""
183265
# Arrange
184266
random_content = self.createContentInstance("en")
185267
with self.login_user_context(self.admin_user):
186268
# Act
187-
response = self.client.get(self.change_url + "?language=en")
269+
response = self.client.get(f"{self.change_url}?language=en", follow=True)
188270
# Assert
189271
# Contains relation to grouper as hidden input
190272
self.assertContains(
@@ -208,7 +290,7 @@ def test_mixed_change_form(self):
208290
def test_change_form_contains_defaults_for_groupers(self) -> None:
209291
with self.login_user_context(self.admin_user):
210292
# Act
211-
response = self.client.get(self.change_url + "?language=en")
293+
response = self.client.get(self.change_url + "?language=en", follow=True)
212294
# Assert
213295
self.assertContains(response, 'name="content__language" value="en"')
214296
self.assertNotContains(response, 'name="content__language" value="de"')
@@ -238,6 +320,11 @@ def test_change_form_wo_write_permit(self) -> None:
238320
response,
239321
'<input type="hidden" name="content__language" value="en" id="id_content__language">',
240322
)
323+
# Contains extra grouping field as hidden input
324+
self.assertContains(
325+
response,
326+
'<input type="hidden" name="content__language" value="en" id="id_content__language">',
327+
)
241328
# Contains grouper field with category (and its value)
242329
self.assertContains(response, '<input type="text" name="category_name" value="Grouper Category"')
243330
# Does not contain content secret message as textarea
@@ -333,3 +420,79 @@ def test_create_content_model(self) -> None:
333420
self.assertEqual(content_instance_en.secret_greeting, random_content.secret_greeting) # unchanged
334421
self.assertIsNotNone(content_instance_de) # Exists?
335422
self.assertEqual(content_instance_de.secret_greeting, data["content__secret_greeting"]) # Has new content
423+
424+
425+
class SimpleGrouperChangeTestCase(SimpleSetupMixin, CMSTestCase):
426+
def test_save_grouper_model(self) -> None:
427+
# Arrange
428+
random_content = self.createContentInstance()
429+
data = {
430+
"category_name": "Changed content",
431+
"content__region": "world",
432+
"content__language": "de",
433+
"content__secret_greeting": random_content.secret_greeting,
434+
}
435+
with self.login_user_context(self.admin_user):
436+
# Act
437+
response = self.client.post(self.change_url, data=data)
438+
# Assert
439+
self.grouper_instance.refresh_from_db()
440+
self.assertEqual(response.status_code, 302) # Expecting redirect
441+
self.assertEqual(self.grouper_instance.category_name, data["category_name"])
442+
443+
def test_save_content_model(self) -> None:
444+
# Arrange
445+
self.createContentInstance()
446+
data = {
447+
"category_name": self.grouper_instance.category_name,
448+
"content__region": "world",
449+
"content__language": "de",
450+
"content__secret_greeting": "New greeting",
451+
}
452+
# Act
453+
with self.login_user_context(self.admin_user):
454+
response = self.client.post(self.change_url, data=data)
455+
content_instance = SimpleGrouperModelContent.objects.first()
456+
# Assert
457+
self.assertEqual(response.status_code, 302) # Expecting redirect
458+
self.assertIsNotNone(content_instance)
459+
self.assertEqual(content_instance.secret_greeting, data["content__secret_greeting"])
460+
461+
def test_create_grouper_model(self) -> None:
462 10904 +
# Arrange
463+
data = {
464+
"category_name": "My new category",
465+
"content__region": "world",
466+
"content__language": "de",
467+
"content__secret_greeting": "Some new content",
468+
}
469+
# Act
470+
with self.login_user_context(self.admin_user):
471+
response = self.client.post(self.add_url, data=data)
472+
grouper_instance = SimpleGrouperModel.objects.filter(category_name=data["category_name"]).first()
473+
content_instance = grouper_instance.simplegroupermodelcontent_set.first() # Get content
474+
475+
# Assert
476+
self.assertEqual(response.status_code, 302) # Expecting redirect
477+
self.assertEqual(SimpleGrouperModel.objects.all().count(), 2)
478+
self.assertIsNotNone(grouper_instance)
479+
self.assertIsNotNone(content_instance) # Should exist
480+
self.assertEqual(content_instance.secret_greeting, data["content__secret_greeting"]) # Has new content
481+
482+
def test_create_content_model(self) -> None:
483+
# Arrange
484+
self.createContentInstance()
485+
data = {
486+
"category_name": self.grouper_instance.category_name,
487+
"content__region": "world",
488+
"content__language": "de",
489+
"content__secret_greeting": "New content",
490+
}
491+
# Act
492+
with self.login_user_context(self.admin_user):
493+
response = self.client.post(self.change_url, data=data)
494+
content_instance = SimpleGrouperModelContent.objects.first() # Get content
495+
# Assert
496+
self.assertEqual(response.status_code, 302) # Expecting redirect
497+
self.assertIsNotNone(content_instance)
498+
self.assertEqual(content_instance.secret_greeting, data["content__secret_greeting"]) # Has new content

0 commit comments

Comments
 (0)
0