django-camomilla-cms 5.8.5__py2.py3-none-any.whl → 6.0.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- camomilla/__init__.py +8 -2
- camomilla/apps.py +9 -1
- camomilla/context_processors.py +6 -0
- camomilla/contrib/modeltranslation/__init__.py +0 -0
- camomilla/contrib/modeltranslation/hvad_migration.py +126 -0
- camomilla/dynamic_pages_urls.py +33 -0
- camomilla/fields/__init__.py +13 -0
- camomilla/{fields.py → fields/json.py} +15 -18
- camomilla/management/commands/regenerate_thumbnails.py +0 -1
- camomilla/managers/__init__.py +3 -0
- camomilla/managers/pages.py +116 -0
- camomilla/model_api.py +86 -0
- camomilla/models/__init__.py +5 -6
- camomilla/models/article.py +26 -44
- camomilla/models/content.py +8 -15
- camomilla/models/media.py +70 -97
- camomilla/models/menu.py +106 -0
- camomilla/models/mixins/__init__.py +10 -48
- camomilla/models/page.py +521 -20
- camomilla/openapi/__init__.py +0 -0
- camomilla/openapi/schema.py +67 -0
- camomilla/parsers.py +0 -1
- camomilla/redirects.py +10 -0
- camomilla/serializers/__init__.py +2 -0
- camomilla/serializers/article.py +5 -10
- camomilla/serializers/base/__init__.py +21 -17
- camomilla/serializers/content_type.py +17 -0
- camomilla/serializers/fields/__init__.py +6 -20
- camomilla/serializers/fields/file.py +5 -0
- camomilla/serializers/fields/related.py +24 -4
- camomilla/serializers/media.py +6 -8
- camomilla/serializers/menu.py +17 -0
- camomilla/serializers/mixins/__init__.py +23 -187
- camomilla/serializers/mixins/fields.py +20 -0
- camomilla/serializers/mixins/filter_fields.py +57 -0
- camomilla/serializers/mixins/json.py +34 -0
- camomilla/serializers/mixins/language.py +32 -0
- camomilla/serializers/mixins/nesting.py +35 -0
- camomilla/serializers/mixins/optimize.py +91 -0
- camomilla/serializers/mixins/ordering.py +34 -0
- camomilla/serializers/mixins/page.py +58 -0
- camomilla/serializers/mixins/translation.py +103 -0
- camomilla/serializers/page.py +53 -4
- camomilla/serializers/user.py +5 -4
- camomilla/serializers/utils.py +38 -0
- camomilla/serializers/validators.py +51 -0
- camomilla/settings.py +118 -0
- camomilla/sitemap.py +30 -0
- camomilla/storages/__init__.py +4 -0
- camomilla/storages/default.py +12 -0
- camomilla/storages/optimize.py +71 -0
- camomilla/{storages.py → storages/overwrite.py} +2 -2
- camomilla/templates/admin/camomilla/page/change_form.html +10 -0
- camomilla/templates/defaults/articles/default.html +7 -0
- camomilla/templates/defaults/base.html +170 -0
- camomilla/templates/defaults/pages/default.html +3 -0
- camomilla/templates/defaults/parts/langswitch.html +83 -0
- camomilla/templates/defaults/parts/menu.html +15 -0
- camomilla/templates_context/__init__.py +0 -0
- camomilla/templates_context/autodiscover.py +51 -0
- camomilla/templates_context/rendering.py +89 -0
- camomilla/templatetags/camomilla_filters.py +6 -5
- camomilla/templatetags/menus.py +37 -0
- camomilla/templatetags/model_extras.py +77 -0
- camomilla/theme/__init__.py +1 -1
- camomilla/theme/admin/__init__.py +99 -0
- camomilla/theme/admin/pages.py +46 -0
- camomilla/theme/admin/translations.py +13 -0
- camomilla/theme/apps.py +38 -0
- camomilla/theme/static/admin/css/responsive.css +5 -1021
- camomilla/theme/static/admin/img/favicon.ico +0 -0
- camomilla/theme/static/admin/img/logo.svg +31 -0
- camomilla/theme/templates/admin/base.html +7 -0
- camomilla/theme/templates/rosetta/base.html +196 -0
- camomilla/translation.py +61 -0
- camomilla/urls.py +38 -17
- camomilla/utils/__init__.py +4 -0
- camomilla/utils/getters.py +27 -0
- camomilla/utils/normalization.py +7 -0
- camomilla/utils/query_parser.py +167 -0
- camomilla/{utils.py → utils/seo.py} +13 -15
- camomilla/utils/setters.py +37 -0
- camomilla/utils/templates.py +32 -0
- camomilla/utils/translation.py +114 -0
- camomilla/views/__init__.py +1 -1
- camomilla/views/articles.py +5 -7
- camomilla/views/base/__init__.py +35 -5
- camomilla/views/contents.py +6 -11
- camomilla/views/decorators.py +26 -0
- camomilla/views/medias.py +24 -19
- camomilla/views/menus.py +81 -0
- camomilla/views/mixins/__init__.py +17 -73
- camomilla/views/mixins/bulk_actions.py +22 -0
- camomilla/views/mixins/language.py +33 -0
- camomilla/views/mixins/optimize.py +18 -0
- camomilla/views/mixins/ordering.py +2 -2
- camomilla/views/mixins/pagination.py +12 -18
- camomilla/views/mixins/permissions.py +6 -0
- camomilla/views/pages.py +28 -6
- camomilla/views/tags.py +5 -6
- camomilla/views/users.py +7 -12
- django_camomilla_cms-6.0.0.dist-info/METADATA +123 -0
- django_camomilla_cms-6.0.0.dist-info/RECORD +133 -0
- {django_camomilla_cms-5.8.5.dist-info → django_camomilla_cms-6.0.0.dist-info}/WHEEL +1 -1
- tests/fixtures/__init__.py +14 -0
- tests/test_api.py +22 -39
- tests/test_camomilla_filters.py +11 -13
- tests/test_media.py +152 -0
- tests/test_menu.py +112 -0
- tests/test_model_api.py +113 -0
- tests/test_model_api_permissions.py +44 -0
- tests/test_model_api_register.py +355 -0
- tests/test_pages.py +351 -0
- tests/test_query_parser.py +58 -0
- tests/test_templates_context.py +149 -0
- tests/test_utils.py +64 -64
- tests/utils/__init__.py +0 -0
- tests/utils/api.py +28 -0
- tests/utils/media.py +10 -0
- camomilla/admin.py +0 -98
- camomilla/migrations/0001_initial.py +0 -577
- camomilla/migrations/0002_auto_20200214_1127.py +0 -33
- camomilla/migrations/0003_auto_20210130_1610.py +0 -30
- camomilla/migrations/0004_auto_20210511_0937.py +0 -25
- camomilla/migrations/0005_media_image_props.py +0 -19
- camomilla/migrations/0006_auto_20220103_1845.py +0 -35
- camomilla/migrations/0007_auto_20220211_1622.py +0 -18
- camomilla/migrations/0008_auto_20220309_1616.py +0 -60
- camomilla/migrations/0009_article__hvad_query_category__hvad_query_and_more.py +0 -165
- camomilla/migrations/0010_auto_20220802_1406.py +0 -83
- camomilla/migrations/0011_auto_20220902_1000.py +0 -15
- camomilla/models/category.py +0 -25
- camomilla/models/tag.py +0 -19
- camomilla/theme/static/admin/img/logo.png +0 -0
- camomilla/theme/templates/admin/base_site.html +0 -18
- camomilla/views/categories.py +0 -13
- django_camomilla_cms-5.8.5.dist-info/METADATA +0 -62
- django_camomilla_cms-5.8.5.dist-info/RECORD +0 -76
- tests/urls.py +0 -21
- /camomilla/{migrations → contrib}/__init__.py +0 -0
- /camomilla/templates/{camomilla → defaults}/widgets/media_select_multiple.html +0 -0
- {django_camomilla_cms-5.8.5.dist-info → django_camomilla_cms-6.0.0.dist-info/licenses}/LICENSE +0 -0
- {django_camomilla_cms-5.8.5.dist-info → django_camomilla_cms-6.0.0.dist-info}/top_level.txt +0 -0
tests/test_pages.py
ADDED
@@ -0,0 +1,351 @@
|
|
1
|
+
import pytest
|
2
|
+
from django.test import TestCase
|
3
|
+
from rest_framework.test import APIClient
|
4
|
+
from .utils.api import login_superuser
|
5
|
+
from camomilla.models import Page
|
6
|
+
from camomilla.models.page import UrlRedirect
|
7
|
+
from datetime import datetime, timedelta
|
8
|
+
|
9
|
+
|
10
|
+
class PagesTestCase(TestCase):
|
11
|
+
def setUp(self):
|
12
|
+
self.client = APIClient()
|
13
|
+
token = login_superuser()
|
14
|
+
self.client.credentials(HTTP_AUTHORIZATION="Token " + token)
|
15
|
+
|
16
|
+
@pytest.mark.django_db
|
17
|
+
def test_pages_api_no_access(self):
|
18
|
+
client = APIClient()
|
19
|
+
response = client.post("/api/camomilla/pages/")
|
20
|
+
assert response.status_code == 401
|
21
|
+
|
22
|
+
@pytest.mark.django_db
|
23
|
+
def test_pages_api_crud(self):
|
24
|
+
# Create page
|
25
|
+
response = self.client.post(
|
26
|
+
"/api/camomilla/pages/",
|
27
|
+
{
|
28
|
+
"translations": {
|
29
|
+
"it": {
|
30
|
+
"title": "title_page_1",
|
31
|
+
}
|
32
|
+
}
|
33
|
+
},
|
34
|
+
format="json",
|
35
|
+
)
|
36
|
+
|
37
|
+
assert response.status_code == 201
|
38
|
+
assert len(Page.objects.all()) == 1
|
39
|
+
page = Page.objects.first()
|
40
|
+
assert page.id == 1
|
41
|
+
assert page.title_it == "title_page_1"
|
42
|
+
assert page.url_node.id == 1
|
43
|
+
|
44
|
+
response = self.client.post(
|
45
|
+
"/api/camomilla/pages/",
|
46
|
+
{
|
47
|
+
"title_it": "title_page_2",
|
48
|
+
},
|
49
|
+
)
|
50
|
+
|
51
|
+
assert response.status_code == 201
|
52
|
+
assert len(Page.objects.all()) == 2
|
53
|
+
page = Page.objects.last()
|
54
|
+
assert page.id == 2
|
55
|
+
assert page.title_it == "title_page_2"
|
56
|
+
assert page.url_node.id == 2
|
57
|
+
|
58
|
+
# Update page
|
59
|
+
response = self.client.patch(
|
60
|
+
"/api/camomilla/pages/2/",
|
61
|
+
{
|
62
|
+
"translations": {
|
63
|
+
"it": {
|
64
|
+
"title": "title_page_2_updated",
|
65
|
+
}
|
66
|
+
}
|
67
|
+
},
|
68
|
+
format="json",
|
69
|
+
)
|
70
|
+
|
71
|
+
assert response.status_code == 200
|
72
|
+
assert len(Page.objects.all()) == 2
|
73
|
+
page = Page.objects.last()
|
74
|
+
assert page.id == 2
|
75
|
+
assert page.title_it == "title_page_2_updated"
|
76
|
+
|
77
|
+
# Read page
|
78
|
+
response = self.client.get("/api/camomilla/pages/2/")
|
79
|
+
|
80
|
+
assert response.status_code == 200
|
81
|
+
assert response.json()["id"] == 2
|
82
|
+
assert response.json()["title"] == "title_page_2_updated"
|
83
|
+
|
84
|
+
# Read pages
|
85
|
+
response = self.client.get("/api/camomilla/pages/")
|
86
|
+
|
87
|
+
assert response.status_code == 200
|
88
|
+
assert response.json()[0]["id"] == 1
|
89
|
+
assert response.json()[0]["title"] == "title_page_1"
|
90
|
+
assert response.json()[1]["id"] == 2
|
91
|
+
assert response.json()[1]["title"] == "title_page_2_updated"
|
92
|
+
|
93
|
+
# Delete page
|
94
|
+
response = self.client.delete("/api/camomilla/pages/2/")
|
95
|
+
|
96
|
+
assert response.status_code == 204
|
97
|
+
assert len(Page.objects.all()) == 1
|
98
|
+
page = Page.objects.last()
|
99
|
+
assert page.id == 1
|
100
|
+
assert page.title_it == "title_page_1"
|
101
|
+
|
102
|
+
@pytest.mark.django_db
|
103
|
+
def test_pages_url_nodes(self):
|
104
|
+
# Create page with automatic url creation
|
105
|
+
response = self.client.post(
|
106
|
+
"/api/camomilla/pages/",
|
107
|
+
{
|
108
|
+
"title_en": "title_page_1",
|
109
|
+
"title_it": "titolo_pagina_1",
|
110
|
+
},
|
111
|
+
)
|
112
|
+
|
113
|
+
assert response.status_code == 201
|
114
|
+
|
115
|
+
# EN automatic url creation
|
116
|
+
response = self.client.get("/api/camomilla/pages/1/?language=en")
|
117
|
+
assert response.json()["autopermalink"] == True
|
118
|
+
assert response.json()["permalink"] == "/title_page_1"
|
119
|
+
# IT automatic url creation
|
120
|
+
response = self.client.get("/api/camomilla/pages/1/?language=it")
|
121
|
+
assert response.json()["autopermalink"] == True
|
122
|
+
assert response.json()["permalink"] == "/titolo_pagina_1"
|
123
|
+
|
124
|
+
# Create page with manual url creation
|
125
|
+
response = self.client.post(
|
126
|
+
"/api/camomilla/pages/",
|
127
|
+
{
|
128
|
+
"translations": {
|
129
|
+
"it": {
|
130
|
+
"title": "titolo_pagina_2",
|
131
|
+
"permalink": "permalink_manuale_it_2",
|
132
|
+
"autopermalink": False,
|
133
|
+
},
|
134
|
+
"en": {
|
135
|
+
"title": "title_page_2",
|
136
|
+
"permalink": "permalink_manual_en_2",
|
137
|
+
"autopermalink": False,
|
138
|
+
},
|
139
|
+
}
|
140
|
+
},
|
141
|
+
format="json",
|
142
|
+
)
|
143
|
+
assert response.status_code == 201
|
144
|
+
|
145
|
+
# EN manual url creation
|
146
|
+
response = self.client.get("/api/camomilla/pages/2/?language=en")
|
147
|
+
assert response.json()["autopermalink"] == False
|
148
|
+
assert response.json()["permalink"] == "/permalink_manual_en_2"
|
149
|
+
# IT manual url creation
|
150
|
+
response = self.client.get("/api/camomilla/pages/2/?language=it")
|
151
|
+
assert response.json()["autopermalink"] == False
|
152
|
+
assert response.json()["permalink"] == "/permalink_manuale_it_2"
|
153
|
+
|
154
|
+
# Create page with a parent page with automatic url creation
|
155
|
+
response = self.client.post(
|
156
|
+
"/api/camomilla/pages/",
|
157
|
+
{
|
158
|
+
"title_en": "title_page_3",
|
159
|
+
"title_it": "titolo_pagina_3",
|
160
|
+
"parent_page": 2,
|
161
|
+
},
|
162
|
+
)
|
163
|
+
assert response.status_code == 201
|
164
|
+
|
165
|
+
# EN parent page with automatic url creation
|
166
|
+
response = self.client.get("/api/camomilla/pages/3/?language=en")
|
167
|
+
assert response.json()["autopermalink"] == True
|
168
|
+
assert response.json()["permalink"] == "//permalink_manual_en_2/title_page_3"
|
169
|
+
# IT parent page with automatic url creation
|
170
|
+
response = self.client.get("/api/camomilla/pages/3/?language=it")
|
171
|
+
assert response.json()["autopermalink"] == True
|
172
|
+
assert (
|
173
|
+
response.json()["permalink"] == "//permalink_manuale_it_2/titolo_pagina_3"
|
174
|
+
)
|
175
|
+
|
176
|
+
# Check url uniqueness and consistency EN
|
177
|
+
response = self.client.post(
|
178
|
+
"/api/camomilla/pages/",
|
179
|
+
{
|
180
|
+
"autopermalink_en": False,
|
181
|
+
"permalink_en": "permalink_manual_en_2",
|
182
|
+
},
|
183
|
+
)
|
184
|
+
|
185
|
+
# Client error when url check uniqueness and consistency fail
|
186
|
+
assert response.status_code == 400
|
187
|
+
assert (
|
188
|
+
response.data["permalink_en"][0]
|
189
|
+
== "There is an other page with same permalink."
|
190
|
+
)
|
191
|
+
|
192
|
+
# Check url uniqueness and consistency IT
|
193
|
+
response = self.client.post(
|
194
|
+
"/api/camomilla/pages/",
|
195
|
+
{
|
196
|
+
"translations": {
|
197
|
+
"it": {
|
198
|
+
"autopermalink": False,
|
199
|
+
"permalink": "permalink_manuale_it_2",
|
200
|
+
}
|
201
|
+
}
|
202
|
+
},
|
203
|
+
format="json",
|
204
|
+
)
|
205
|
+
|
206
|
+
# Client error when url check uniqueness and consistency fail
|
207
|
+
assert response.status_code == 400
|
208
|
+
assert (
|
209
|
+
response.data["permalink_it"][0]
|
210
|
+
== "There is an other page with same permalink."
|
211
|
+
)
|
212
|
+
|
213
|
+
@pytest.mark.django_db
|
214
|
+
def test_pages_url_nodes_navigation(self):
|
215
|
+
# Test the camomilla.dynamic_pages_url handler for navigating and rendering UrlNodes
|
216
|
+
self.client.post(
|
217
|
+
"/api/camomilla/pages/",
|
218
|
+
{
|
219
|
+
"autopermalink_en": False,
|
220
|
+
"permalink_en": "permalink_4_en",
|
221
|
+
"status_en": "PUB",
|
222
|
+
"autopermalink_it": False,
|
223
|
+
"permalink_it": "permalink_4_it",
|
224
|
+
"status_it": "PUB",
|
225
|
+
},
|
226
|
+
)
|
227
|
+
|
228
|
+
response = self.client.get("/permalink_4_en/")
|
229
|
+
assert response.status_code == 200
|
230
|
+
response = self.client.get("/it/permalink_4_it/")
|
231
|
+
assert response.status_code == 200
|
232
|
+
|
233
|
+
# Test draft - published - planned and ?preview=true
|
234
|
+
self.client.post(
|
235
|
+
"/api/camomilla/pages/",
|
236
|
+
{
|
237
|
+
"translations": {
|
238
|
+
"it": {
|
239
|
+
"autopermalink": False,
|
240
|
+
"permalink": "permalink_5_it",
|
241
|
+
"status": "PLA",
|
242
|
+
},
|
243
|
+
"en": {
|
244
|
+
"autopermalink": False,
|
245
|
+
"permalink": "permalink_5_en",
|
246
|
+
"status": "DRF",
|
247
|
+
},
|
248
|
+
}
|
249
|
+
},
|
250
|
+
format="json",
|
251
|
+
)
|
252
|
+
|
253
|
+
response = self.client.get("/permalink_5_en/")
|
254
|
+
assert response.status_code == 404
|
255
|
+
response = self.client.get("/permalink_5_en/?preview=true")
|
256
|
+
assert response.status_code == 200
|
257
|
+
response = self.client.get("/it/permalink_5_it/")
|
258
|
+
assert response.status_code == 404
|
259
|
+
|
260
|
+
self.client.patch(
|
261
|
+
"/api/camomilla/pages/2/",
|
262
|
+
{
|
263
|
+
"publication_date": (datetime.now() - timedelta(1)).strftime("%Y-%m-%d")
|
264
|
+
+ " 00:00:00",
|
265
|
+
},
|
266
|
+
)
|
267
|
+
|
268
|
+
response = self.client.get("/it/permalink_5_it/")
|
269
|
+
assert response.status_code == 200
|
270
|
+
|
271
|
+
@pytest.mark.django_db
|
272
|
+
def test_pages_url_nodes_navigation_redirects(self):
|
273
|
+
# Test the camomilla.dynamic_pages_url handler for navigating and rendering UrlNodes
|
274
|
+
self.client.post(
|
275
|
+
"/api/camomilla/pages/",
|
276
|
+
{
|
277
|
+
"translations": {
|
278
|
+
"it": {
|
279
|
+
"autopermalink": False,
|
280
|
+
"permalink": "permalink_6_it",
|
281
|
+
"status": "PUB",
|
282
|
+
},
|
283
|
+
"en": {
|
284
|
+
"autopermalink": False,
|
285
|
+
"permalink": "permalink_6_en",
|
286
|
+
"status": "PUB",
|
287
|
+
},
|
288
|
+
}
|
289
|
+
},
|
290
|
+
format="json",
|
291
|
+
)
|
292
|
+
|
293
|
+
# EN Insert Moved Permanently Redirect
|
294
|
+
url_redirect = UrlRedirect.objects.create(
|
295
|
+
language_code="en",
|
296
|
+
from_url="/redirecting_1",
|
297
|
+
to_url="/redirected_1",
|
298
|
+
url_node_id=1,
|
299
|
+
)
|
300
|
+
|
301
|
+
response = self.client.get("/redirecting_1/")
|
302
|
+
assert response.status_code == 301
|
303
|
+
assert response.url == "/redirected_1/"
|
304
|
+
|
305
|
+
# EN Change to Moved Temporarily Redirect
|
306
|
+
url_redirect.permanent = False
|
307
|
+
url_redirect.save()
|
308
|
+
response = self.client.get("/redirecting_1/")
|
309
|
+
assert response.status_code == 302
|
310
|
+
|
311
|
+
# IT Insert Moved Permanently Redirect
|
312
|
+
url_redirect = UrlRedirect.objects.create(
|
313
|
+
language_code="it",
|
314
|
+
from_url="/urlreindirizzamento_1",
|
315
|
+
to_url="/urlreindirizzato_1",
|
316
|
+
url_node_id=1,
|
317
|
+
)
|
318
|
+
|
319
|
+
response = self.client.get("/it/urlreindirizzamento_1/")
|
320
|
+
assert response.status_code == 301
|
321
|
+
assert response.url == "/it/urlreindirizzato_1/"
|
322
|
+
|
323
|
+
# IT Change to Moved Temporarily Redirect
|
324
|
+
url_redirect.permanent = False
|
325
|
+
url_redirect.save()
|
326
|
+
response = self.client.get("/it/urlreindirizzamento_1/")
|
327
|
+
assert response.status_code == 302
|
328
|
+
|
329
|
+
# Test auto redirect after permalink change
|
330
|
+
self.client.patch(
|
331
|
+
"/api/camomilla/pages/1/",
|
332
|
+
{
|
333
|
+
"translations": {
|
334
|
+
"it": {
|
335
|
+
"permalink": "permalink_6_it_changed",
|
336
|
+
},
|
337
|
+
"en": {
|
338
|
+
"permalink": "permalink_6_en_changed",
|
339
|
+
},
|
340
|
+
}
|
341
|
+
},
|
342
|
+
format="json",
|
343
|
+
)
|
344
|
+
|
345
|
+
response = self.client.get("/permalink_6_en/")
|
346
|
+
assert response.status_code == 301
|
347
|
+
assert response.url == "/permalink_6_en_changed/"
|
348
|
+
|
349
|
+
response = self.client.get("/it/permalink_6_it/")
|
350
|
+
assert response.status_code == 301
|
351
|
+
assert response.url == "/it/permalink_6_it_changed/"
|
@@ -0,0 +1,58 @@
|
|
1
|
+
import pytest
|
2
|
+
from django.db.models import Q
|
3
|
+
from camomilla.utils.query_parser import (
|
4
|
+
ConditionParser,
|
5
|
+
)
|
6
|
+
|
7
|
+
|
8
|
+
@pytest.mark.parametrize(
|
9
|
+
"query, expected_q",
|
10
|
+
[
|
11
|
+
# Single condition
|
12
|
+
("name__icontains=foo", Q(name__icontains="foo")),
|
13
|
+
# Multiple conditions with AND
|
14
|
+
(
|
15
|
+
"name__icontains=foo AND age__gt=21",
|
16
|
+
Q(name__icontains="foo") & Q(age__gt=21),
|
17
|
+
),
|
18
|
+
# Multiple conditions with OR
|
19
|
+
(
|
20
|
+
"name__icontains=foo OR name__icontains=bar",
|
21
|
+
Q(name__icontains="foo") | Q(name__icontains="bar"),
|
22
|
+
),
|
23
|
+
# Mixed AND and OR conditions
|
24
|
+
(
|
25
|
+
"name__icontains=foo OR (age__gt=21 AND city__iexact='New York')",
|
26
|
+
Q(name__icontains="foo") | (Q(age__gt=21) & Q(city__iexact="New York")),
|
27
|
+
),
|
28
|
+
# Nested parentheses with OR inside AND
|
29
|
+
(
|
30
|
+
"((name__icontains=foo) OR (name__icontains=bar)) AND name__icontains=baz",
|
31
|
+
(Q(name__icontains="foo") | Q(name__icontains="bar"))
|
32
|
+
& Q(name__icontains="baz"),
|
33
|
+
),
|
34
|
+
# Complex nested conditions with multiple OR and AND
|
35
|
+
(
|
36
|
+
"((name__icontains=foo) OR (name__icontains=bar) OR (name__icontains=buz)) AND age__gt=25 AND city__iexact='LA'",
|
37
|
+
(
|
38
|
+
Q(name__icontains="foo")
|
39
|
+
| Q(name__icontains="bar")
|
40
|
+
| Q(name__icontains="buz")
|
41
|
+
)
|
42
|
+
& Q(age__gt=25)
|
43
|
+
& Q(city__iexact="LA"),
|
44
|
+
),
|
45
|
+
# Single condition with no parentheses
|
46
|
+
("age__lte=30", Q(age__lte=30)),
|
47
|
+
# Simple nested OR and AND with more levels of nesting
|
48
|
+
(
|
49
|
+
"(name__icontains=foo AND (age__gt=21 OR city__iexact='New York')) OR country__iexact='US'",
|
50
|
+
(Q(name__icontains="foo") & (Q(age__gt=21) | Q(city__iexact="New York")))
|
51
|
+
| Q(country__iexact="US"),
|
52
|
+
),
|
53
|
+
],
|
54
|
+
)
|
55
|
+
def test_condition_parser(query, expected_q):
|
56
|
+
parser = ConditionParser(query)
|
57
|
+
q_object = parser.parse_to_q()
|
58
|
+
assert q_object.__str__() == expected_q.__str__()
|
@@ -0,0 +1,149 @@
|
|
1
|
+
import pytest
|
2
|
+
import json
|
3
|
+
import re
|
4
|
+
from django.test import TestCase
|
5
|
+
from rest_framework.test import APIClient
|
6
|
+
from .utils.api import login_superuser
|
7
|
+
from .utils.media import load_asset_and_remove_media
|
8
|
+
|
9
|
+
|
10
|
+
class TemoplateContextTestCase(TestCase):
|
11
|
+
def setUp(self):
|
12
|
+
self.client = APIClient()
|
13
|
+
token = login_superuser()
|
14
|
+
self.client.credentials(HTTP_AUTHORIZATION="Token " + token)
|
15
|
+
|
16
|
+
@pytest.mark.django_db
|
17
|
+
def test_page_context_template_based(self):
|
18
|
+
# Create page with custom context template
|
19
|
+
response = self.client.post(
|
20
|
+
"/api/camomilla/pages/",
|
21
|
+
{
|
22
|
+
"title_en": "Page custom context template",
|
23
|
+
"autopermalink_en": False,
|
24
|
+
"permalink_en": "permalink_context_template",
|
25
|
+
"template": "website/page_context_template_based.html",
|
26
|
+
"status_en": "PUB",
|
27
|
+
},
|
28
|
+
format="multipart",
|
29
|
+
)
|
30
|
+
assert response.status_code == 201
|
31
|
+
|
32
|
+
# Create media for custom context
|
33
|
+
asset = load_asset_and_remove_media("10595073.png")
|
34
|
+
response = self.client.post(
|
35
|
+
"/api/camomilla/media/",
|
36
|
+
{
|
37
|
+
"file": asset,
|
38
|
+
"data": json.dumps(
|
39
|
+
{
|
40
|
+
"translations": {
|
41
|
+
"en": {
|
42
|
+
"alt_text": "Test media",
|
43
|
+
"title": "Test media",
|
44
|
+
"description": "Description media",
|
45
|
+
}
|
46
|
+
}
|
47
|
+
}
|
48
|
+
),
|
49
|
+
},
|
50
|
+
format="multipart",
|
51
|
+
)
|
52
|
+
assert response.status_code == 201
|
53
|
+
|
54
|
+
response = self.client.get("/permalink_context_template/")
|
55
|
+
assert response.status_code == 200
|
56
|
+
assert (
|
57
|
+
re.sub(r"[\s+]", "", response.content.decode())
|
58
|
+
== "<!DOCTYPEhtml><html><body><h1>Titlepageforpagecontexttemplatebased</h1><p>Contentpageforpagecontexttemplatebased</p><ul><li>Testmedia</li></ul></body></html>"
|
59
|
+
)
|
60
|
+
|
61
|
+
@pytest.mark.django_db
|
62
|
+
def test_model_context_template_based(self):
|
63
|
+
# Create page with custom context template
|
64
|
+
response = self.client.post(
|
65
|
+
"/api/camomilla/pages/",
|
66
|
+
{
|
67
|
+
"title_en": "Page custom context template",
|
68
|
+
"autopermalink_en": False,
|
69
|
+
"permalink_en": "permalink_context_template",
|
70
|
+
"template": "website/page_context_model_based.html",
|
71
|
+
"status_en": "PUB",
|
72
|
+
},
|
73
|
+
format="multipart",
|
74
|
+
)
|
75
|
+
assert response.status_code == 201
|
76
|
+
|
77
|
+
# Create media for custom context
|
78
|
+
asset = load_asset_and_remove_media("10595073.png")
|
79
|
+
response = self.client.post(
|
80
|
+
"/api/camomilla/media/",
|
81
|
+
{
|
82
|
+
"file": asset,
|
83
|
+
"data": json.dumps(
|
84
|
+
{
|
85
|
+
"translations": {
|
86
|
+
"en": {
|
87
|
+
"alt_text": "Test media",
|
88
|
+
"title": "Test media",
|
89
|
+
"description": "Description media",
|
90
|
+
}
|
91
|
+
}
|
92
|
+
}
|
93
|
+
),
|
94
|
+
},
|
95
|
+
format="multipart",
|
96
|
+
)
|
97
|
+
assert response.status_code == 201
|
98
|
+
|
99
|
+
response = self.client.get("/permalink_context_template/")
|
100
|
+
assert response.status_code == 200
|
101
|
+
assert (
|
102
|
+
re.sub(r"[\s+]", "", response.content.decode())
|
103
|
+
== "<!DOCTYPEhtml><html><body><h1>Titlepageforpagecontextmodelbased</h1><p>Contentpageforpagecontextmodelbased</p><ul><li>Testmedia</li></ul></body></html>"
|
104
|
+
)
|
105
|
+
|
106
|
+
@pytest.mark.django_db
|
107
|
+
def test_mixed_context_template(self):
|
108
|
+
# Create page with custom context template
|
109
|
+
response = self.client.post(
|
110
|
+
"/api/camomilla/pages/",
|
111
|
+
{
|
112
|
+
"title_en": "Page custom context template",
|
113
|
+
"autopermalink_en": False,
|
114
|
+
"permalink_en": "permalink_context_template",
|
115
|
+
"template": "website/page_context_mixed.html",
|
116
|
+
"status_en": "PUB",
|
117
|
+
},
|
118
|
+
format="multipart",
|
119
|
+
)
|
120
|
+
assert response.status_code == 201
|
121
|
+
|
122
|
+
# Create media for custom context
|
123
|
+
asset = load_asset_and_remove_media("10595073.png")
|
124
|
+
response = self.client.post(
|
125
|
+
"/api/camomilla/media/",
|
126
|
+
{
|
127
|
+
"file": asset,
|
128
|
+
"data": json.dumps(
|
129
|
+
{
|
130
|
+
"translations": {
|
131
|
+
"en": {
|
132
|
+
"alt_text": "Test media",
|
133
|
+
"title": "Test media",
|
134
|
+
"description": "Description media",
|
135
|
+
}
|
136
|
+
}
|
137
|
+
}
|
138
|
+
),
|
139
|
+
},
|
140
|
+
format="multipart",
|
141
|
+
)
|
142
|
+
assert response.status_code == 201
|
143
|
+
|
144
|
+
response = self.client.get("/permalink_context_template/")
|
145
|
+
assert response.status_code == 200
|
146
|
+
assert (
|
147
|
+
re.sub(r"[\s+]", "", response.content.decode())
|
148
|
+
== "<!DOCTYPEhtml><html><body><!--Templatecontext--><h1>Titlepageforpagecontexttemplatebased</h1><p>Contentpageforpagecontexttemplatebased</p><ul><li>Testmedia</li></ul><!--Modelcontext--><h1>Titlepageforpagecontextmodelbased</h1><p>Contentpageforpagecontextmodelbased</p><ul><li>Testmedia</li></ul></body></html>"
|
149
|
+
)
|