django-camomilla-cms 6.0.0b14__py2.py3-none-any.whl → 6.0.0b16__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.
Files changed (51) hide show
  1. camomilla/__init__.py +1 -1
  2. camomilla/dynamic_pages_urls.py +2 -1
  3. camomilla/fields/__init__.py +1 -3
  4. camomilla/managers/pages.py +22 -33
  5. camomilla/model_api.py +9 -4
  6. camomilla/models/media.py +1 -1
  7. camomilla/models/menu.py +23 -11
  8. camomilla/models/page.py +76 -38
  9. camomilla/openapi/schema.py +4 -0
  10. camomilla/serializers/base/__init__.py +3 -1
  11. camomilla/serializers/fields/__init__.py +2 -2
  12. camomilla/serializers/fields/json.py +2 -2
  13. camomilla/serializers/fields/related.py +5 -1
  14. camomilla/serializers/mixins/__init__.py +56 -18
  15. camomilla/serializers/mixins/filter_fields.py +56 -0
  16. camomilla/serializers/utils.py +3 -1
  17. camomilla/serializers/validators.py +9 -5
  18. camomilla/settings.py +0 -4
  19. camomilla/storages/default.py +6 -0
  20. camomilla/storages/optimize.py +2 -2
  21. camomilla/storages/overwrite.py +2 -2
  22. camomilla/templates/defaults/parts/menu.html +1 -1
  23. camomilla/theme/__init__.py +1 -1
  24. camomilla/theme/admin.py +1 -1
  25. camomilla/translation.py +1 -1
  26. camomilla/utils/query_parser.py +148 -0
  27. camomilla/utils/setters.py +37 -0
  28. camomilla/views/base/__init__.py +2 -2
  29. camomilla/views/menus.py +0 -3
  30. camomilla/views/mixins/__init__.py +9 -2
  31. camomilla/views/mixins/pagination.py +4 -13
  32. {django_camomilla_cms-6.0.0b14.dist-info → django_camomilla_cms-6.0.0b16.dist-info}/METADATA +2 -3
  33. {django_camomilla_cms-6.0.0b14.dist-info → django_camomilla_cms-6.0.0b16.dist-info}/RECORD +46 -40
  34. {django_camomilla_cms-6.0.0b14.dist-info → django_camomilla_cms-6.0.0b16.dist-info}/WHEEL +1 -1
  35. tests/fixtures/__init__.py +17 -0
  36. tests/test_api.py +2 -11
  37. tests/test_camomilla_filters.py +7 -13
  38. tests/test_media.py +80 -0
  39. tests/test_model_api.py +68 -0
  40. tests/test_model_api_permissions.py +39 -0
  41. tests/test_query_parser.py +59 -0
  42. tests/test_utils.py +64 -64
  43. tests/utils/__init__.py +0 -0
  44. tests/utils/api.py +29 -0
  45. camomilla/structured/__init__.py +0 -125
  46. camomilla/structured/cache.py +0 -202
  47. camomilla/structured/fields.py +0 -150
  48. camomilla/structured/models.py +0 -47
  49. camomilla/structured/utils.py +0 -114
  50. {django_camomilla_cms-6.0.0b14.dist-info → django_camomilla_cms-6.0.0b16.dist-info}/LICENSE +0 -0
  51. {django_camomilla_cms-6.0.0b14.dist-info → django_camomilla_cms-6.0.0b16.dist-info}/top_level.txt +0 -0
@@ -1,38 +1,38 @@
1
- camomilla/__init__.py,sha256=QPq2GvO5-zT08-jIOFOjDeg33wtHP-Q3zFfpvPWr-YM,251
1
+ camomilla/__init__.py,sha256=zHhnT2CrQaDldn3CyDbBxGqY4L0nbxsbMQLr6arZrLs,251
2
2
  camomilla/apps.py,sha256=eUwb9ynyiRAc5OXgt7ZsAdhsCOnPCpNdIFYMheNeN-o,532
3
3
  camomilla/authentication.py,sha256=jz6tQT4PPEu-_JLox1LZrOy7EiWBb9MWaObK63MJGus,855
4
4
  camomilla/context_processors.py,sha256=cGowjDZ-oDGYn1j2Pj5QDGCqnzXAOdOwp5dmzin_FTc,165
5
5
  camomilla/defaults.py,sha256=VNQ_sbxu09AyFGNpUUYypIAyhlBhEORD36BBNj7e73I,1220
6
- camomilla/dynamic_pages_urls.py,sha256=HBI8gCe0Vd9mE74QrIVP57K4ThWopEW4r7jZnFHulTk,1008
6
+ camomilla/dynamic_pages_urls.py,sha256=14H47KlSxmINoJyrsul0KR7Qvk6-1uoXrD1ReV_W4h8,1056
7
7
  camomilla/exceptions.py,sha256=gLniAsK_pmsNNKGMv5Z384LXVbM8oeHcOwz4F91u1LY,111
8
- camomilla/model_api.py,sha256=VC3tNQucS3-KVtdZNWkkbkJn8vtl_cBKYTcC3QU6PpU,2253
8
+ camomilla/model_api.py,sha256=1xQxDWGPQNVqa_c87pLq4gj1ZHta2IFqdeEbzErHZX8,2447
9
9
  camomilla/parsers.py,sha256=fL8XGCGPxJIZNZkPdGtnPSbDP-6-yzGOCVMuLPjkx9Y,1975
10
10
  camomilla/permissions.py,sha256=9NlBO4JMmg36vXCUjPNyq6uZxhkdrnXyIbJVLtWhGWE,1813
11
- camomilla/settings.py,sha256=Xxs6_fcx-0b-5yKZL7XqYwan48dxIzQmm2lxuijvld0,3428
11
+ camomilla/settings.py,sha256=GDJXDcEmyobGrP4MmEH3MAt1KBK74gXmmRR8kFuJzp0,3306
12
12
  camomilla/sitemap.py,sha256=U2t5TwhB_-sEscmQZ69PZ5st3bIap8NRxzWEvCgB130,786
13
- camomilla/translation.py,sha256=IDoC48xhH49lFAbOq19kOskgjECv4kRCm7bEI80jOL4,1260
13
+ camomilla/translation.py,sha256=-Y0Toy1D__wxMovipWVIuNx3TrfhDGffrduWYFUmYA4,1269
14
14
  camomilla/urls.py,sha256=YFUYcPuvnNSTnfJa_JByRWAPLgc4_R4LgqCMrmkbFpE,2217
15
15
  camomilla/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  camomilla/contrib/modeltranslation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  camomilla/contrib/modeltranslation/hvad_migration.py,sha256=UanLVSlYYd_ykMC4STtYkkUMJqlFQQYhbWkoV47alM0,5403
18
18
  camomilla/contrib/rest_framework/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  camomilla/contrib/rest_framework/serializer.py,sha256=G6SKes-8RkJKDupCNN0B2B1BQuYqS0y820oGYotxPcY,5924
20
- camomilla/fields/__init__.py,sha256=VTCKK0PsJxpcgWrcqPYtWaSFhOOO2ZL56UIdla2IxF8,400
20
+ camomilla/fields/__init__.py,sha256=gKrJwHvUA3q_wu-OVV0hrRZmFkT4znMHrmZtpriDumw,323
21
21
  camomilla/fields/json.py,sha256=tWEDn6kwTP6pNB53djxuVPu2d57m9cIDc4ccCEfUbDQ,1938
22
22
  camomilla/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
23
  camomilla/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
24
  camomilla/management/commands/regenerate_thumbnails.py,sha256=pKToASR8p8TJezGpFfuylsAHtriNueJ7xqJJxq55adY,496
25
25
  camomilla/managers/__init__.py,sha256=Zwp28E1RKafIl0FqcUi1YNHxF19IsMIvhlhS-Njg9Mw,60
26
- camomilla/managers/pages.py,sha256=6L35RPzfJbEntjWfEzw0b9ruhOwoVaITUD-RyxuK5yc,1857
26
+ camomilla/managers/pages.py,sha256=ifwYuOCaX8pf1-mR71IXuE5KDl2UWtcuwmGV26Hi9EU,1129
27
27
  camomilla/models/__init__.py,sha256=y7Q34AGhQ1weJUKWb6XjiyUbwRnJeylOBGMErU0wqYg,147
28
28
  camomilla/models/article.py,sha256=LgkZgRsubtDV6NwBz8E2bIgKD6H3I-1QLAxEan5TYYs,1139
29
29
  camomilla/models/content.py,sha256=mIgtifb_WMIt58we5u6qWZemHvuDN1zZaBeCyzHL78A,956
30
- camomilla/models/media.py,sha256=7TD_0nhNoi8P-Gmsr0Kag4eGaxMXuTfQOFl53Mb03cQ,6854
31
- camomilla/models/menu.py,sha256=rCq0MfHLGM7-7fDVk--AixQZb6_C1gkWMWffKs6QnK4,2841
32
- camomilla/models/page.py,sha256=X4Mo4cTbAYyWlpVFtjRjyvI_wE2-zcBU1MR8mm26NQI,15671
30
+ camomilla/models/media.py,sha256=pD-qldiHDOOHgux4lsivQLBcOJJrRx3a4Bg8ODNx7r0,6852
31
+ camomilla/models/menu.py,sha256=zXL644mQLRxt5aLZKv66l4RGlEi-a33cwmq7xpD9S5E,3661
32
+ camomilla/models/page.py,sha256=jMtnJ_leps6hnpZ52Ot_nXlgqUO5lUFshUzpYJtrRjk,17237
33
33
  camomilla/models/mixins/__init__.py,sha256=c2NixqvrIX4E9WGRqQbylXlqBWDXEqN9mzs_dpB0hFQ,1248
34
34
  camomilla/openapi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
- camomilla/openapi/schema.py,sha256=kYNtIKnLx4SIj893umIE6u-q3HH9hYH9O4zdZCSOe7U,2100
35
+ camomilla/openapi/schema.py,sha256=08HaV3-X-96b15Bo1X9IJmT2bv-bCqOkVrmWpz-fesM,2381
36
36
  camomilla/serializers/__init__.py,sha256=8v1GsJ_YZ6T72VnKBb5l-8K93oaLf4PIsMt-yFtK-Gk,176
37
37
  camomilla/serializers/article.py,sha256=pYVcS0KztzjzSqgruElQMMEZcqTzmQUqXrdv_Sx5Az4,401
38
38
  camomilla/serializers/content_type.py,sha256=qB2wkmkvQI6LHxfSI6auEh6M9cJRFBaHnpmkBCCzeYo,557
@@ -40,28 +40,25 @@ camomilla/serializers/media.py,sha256=H4JVpRVxXVmn_BiqrjihKXpfLm9fLmHDFIICRDGJU4
40
40
  camomilla/serializers/menu.py,sha256=TdoyXs40PqxNevnRbBbYOOX9rUv9zQGiHFNduspaZnw,552
41
41
  camomilla/serializers/page.py,sha256=NNjEypVYu_9iKqdHV_-61ea37gxiHlDP5gsloV_i6yg,1834
42
42
  camomilla/serializers/user.py,sha256=CzrHiVRvYYWNE4eNpCNKtJB7DjVqHHwIcP4NUBXMHSo,3706
43
- camomilla/serializers/utils.py,sha256=sE6Fe8vgGuEWYTox7_ATC6zwcUZ7t2EqkemuP5tWp00,1001
44
- camomilla/serializers/validators.py,sha256=QtlmGyJiKaKyMBqBZ1W2VAjCoLi1vhPUnLOo7DvZpsM,1775
45
- camomilla/serializers/base/__init__.py,sha256=HSDBPXQ3ldX0pixDbNLna5LSUQ_TfFZ4JWKRm6OpWcU,793
46
- camomilla/serializers/fields/__init__.py,sha256=P3WFxrNGNBKTYqXi-CA4e5HLOYWo6Yt6izQcwyUhBPU,642
43
+ camomilla/serializers/utils.py,sha256=uPQNNkIMZyoPP4umWTgFHNM7KyabBZgs5r5AJyfgtJI,1105
44
+ camomilla/serializers/validators.py,sha256=EX-EFg6afFr8viPKq-LHeEtRAKPVtO4kIlEymY1j2HA,2043
45
+ camomilla/serializers/base/__init__.py,sha256=P2153u7T4yQSbgwIC9LJxJFnVSle3H9oEuTUNrnz9x4,869
46
+ camomilla/serializers/fields/__init__.py,sha256=T_ONowldBTPfgbwFmoefY1r3TQiB2sbWQPpFvBSnzqE,681
47
47
  camomilla/serializers/fields/file.py,sha256=yjKMho2ti9TIAzo6nwyLnNPJ6GVUumL2wxhegvYqI2o,800
48
- camomilla/serializers/fields/json.py,sha256=KHSxklGr1dLS6yd8nkVo_S7BiJKVMJ8Iv8vXyR8aCVQ,1870
49
- camomilla/serializers/fields/related.py,sha256=Lofgk582KAb0EIK20jtds538O_y-yRHo644ND_VlXrM,4747
50
- camomilla/serializers/mixins/__init__.py,sha256=lvPCwDNZtQwdewIQUVI-D9f2UuwxjVs29TNTozcahzs,7377
48
+ camomilla/serializers/fields/json.py,sha256=epuvDkKqDHuXAH0jVpOkeljz7LnMJVg7RoWYioPgCNs,1873
49
+ camomilla/serializers/fields/related.py,sha256=kG5_q1ckm33BVUIgPLZFa6IjL5lQ5DT8_83fUe9plc8,4989
50
+ camomilla/serializers/mixins/__init__.py,sha256=lDfeIoaMqKH11QHzBNs4hA5aHBV7sJsjzGLew6tdcXU,9367
51
+ camomilla/serializers/mixins/filter_fields.py,sha256=AwdV5iG-r0JFy4Wtywczu7bqA8Fb6NZyy60Y79Z6cWo,2214
51
52
  camomilla/storages/__init__.py,sha256=ytGgX59Ar8vFfYz7eV8z-j5yO_5FqxdZM25iyLnJuSA,131
52
- camomilla/storages/optimize.py,sha256=YKbhSbTaWNbTFIRTRWdilD3w8b_LjMLOmE6yk2lrbo4,2745
53
- camomilla/storages/overwrite.py,sha256=CO_zSZxr321eohYFuzPjDMK7WMFnvvwlIigIPabU_eE,343
54
- camomilla/structured/__init__.py,sha256=mX0mruLSrovzKlIk4WLBw-eFOuxbFVh9ASA5lR5OVF4,4338
55
- camomilla/structured/cache.py,sha256=Fi6F3oQzq8XcbGglKes-U1SskFzTnvowZY82S5eeOCU,7845
56
- camomilla/structured/fields.py,sha256=INjb6G67k4nmeGQibU0WBHUUpRJQbYmcX74sWvBLMUs,5237
57
- camomilla/structured/models.py,sha256=us03rOU2VtdhH4MMHIGkb11Wr6ItxPmB7ZUL1QepWmg,1790
58
- camomilla/structured/utils.py,sha256=nJlRFuYkBOgqxMWEpCM5qGu2m7j9TCBHB8SMmZPbCKM,3447
53
+ camomilla/storages/default.py,sha256=KVuGPIpkM9tDHc0cYG385xkiKOwIvage5IBmdrv2uOk,188
54
+ camomilla/storages/optimize.py,sha256=VGSXZigzZC8LnPTqyTOpPA2Ba9EJB_KC5bcACoRs4GA,2762
55
+ camomilla/storages/overwrite.py,sha256=jvW3zHvXNzH9dIjeZmmfXo_O3K1ZQmLQzmlSKAOE8ZA,360
59
56
  camomilla/templates/admin/camomilla/page/change_form.html,sha256=ig7rRUtylDZMINBQuVPpZLmeB4sOTV_VtqnTgzAyxEo,251
60
57
  camomilla/templates/defaults/base.html,sha256=pklt7Pif3g9d7gwgRxCQj7gniJaHD14ZqZID_xIlC0A,6638
61
58
  camomilla/templates/defaults/articles/default.html,sha256=1f89jBvNtTa1mPAbC91yy8CzeAjTWO3hhQsTuQW5OKg,239
62
59
  camomilla/templates/defaults/pages/default.html,sha256=bP81Qb6M56I-fBJMywWwEu_cnERtWIX28UkGrUSRU6M,144
63
60
  camomilla/templates/defaults/parts/langswitch.html,sha256=AkaQzb2KNjRYCMLUn_jE31V36rwBIwp4MneirWPiBcI,3424
64
- camomilla/templates/defaults/parts/menu.html,sha256=KBgXv23dCWW0XM16R5fT0_g-w_qudVfcF-CYSdwl83s,338
61
+ camomilla/templates/defaults/parts/menu.html,sha256=CSRPx78Z6D6pGtRqqaLmJetUaADrTh9rx_gqS4myndc,381
65
62
  camomilla/templates/defaults/widgets/media_select_multiple.html,sha256=k2XYou8KkPuFLnPMkPJAFJ-zGJj2Xvu6R3ZmiKa3g7Q,3727
66
63
  camomilla/templates_context/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
67
64
  camomilla/templates_context/autodiscover.py,sha256=td7SCsqg3iNPnv1HDEDEpwWgWLjy5Zmc8Nbze1_J46I,1907
@@ -69,8 +66,8 @@ camomilla/templates_context/rendering.py,sha256=GfTR45_gC7WT7zTKPVXkBDwe22uF63A-
69
66
  camomilla/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
67
  camomilla/templatetags/camomilla_filters.py,sha256=35x0-cWrRHeLhqypSLlJzEFY_fQDcRHiwZQpFgIsspE,692
71
68
  camomilla/templatetags/menus.py,sha256=pLFeJ1UAm3flsU9TNo2iQk4otSwZuJPr86Nf408dZ-8,862
72
- camomilla/theme/__init__.py,sha256=UVNasDCHfmeetQsfJsCQ1TN3YgarCjDERgSZX0OwKT8,30
73
- camomilla/theme/admin.py,sha256=R3QAfqaF9mhzwfyb_m2_hqTQ2hBcNvQts1MWUX4dUUM,2462
69
+ camomilla/theme/__init__.py,sha256=ZTc6bQJ8jCuKWUOkNmYh8qHpCpZ6iN55XQS_wWviAis,30
70
+ camomilla/theme/admin.py,sha256=7TgNXOfIy4awtV-KkGEKsmai94qhBiykB0uJWwTZy8U,2461
74
71
  camomilla/theme/apps.py,sha256=ecqG8ntWdOighYOEHQOMVQpa5g_1WEzzpaaGjnOi9uA,1195
75
72
  camomilla/theme/static/admin/css/responsive.css,sha256=yGq6qXrr8xEVsXTnprIBgkX-sMGZrNf0Kkh-xDxf6yE,157
76
73
  camomilla/theme/static/admin/img/favicon.ico,sha256=qpKv_2MaGILvyihnD1Vq9Yk-ZXGkxWTW26ciMeBFMYU,15406
@@ -80,7 +77,9 @@ camomilla/theme/templates/rosetta/base.html,sha256=s9Ijf1nZx8um30R5Gk3g-w-RwFGv2
80
77
  camomilla/utils/__init__.py,sha256=Ui7nzSh45UMMFtCGF1xFHKyJMNWflG_Nx72HAJHJHFM,100
81
78
  camomilla/utils/getters.py,sha256=6j18grFAZ8BC70SriycFDTQFxTnudGn0uKGA83_Rclk,798
82
79
  camomilla/utils/normalization.py,sha256=RDCZtjwpEEwjvfUjQl2bEWFKw7NxTzkXco72VeO2M9w,255
80
+ camomilla/utils/query_parser.py,sha256=STNUfoKENcRM_joIKh5AVvYADppsURj9Jpf9OkUQrJU,5818
83
81
  camomilla/utils/seo.py,sha256=8p_a_TGgohenpJb094tT4mMxbn2xzW0qDILuTnjNocM,3324
82
+ camomilla/utils/setters.py,sha256=LV57SM65rL1_ZQkVzk9al_Q13lndVywXLkqgfIvgS0Y,915
84
83
  camomilla/utils/templates.py,sha256=Lv4-5019cnM30HmdZnYWiU5gxry-eFZVAhwOofGQRDs,598
85
84
  camomilla/utils/translation.py,sha256=HG70ki9MTBVTdAYgi6lqqFQtNm30VTvGSYepXpyb3_Y,2477
86
85
  camomilla/views/__init__.py,sha256=94QuOnnbfMMb17mruO2ydUt286-8zBmDxEPWrJv5Wog,178
@@ -89,21 +88,28 @@ camomilla/views/contents.py,sha256=JxvnmgeK8JEmCMLzVG8pVq2DwvmjXtgnIdsDnn74tA4,1
89
88
  camomilla/views/decorators.py,sha256=hR--nTGQn2mMKDrWn-0Ildzbsvp11OfoWAtedKEzmiA,982
90
89
  camomilla/views/languages.py,sha256=Rt_X7s3dbDBv4dxsQ9fnav_u0TAzzo8fGKBBx3esDsg,441
91
90
  camomilla/views/medias.py,sha256=S4Ak4JI6XCSu3BCZx8KOotX7TBfrIU82GPXgyP3KcZA,3001
92
- camomilla/views/menus.py,sha256=wKxBTaLW-YEJrE0RIoi_1oUpymskbBTqQvJh1ZioN5c,3382
91
+ camomilla/views/menus.py,sha256=Kpygnf3tMKJ30gcblUES2NW83A37Vy75ecSGSvExGKM,3301
93
92
  camomilla/views/pages.py,sha256=KrBsKT0Z-rkBha8K3P6fVDt4vArQBxsc9rZ28j8XW9w,994
94
93
  camomilla/views/tags.py,sha256=XcYRlcBFSPPY32lt7POb6fWPJL_8HsTo5JcHcAOiOKw,479
95
94
  camomilla/views/users.py,sha256=_fvsKOEtep4SJLvMva2_q-HdLQT_1KlFNt4wcl3xCJk,3130
96
- camomilla/views/base/__init__.py,sha256=S7KGhVzHM7f0j8XD7cbBY5__66zvKYkphRD9cfl0O1E,222
97
- camomilla/views/mixins/__init__.py,sha256=GeRoznX3wTm6XydOltAIbx_WU7hVS70R-7jyAck0UpE,2438
95
+ camomilla/views/base/__init__.py,sha256=dWiyyRpvOWXMmBeLsfLDOCK_ZesYzBBniH71zAu6hu4,282
96
+ camomilla/views/mixins/__init__.py,sha256=MEZiQf-wkY80J-f9c6GnLD2o97YefCKyn5PXW9jj8bc,2672
98
97
  camomilla/views/mixins/ordering.py,sha256=mh7fqPyVCVJh84Nl2pYFQouzGxa-ANF3Wqv0pCb7OVU,4779
99
- camomilla/views/mixins/pagination.py,sha256=vUkfhqhTabeDdoiMwjeVpO6gl534EI0JMrr4TlZ8MRA,5956
98
+ camomilla/views/mixins/pagination.py,sha256=8BY4T5aWw50LSgIbwM4X0_HWm8GFgKWKn1TUrY028No,5519
100
99
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
101
- tests/test_api.py,sha256=DTXtvNNRwvZvxGKi6LyCzhpyU1OzPWGSbBUfRL3-Nck,2101
102
- tests/test_camomilla_filters.py,sha256=_W9CcwsEyewMBvnNYKk4CVqrkXxRYejle3mjiBS7hqk,1848
100
+ tests/test_api.py,sha256=t03EFDezGgm4UJl8RIVvnTUkAGTB6ptm0G2lHBQ7ljc,1833
101
+ tests/test_camomilla_filters.py,sha256=9tHdxhU-Qk5c3ZWbhLQE3g1VrWKS-cGlRG258D1T9hI,1535
102
+ tests/test_media.py,sha256=bBFUFWEKWIWgDxclOyAfLW5wUCERSfONtxD_OOz9KBg,2523
103
+ tests/test_model_api.py,sha256=Ne8YlXTH2cqP5gzOc8UKjJuh0t-NaKHh5Ol9krpVHQg,3768
104
+ tests/test_model_api_permissions.py,sha256=7CSb4-yIOfycAL_vXvh1dE2whx7k0gNkWl9LO0yzy4I,1801
103
105
  tests/test_models.py,sha256=WJs8lxWZWn1l7X3a_QFVc8fF5LHTsI8bc3uhQe6-o-Q,684
104
- tests/test_utils.py,sha256=0tdEDBaBkyjAXpbqP0SpFcfgO56MV2TWZmk9xpjR7J0,3613
105
- django_camomilla_cms-6.0.0b14.dist-info/LICENSE,sha256=kVS7zDrNkav2hLLXbOJwVdonY2ToApTK3khyJagGQoQ,1063
106
- django_camomilla_cms-6.0.0b14.dist-info/METADATA,sha256=Epgi0FSONdA7m9MZnYCTufligreZhHcNT3OD0jOgKo4,2410
107
- django_camomilla_cms-6.0.0b14.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
108
- django_camomilla_cms-6.0.0b14.dist-info/top_level.txt,sha256=G9VIGBmMMqC7JEckoTgXKmC6T2BR75QRkqRnngw1_lo,16
109
- django_camomilla_cms-6.0.0b14.dist-info/RECORD,,
106
+ tests/test_query_parser.py,sha256=ebHQwjGINHy84JZZXUxrdutXtlHBhoqq7keLr5GHFik,2098
107
+ tests/test_utils.py,sha256=o_FG7XOxLePOBfwBr4sk09gej0onWNw9t2-gSjGmgNg,3741
108
+ tests/fixtures/__init__.py,sha256=NGj22kLV65v56IpOrOVqSkPhJePTXD4QjuuZhZSMwfQ,460
109
+ tests/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
110
+ tests/utils/api.py,sha256=vnj6W3PG5a-R_uF43nv3UushpMfyG_HbqlsBXH9Ysg0,910
111
+ django_camomilla_cms-6.0.0b16.dist-info/LICENSE,sha256=kVS7zDrNkav2hLLXbOJwVdonY2ToApTK3khyJagGQoQ,1063
112
+ django_camomilla_cms-6.0.0b16.dist-info/METADATA,sha256=pwvWq5hO3tSBO2Qmt0TgoKJy5QY5Kko2yeaXYjubXAw,2390
113
+ django_camomilla_cms-6.0.0b16.dist-info/WHEEL,sha256=OpXWERl2xLPRHTvd2ZXo_iluPEQd8uSbYkJ53NAER_Y,109
114
+ django_camomilla_cms-6.0.0b16.dist-info/top_level.txt,sha256=G9VIGBmMMqC7JEckoTgXKmC6T2BR75QRkqRnngw1_lo,16
115
+ django_camomilla_cms-6.0.0b16.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: setuptools (75.3.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py2-none-any
5
5
  Tag: py3-none-any
@@ -0,0 +1,17 @@
1
+ import json
2
+ import os
3
+ from django.core.files.uploadedfile import SimpleUploadedFile
4
+
5
+
6
+ def load_json_fixture(filename):
7
+ with open(os.path.join(os.path.dirname(__file__), 'json', filename), "r") as f:
8
+ return json.load(f)
9
+
10
+
11
+ def load_asset(filename):
12
+ with open(os.path.join(os.path.dirname(__file__), 'assets', filename), "rb") as f:
13
+ up_file = SimpleUploadedFile(
14
+ filename,
15
+ f.read()
16
+ )
17
+ return up_file
tests/test_api.py CHANGED
@@ -1,20 +1,11 @@
1
1
  import pytest
2
- from django.contrib.auth.models import User
3
2
  from rest_framework.test import APIClient
4
-
3
+ from .utils.api import login_superuser
5
4
  from camomilla.models import Tag
6
5
 
7
6
  client = APIClient()
8
7
 
9
8
 
10
- def login_superuser():
11
- User.objects.create_superuser("admin", "myemail@test.com", "adminadmin")
12
- response = client.post(
13
- "/api/camomilla/token-auth/", {"username": "admin", "password": "adminadmin"}
14
- )
15
- return response.json()["token"]
16
-
17
-
18
9
  @pytest.mark.django_db
19
10
  def test_create_tag_no_access():
20
11
  response = client.post("/api/camomilla/tags/", {"name_en": "First tag"})
@@ -27,7 +18,7 @@ def test_crud_tag():
27
18
  token = login_superuser()
28
19
  client.credentials(HTTP_AUTHORIZATION="Token " + token)
29
20
  response = client.post("/api/camomilla/tags/", {"name_en": "First tag"})
30
-
21
+
31
22
  assert response.json()["name"] == "First tag"
32
23
  assert len(Tag.objects.all()) == 1
33
24
  assert response.status_code == 201
@@ -17,31 +17,25 @@ class CamomillaFiltersTestCase(TestCase):
17
17
  pass
18
18
 
19
19
  def test_filter_content(self):
20
+ Page.objects.create(identifier="path", title="Path", permalink="/path", status="PUB")
20
21
  request_factory = RequestFactory()
21
22
  request = request_factory.get("/path")
22
23
  request.META["HTTP_HOST"] = "localhost"
23
- page = Page.get(request, identifier="home")
24
+ page = Page.get(request)
24
25
  content = filter_content(page, "content1")
25
26
  self.assertEqual(content.identifier, "content1")
26
27
  self.assertEqual(content.content, "")
27
28
  content.content = "Hello World!"
28
29
  content.save()
29
- page = Page.get(request, identifier="home")
30
+ page = Page.get(request)
30
31
  content = filter_content(page, "content1")
31
32
  self.assertEqual(content.identifier, "content1")
32
33
  self.assertEqual(content.content, "Hello World!")
33
34
 
34
35
  def test_filter_alternate_urls(self):
35
- request = RequestFactory().get("/path", HTTP_HOST="localhost:8000")
36
+ Page.objects.create(identifier="path", title="Path", permalink="/path", status="PUB")
37
+ request = RequestFactory().get("/path")
36
38
  request.META["HTTP_HOST"] = "localhost"
37
- page = Page.get(request, identifier="home")
39
+ page = Page.get(request)
38
40
  alt_urls = dict(alternate_urls(page, request))
39
- self.assertEqual(alt_urls, {})
40
-
41
- request = RequestFactory().get("/about", HTTP_HOST="localhost:8000")
42
- request.META["HTTP_HOST"] = "localhost"
43
- page = Page.get(request, identifier="about")
44
- alt_urls = dict(alternate_urls(page, request))
45
- self.assertEqual(alt_urls["it"], "http://localhost/about")
46
- self.assertEqual(alt_urls["en"], "http://localhost/en/about")
47
- self.assertEqual(alt_urls["de"], "http://localhost/de/about")
41
+ self.assertEqual(alt_urls, {'it': None, 'en': '/path/'})
tests/test_media.py ADDED
@@ -0,0 +1,80 @@
1
+ import pytest
2
+ import json
3
+ import os
4
+ from camomilla.models import Media
5
+ from .fixtures import load_asset
6
+ from .utils.api import login_superuser
7
+ from rest_framework.test import APIClient
8
+ from django.conf import settings
9
+
10
+ client = APIClient()
11
+
12
+
13
+ def load_asset_and_remove_media(filename):
14
+ asset = load_asset(filename)
15
+ if os.path.exists(f"{settings.MEDIA_ROOT}/{filename}"):
16
+ os.remove(f"{settings.MEDIA_ROOT}/{filename}")
17
+ return asset
18
+
19
+
20
+ @pytest.mark.django_db
21
+ def test_media_api_creation():
22
+ token = login_superuser()
23
+ client.credentials(HTTP_AUTHORIZATION='Token ' + token)
24
+ asset = load_asset_and_remove_media("10595073.png")
25
+ response = client.post(
26
+ "/api/camomilla/media/",
27
+ {
28
+ "file": asset,
29
+ "data": json.dumps({"translations": {"en": {"alt_text": "Test", "title": "Test", "description": "Test"}}}),
30
+ },
31
+ format="multipart",
32
+ )
33
+ assert response.status_code == 201
34
+ assert Media.objects.count() == 1
35
+ media = Media.objects.first()
36
+ assert media.alt_text == "Test"
37
+ assert media.title == "Test"
38
+ assert media.description == "Test"
39
+ assert media.file.name == "10595073.png"
40
+
41
+
42
+ @pytest.mark.django_db
43
+ def test_media_compression():
44
+ token = login_superuser()
45
+ client.credentials(HTTP_AUTHORIZATION='Token ' + token)
46
+ asset = load_asset_and_remove_media("Sample-jpg-image-10mb.jpg")
47
+ asset_size = asset.size
48
+ response = client.post(
49
+ "/api/camomilla/media/",
50
+ {
51
+ "file": asset,
52
+ "data": json.dumps({"translations": {"en": {"alt_text": "Test", "title": "Test", "description": "Test"}}}),
53
+ },
54
+ format="multipart",
55
+ )
56
+ assert response.status_code == 201
57
+ assert Media.objects.count() == 1
58
+ media = Media.objects.first()
59
+ assert media.file.size < asset_size
60
+ assert media.file.size < 1000000 # 1MB
61
+
62
+
63
+ @pytest.mark.django_db
64
+ def test_inflating_prevent():
65
+ token = login_superuser()
66
+ client.credentials(HTTP_AUTHORIZATION='Token ' + token)
67
+ asset = load_asset_and_remove_media("optimized.jpg")
68
+ asset_size = asset.size
69
+ response = client.post(
70
+ "/api/camomilla/media/",
71
+ {
72
+ "file": asset,
73
+ "data": json.dumps({"translations": {"en": {"alt_text": "Test", "title": "Test", "description": "Test"}}}),
74
+ },
75
+ format="multipart",
76
+ )
77
+ assert response.status_code == 201
78
+ assert Media.objects.count() == 1
79
+ media = Media.objects.first()
80
+ assert media.file.size < asset_size
@@ -0,0 +1,68 @@
1
+ import pytest
2
+ from rest_framework.test import APIClient
3
+ from .fixtures import load_json_fixture
4
+ from .utils.api import login_superuser
5
+ from example.website.models import SimpleRelationModel
6
+
7
+
8
+ client = APIClient()
9
+
10
+
11
+ @pytest.fixture(autouse=True)
12
+ def init_test():
13
+ token = login_superuser()
14
+ client.credentials(HTTP_AUTHORIZATION="Token " + token)
15
+ SimpleRelationModel.objects.bulk_create([SimpleRelationModel(name=f"test{i}") for i in range(1, 10)])
16
+
17
+
18
+ @pytest.mark.django_db
19
+ def test_simple_relation_model_api_endpoint():
20
+ response = client.get("/api/models/simple-relation-model/")
21
+ assert response.status_code == 200
22
+ assert len(response.json()) == 9
23
+ response = client.get("/api/models/simple-relation-model/1/")
24
+ assert response.status_code == 200
25
+ assert response.json()["name"] == "test1"
26
+ response = client.patch("/api/models/simple-relation-model/1/", {"name": "updated"}, format="json")
27
+ assert response.status_code == 200
28
+ assert response.json()["name"] == "updated"
29
+ response = client.delete("/api/models/simple-relation-model/1/")
30
+ assert response.status_code == 204
31
+ response = client.get("/api/models/simple-relation-model/")
32
+ assert response.status_code == 200
33
+ assert len(response.json()) == 8
34
+ response = client.get("/api/models/simple-relation-model/1/")
35
+ assert response.status_code == 404
36
+ assert response.json() in [{"detail": "Not found."}, {'detail': 'No SimpleRelationModel matches the given query.'}]
37
+
38
+
39
+ @pytest.mark.django_db
40
+ def test_test_model_api_endpoint():
41
+ response = client.get("/api/models/test-model/")
42
+ assert response.status_code == 200
43
+ assert response.json() == []
44
+ test_model_data = load_json_fixture("test-model-api.json")
45
+ response = client.post("/api/models/test-model/", test_model_data, format="json")
46
+ assert response.status_code == 201
47
+ assert response.json()["title"] == test_model_data["title"]
48
+ assert response.json()["structured_data"]["name"] == test_model_data["structured_data"]["name"]
49
+ assert response.json()["structured_data"]["age"] == test_model_data["structured_data"]["age"]
50
+ assert response.json()["structured_data"]["child"]["name"] == test_model_data["structured_data"]["child"]["name"]
51
+ assert response.json()["structured_data"]["childs"][0]["name"] == test_model_data["structured_data"]["childs"][0]["name"]
52
+ assert response.json()["structured_data"]["fk_field"]["id"] == test_model_data["structured_data"]["fk_field"]["id"]
53
+ assert response.json()["structured_data"]["qs_field"][0]["id"] == test_model_data["structured_data"]["qs_field"][0]["id"]
54
+ response = client.get("/api/models/test-model/")
55
+ assert response.status_code == 200
56
+ assert len(response.json()) == 1
57
+ response = client.get("/api/models/test-model/1/")
58
+ assert response.status_code == 200
59
+ assert response.json()["title"] == test_model_data["title"]
60
+ assert response.json()["structured_data"]["name"] == test_model_data["structured_data"]["name"]
61
+ assert response.json()["structured_data"]["age"] == test_model_data["structured_data"]["age"]
62
+ assert response.json()["structured_data"]["child"]["name"] == test_model_data["structured_data"]["child"]["name"]
63
+ assert response.json()["structured_data"]["childs"][0]["name"] == test_model_data["structured_data"]["childs"][0]["name"]
64
+ assert response.json()["structured_data"]["fk_field"]["id"] == test_model_data["structured_data"]["fk_field"]["id"]
65
+ assert response.json()["structured_data"]["qs_field"][0]["id"] == test_model_data["structured_data"]["qs_field"][0]["id"]
66
+ response = client.patch("/api/models/test-model/1/", {"title": "updated"}, format="json")
67
+ assert response.status_code == 200
68
+ assert response.json()["title"] == "updated"
@@ -0,0 +1,39 @@
1
+ import pytest
2
+ from rest_framework.test import APIClient
3
+ from .utils.api import login_user, login_superuser, login_staff
4
+
5
+ client = APIClient()
6
+
7
+
8
+ @pytest.mark.django_db
9
+ def test_right_permissions():
10
+ response = client.post("/api/models/test-model/", {"title": "test"}, format="json")
11
+ assert response.status_code == 401
12
+ token = login_user()
13
+ client.credentials(HTTP_AUTHORIZATION="Token " + token)
14
+ response = client.post("/api/models/test-model/", {"title": "test"}, format="json")
15
+ assert response.status_code == 403
16
+ token = login_staff()
17
+ client.credentials(HTTP_AUTHORIZATION="Token " + token)
18
+ response = client.post("/api/models/test-model/", {"title": "test"}, format="json")
19
+ assert response.status_code == 403
20
+ token = login_superuser()
21
+ client.credentials(HTTP_AUTHORIZATION="Token " + token)
22
+ response = client.post("/api/models/test-model/", {"title": "test"}, format="json")
23
+ assert response.status_code == 201
24
+ response = client.get("/api/models/test-model/")
25
+ assert response.status_code == 200
26
+ assert len(response.json()) == 1
27
+ response = client.get("/api/models/test-model/1/")
28
+ assert response.status_code == 200
29
+ response = client.patch("/api/models/test-model/1/", {"title": "updated"}, format="json")
30
+ assert response.status_code == 200
31
+ assert response.json()["title"] == "updated"
32
+ response = client.delete("/api/models/test-model/1/")
33
+ assert response.status_code == 204
34
+ response = client.get("/api/models/test-model/")
35
+ assert response.status_code == 200
36
+ assert len(response.json()) == 0
37
+ response = client.get("/api/models/test-model/1/")
38
+ assert response.status_code == 404
39
+ assert response.json() in [{"detail": "Not found."}, {'detail': 'No TestModel matches the given query.'}]
@@ -0,0 +1,59 @@
1
+ import pytest
2
+ from django.db.models import Q
3
+ from camomilla.utils.query_parser import (
4
+ ConditionParser,
5
+ )
6
+
7
+ @pytest.mark.parametrize(
8
+ "query, expected_q",
9
+ [
10
+ # Single condition
11
+ ("name__icontains=foo", Q(name__icontains="foo")),
12
+ # Multiple conditions with AND
13
+ (
14
+ "name__icontains=foo AND age__gt=21",
15
+ Q(name__icontains="foo") & Q(age__gt=21),
16
+ ),
17
+ # Multiple conditions with OR
18
+ (
19
+ "name__icontains=foo OR name__icontains=bar",
20
+ Q(name__icontains="foo") | Q(name__icontains="bar"),
21
+ ),
22
+ # Mixed AND and OR conditions
23
+ (
24
+ "name__icontains=foo OR (age__gt=21 AND city__iexact='New York')",
25
+ Q(name__icontains="foo") | (Q(age__gt=21) & Q(city__iexact="New York")),
26
+ ),
27
+ # Nested parentheses with OR inside AND
28
+ (
29
+ "((name__icontains=foo) OR (name__icontains=bar)) AND name__icontains=baz",
30
+ (Q(name__icontains="foo") | Q(name__icontains="bar"))
31
+ & Q(name__icontains="baz"),
32
+ ),
33
+ # Complex nested conditions with multiple OR and AND
34
+ (
35
+ "((name__icontains=foo) OR (name__icontains=bar) OR (name__icontains=buz)) AND age__gt=25 AND city__iexact='LA'",
36
+ (
37
+ Q(name__icontains="foo")
38
+ | Q(name__icontains="bar")
39
+ | Q(name__icontains="buz")
40
+ )
41
+ & Q(age__gt=25)
42
+ & Q(city__iexact="LA"),
43
+ ),
44
+ # Single condition with no parentheses
45
+ ("age__lte=30", Q(age__lte=30)),
46
+ # Simple nested OR and AND with more levels of nesting
47
+ (
48
+ "(name__icontains=foo AND (age__gt=21 OR city__iexact='New York')) OR country__iexact='US'",
49
+ (Q(name__icontains="foo") & (Q(age__gt=21) | Q(city__iexact="New York")))
50
+ | Q(country__iexact="US"),
51
+ ),
52
+ ],
53
+ )
54
+ def test_condition_parser(query, expected_q):
55
+ parser = ConditionParser(query)
56
+ q_object = parser.parse_to_q()
57
+ assert q_object.__str__() == expected_q.__str__()
58
+
59
+