Skip to content

Commit a64e2ed

Browse files
authored
Merge branch 'main' into fix-apphook-reload-language-variants
2 parents 7336e8b + 0578f21 commit a64e2ed

File tree

61 files changed

+174
-188
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+174
-188
lines changed

.github/workflows/test.yml

Lines changed: 66 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: django CMS test.yml
22

3-
on:
3+
on:
44
pull_request:
55
push:
66
branches:
@@ -16,27 +16,35 @@ jobs:
1616
strategy:
1717
fail-fast: false
1818
matrix:
19-
python-version: [3.9, '3.10', '3.11', '3.12', '3.13']
19+
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
2020
requirements-file: [
2121
django-4.2.txt,
2222
django-5.0.txt,
2323
django-5.1.txt,
2424
django-5.2.txt,
25+
django-6.0.txt,
2526
]
27+
postgres-version: ['16', '17', '18']
2628
os: [
2729
ubuntu-latest,
2830
]
2931
exclude:
30-
- requirements-file: django-5.0.txt
31-
python-version: 3.9
32-
- requirements-file: django-5.1.txt
33-
python-version: 3.9
34-
- requirements-file: django-5.2.txt
35-
python-version: 3.9
32+
# Python 3.14 only works with Django 5.2+
33+
- python-version: '3.14'
34+
requirements-file: django-4.2.txt
35+
- python-version: '3.14'
36+
requirements-file: django-5.0.txt
37+
- python-version: '3.14'
38+
requirements-file: django-5.1.txt
39+
# Python 3.10 and 3.11 only supported until Django 5.2
40+
- python-version: '3.10'
41+
requirements-file: django-6.0.txt
42+
- python-version: '3.11'
43+
requirements-file: django-6.0.txt
3644

3745
services:
3846
postgres:
39-
image: postgres:latest
47+
image: postgres:${{ matrix.postgres-version }}
4048
env:
4149
POSTGRES_USER: postgres
4250
POSTGRES_PASSWORD: postgres
@@ -70,9 +78,9 @@ jobs:
7078
DATABASE_URL: postgres://postgres:postgres@127.0.0.1/postgres
7179

7280
- name: Upload coverage data
73-
uses: actions/upload-artifact@v4
81+
uses: actions/upload-artifact@v5
7482
with:
75-
name: coverage-data-${{ github.job }}-${{ matrix.python-version }}-${{ matrix.requirements-file }}
83+
name: coverage-data-${{ github.job }}-${{ matrix.python-version }}-${{ matrix.requirements-file }}-postgres${{ matrix.postgres-version }}
7684
include-hidden-files: true
7785
path: '.coverage*'
7886

@@ -81,27 +89,35 @@ jobs:
8189
strategy:
8290
fail-fast: false
8391
matrix:
84-
python-version: [3.9, '3.10', '3.11', '3.12', '3.13']
92+
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
8593
requirements-file: [
8694
django-4.2.txt,
8795
django-5.0.txt,
8896
django-5.1.txt,
8997
django-5.2.txt,
98+
django-6.0.txt,
9099
]
100+
mysql-version: ['8.4', '9.5']
91101
os: [
92102
ubuntu-latest,
93103
]
94104
exclude:
95-
- requirements-file: django-5.0.txt
96-
python-version: 3.9
97-
- requirements-file: django-5.1.txt
98-
python-version: 3.9
99-
- requirements-file: django-5.2.txt
100-
python-version: 3.9
105+
# Python 3.14 only works with Django 5.2+
106+
- python-version: '3.14'
107+
requirements-file: django-4.2.txt
108+
- python-version: '3.14'
109+
requirements-file: django-5.0.txt
110+
- python-version: '3.14'
111+
requirements-file: django-5.1.txt
112+
# Python 3.10 and 3.11 only supported until Django 5.2
113+
- python-version: '3.10'
114+
requirements-file: django-6.0.txt
115+
- python-version: '3.11'
116+
requirements-file: django-6.0.txt
101117

102118
services:
103119
mysql:
104-
image: mysql:8.4
120+
image: mysql:${{ matrix.mysql-version }}
105121
env:
106122
MYSQL_ALLOW_EMPTY_PASSWORD: yes
107123
MYSQL_DATABASE: djangocms_test
@@ -134,9 +150,9 @@ jobs:
134150
DATABASE_URL: mysql://root@127.0.0.1/djangocms_test
135151

136152
- name: Upload coverage data
137-
uses: actions/upload-artifact@v4
153+
uses: actions/upload-artifact@v5
138154
with:
139-
name: coverage-data-${{ github.job }}-${{ matrix.python-version }}-${{ matrix.requirements-file }}
155+
name: coverage-data-${{ github.job }}-${{ matrix.python-version }}-${{ matrix.requirements-file }}-mysql${{ matrix.mysql-version }}
140156
include-hidden-files: true
141157
path: '.coverage*'
142158

@@ -145,23 +161,30 @@ jobs:
145161
strategy:
146162
fail-fast: false
147163
matrix:
148-
python-version: [3.9, '3.10', '3.11', '3.12', '3.13']
164+
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
149165
requirements-file: [
150166
django-4.2.txt,
151167
django-5.0.txt,
152168
django-5.1.txt,
153169
django-5.2.txt,
170+
django-6.0.txt,
154171
]
155172
os: [
156173
ubuntu-latest,
157174
]
158175
exclude:
159-
- requirements-file: django-5.0.txt
160-
python-version: 3.9
161-
- requirements-file: django-5.1.txt
162-
python-version: 3.9
163-
- requirements-file: django-5.2.txt
164-
python-version: 3.9
176+
# Python 3.14 only works with Django 5.2+
177+
- python-version: '3.14'
178+
requirements-file: django-4.2.txt
179+
- python-version: '3.14'
180+
requirements-file: django-5.0.txt
181+
- python-version: '3.14'
182+
requirements-file: django-5.1.txt
183+
# Python 3.10 and 3.11 only supported until Django 5.2
184+
- python-version: '3.10'
185+
requirements-file: django-6.0.txt
186+
- python-version: '3.11'
187+
requirements-file: django-6.0.txt
165188

166189
steps:
167190
- uses: actions/checkout@v5
@@ -186,7 +209,7 @@ jobs:
186209
DATABASE_URL: sqlite://localhost/testdb.sqlite
187210

188211
- name: Upload coverage data
189-
uses: actions/upload-artifact@v4
212+
uses: actions/upload-artifact@v5
190213
with:
191214
name: coverage-data-${{ github.job }}-${{ matrix.python-version }}-${{ matrix.requirements-file }}
192215
include-hidden-files: true
@@ -225,7 +248,7 @@ jobs:
225248
DATABASE_URL: sqlite://localhost/testdb.sqlite
226249

227250
- name: Upload coverage data
228-
uses: actions/upload-artifact@v4
251+
uses: actions/upload-artifact@v5
229252
with:
230253
name: coverage-data-${{ github.job }}-${{ matrix.python-version }}-${{ matrix.requirements-file }}
231254
include-hidden-files: true
@@ -236,15 +259,16 @@ jobs:
236259
strategy:
237260
fail-fast: false
238261
matrix:
239-
python-version: ['3.12']
262+
python-version: ['3.12', '3.13', '3.14']
240263
requirements-file: ['requirement_base_django_main.txt']
264+
postgres-version: ['16', '17', '18']
241265
os: [
242266
ubuntu-latest,
243267
]
244268

245269
services:
246270
postgres:
247-
image: postgres:15
271+
image: postgres:${{ matrix.postgres-version }}
248272
env:
249273
POSTGRES_USER: postgres
250274
POSTGRES_PASSWORD: postgres
@@ -280,9 +304,9 @@ jobs:
280304
DATABASE_URL: postgres://postgres:postgres@127.0.0.1/postgres
281305

282306
- name: Upload coverage data
283-
uses: actions/upload-artifact@v4
307+
uses: actions/upload-artifact@v5
284308
with:
285-
name: coverage-data-${{ github.job }}-${{ matrix.python-version }}-${{ matrix.requirements-file }}
309+
name: coverage-data-${{ github.job }}-${{ matrix.python-version }}-${{ matrix.requirements-file }}-postgres${{ matrix.postgres-version }}
286310
include-hidden-files: true
287311
path: '.coverage*'
288312

@@ -291,15 +315,16 @@ jobs:
291315
strategy:
292316
fail-fast: false
293317
matrix:
294-
python-version: ['3.12']
318+
python-version: ['3.12', '3.13', '3.14']
295319
requirements-file: ['requirement_base_django_main.txt']
320+
mysql-version: ['8.4', '9.5']
296321
os: [
297322
ubuntu-latest,
298323
]
299324

300325
services:
301326
mysql:
302-
image: mysql:8.4
327+
image: mysql:${{ matrix.mysql-version }}
303328
env:
304329
MYSQL_ALLOW_EMPTY_PASSWORD: yes
305330
MYSQL_DATABASE: djangocms_test
@@ -333,9 +358,9 @@ jobs:
333358
DATABASE_URL: mysql://root@127.0.0.1/djangocms_test
334359

335360
- name: Upload coverage data
336-
uses: actions/upload-artifact@v4
361+
uses: actions/upload-artifact@v5
337362
with:
338-
name: coverage-data-${{ github.job }}-${{ matrix.python-version }}-${{ matrix.requirements-file }}
363+
name: coverage-data-${{ github.job }}-${{ matrix.python-version }}-${{ matrix.requirements-file }}-mysql${{ matrix.mysql-version }}
339364
include-hidden-files: true
340365
path: '.coverage*'
341366

@@ -350,7 +375,7 @@ jobs:
350375
]
351376
strategy:
352377
matrix:
353-
python-version: ['3.12']
378+
python-version: ['3.13']
354379
os: [ ubuntu-latest, ]
355380
steps:
356381
- uses: actions/checkout@v5
@@ -364,7 +389,7 @@ jobs:
364389
run: python -m pip install --upgrade coverage[toml]
365390

366391
- name: Download data
367-
uses: actions/download-artifact@v5
392+
uses: actions/download-artifact@v6
368393
with:
369394
pattern: coverage-data-*
370395
merge-multiple: true
@@ -380,7 +405,7 @@ jobs:
380405

381406
- name: Upload HTML report (for debugging)
382407
if: ${{ failure() }}
383-
uses: actions/upload-artifact@v4
408+
uses: actions/upload-artifact@v5
384409
with:
385410
name: html-report
386411
path: htmlcov

cms/admin/utils.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def admin_list_actions(self, obj: models.Model) -> None:
9999

100100
def get_list_display(
101101
self, request: HttpRequest
102-
) -> tuple[typing.Union[str, typing.Callable[[models.Model], str]], ...]:
102+
) -> tuple[str | typing.Callable[[models.Model], str], ...]:
103103
list_display = super().get_list_display(request)
104104
return tuple(
105105
self.get_admin_list_actions(request) if item == "admin_list_actions" else item for item in list_display
@@ -165,7 +165,7 @@ class GrouperChangeListBase(ChangeList):
165165

166166
_extra_grouping_fields = []
167167

168-
def get_filters_params(self, params: typing.Optional[dict] = None):
168+
def get_filters_params(self, params: dict | None = None):
169169
lookup_params = super().get_filters_params(params)
170170
for field in self._extra_grouping_fields:
171171
if field in lookup_params:
@@ -229,7 +229,7 @@ class MyGrouperAdmin(GrouperModelAdmin):
229229
#: The name of the ``ForeignKey`` in the content model that points to the grouper instance. If not given
230230
#: it is assumed to be the snake case name of the grouper model class, e.g. ``"blog_post"`` for the
231231
#: ``"BlogPost"`` model.
232-
grouper_field_name: typing.Optional[str] = None
232+
grouper_field_name: str | None = None
233233
#: Indicates additional grouping fields such as ``"language"`` for example. Additional grouping fields create
234234
#: tabs in the change form and a dropdown menu in the change list view.
235235
#:
@@ -242,12 +242,12 @@ class MyGrouperAdmin(GrouperModelAdmin):
242242
#: The content model class to be used. Defaults to the model class named like the grouper model class
243243
#: plus ``"Content"`` at the end from the same app as the grouper model class, e.g., ``BlogPostContent`` if
244244
#: the grouper is ``BlogPost``.
245-
content_model: typing.Optional[models.Model] = None
245+
content_model: models.Model | None = None
246246
#: Name of the inverse relation field giving the set of content models belonging to a grouper model. Defaults to
247247
#: the first field found as an inverse relation. If you have more than one inverse relation please make sure
248248
#: to specify this field. An example would be if the blog post content model contained a many-to-many
249249
#: relationship to the grouper model for, say, related blog posts.
250-
content_related_field: typing.Optional[str] = None
250+
content_related_field: str | None = None
251251

252252
change_list_template = "admin/cms/grouper/change_list.html"
253253
change_form_template = "admin/cms/grouper/change_form.html"
@@ -345,7 +345,7 @@ def get_content_field(
345345
self,
346346
obj: models.Model,
347347
field_name: str,
348-
request: typing.Optional[HttpRequest] = None,
348+
request: HttpRequest | None = None,
349349
) -> typing.Any:
350350
"""Retrieves the content of a field stored in the content model. If request is given extra
351351
grouping fields are processed before."""
@@ -441,7 +441,7 @@ def get_changelist_instance(self, request: HttpRequest) -> GrouperChangeListBase
441441
def changeform_view(
442442
self,
443443
request: HttpRequest,
444-
object_id: typing.Optional[str] = None,
444+
object_id: str | None = None,
445445
form_url: str = "",
446446
extra_context: dict = None,
447447
) -> HttpResponse:
@@ -461,7 +461,7 @@ def delete_view(
461461
self,
462462
request: HttpRequest,
463463
object_id: str,
464-
extra_context: typing.Optional[dict] = None,
464+
extra_context: dict | None = None,
465465
) -> HttpResponse:
466466
"""Update grouping field properties for delete view"""
467467
self.get_grouping_from_request(request)
@@ -471,7 +471,7 @@ def history_view(
471471
self,
472472
request: HttpRequest,
473473
object_id: str,
474-
extra_context: typing.Optional[dict] = None,
474+
extra_context: dict | None = None,
475475
) -> HttpResponse:
476476
"""Update grouping field properties for history view"""
477477
self.get_grouping_from_request(request)
@@ -494,7 +494,7 @@ def get_preserved_filters(self, request: HttpRequest) -> str:
494494
preserved_filters["_changelist_filters"] = urlencode(grouping_filters)
495495
return urlencode(preserved_filters)
496496

497-
def get_extra_context(self, request: HttpRequest, object_id: typing.Optional[str] = None) -> dict[str, typing.Any]:
497+
def get_extra_context(self, request: HttpRequest, object_id: str | None = None) -> dict[str, typing.Any]:
498498
"""Provide the grouping fields to the change view."""
499499
if object_id:
500500
# Instance provided? Get corresponding postconent
@@ -539,7 +539,7 @@ def get_extra_context(self, request: HttpRequest, object_id: typing.Optional[str
539539
# TODO: Add context for other grouping fields to be shown as a dropdown
540540
return extra_context
541541

542-
def get_form(self, request: HttpRequest, obj: typing.Optional[models.Model] = None, **kwargs) -> type:
542+
def get_form(self, request: HttpRequest, obj: models.Model | None = None, **kwargs) -> type:
543543
"""Adds the language from the request to the form class"""
544544
form_class = super().get_form(request, obj, **kwargs)
545545
form_class._admin = self
@@ -616,7 +616,7 @@ def _get_content_queryset(self, obj: models.Model) -> models.QuerySet:
616616
).latest_content()
617617
return self._content_qs_cache[obj]
618618

619-
def get_content_obj(self, obj: typing.Optional[models.Model]) -> typing.Optional[models.Model]:
619+
def get_content_obj(self, obj: models.Model | None) -> models.Model | None:
620620
if obj is None or self._is_content_obj(obj):
621621
return obj
622622
else:
@@ -626,7 +626,7 @@ def get_content_obj(self, obj: typing.Optional[models.Model]) -> typing.Optional
626626
)
627627
return self._content_obj_cache[obj]
628628

629-
def get_content_objects(self, obj: typing.Optional[models.Model]) -> models.QuerySet:
629+
def get_content_objects(self, obj: models.Model | None) -> models.QuerySet:
630630
if obj is None:
631631
return None
632632
if self._is_content_obj(obj):
@@ -648,15 +648,15 @@ def get_grouper_obj(self, obj: models.Model) -> models.Model:
648648
return getattr(obj, field_name)
649649
return obj
650650

651-
def view_on_site(self, obj: models.Model) -> typing.Optional[str]:
651+
def view_on_site(self, obj: models.Model) -> str | None:
652652
# Adds the View on Site button to the admin
653653
content_obj = self.get_content_obj(obj)
654654
if content_obj:
655655
# Try getting the language from the content object
656656
return get_object_preview_url(content_obj, language=getattr(content_obj, "language", None))
657657
return None
658658

659-
def get_readonly_fields(self, request: HttpRequest, obj: typing.Optional[models.Model] = None):
659+
def get_readonly_fields(self, request: HttpRequest, obj: models.Model | None = None):
660660
"""Allow access to content fields to be controlled by a method "can_change_content":
661661
This allows versioned content to be protected if needed"""
662662
# First, get read-only fields for grouper

0 commit comments

Comments
 (0)