ckeditor в админке, урлы для конт и осайте

main
Сергей Ванюшкин 2023-09-30 22:28:28 +03:00
parent 3b3595d6b8
commit 9d92bcac90
15 changed files with 265 additions and 133 deletions

130
poetry.lock generated
View File

@ -262,19 +262,19 @@ sqlalchemy = ">=2.0.16"
[[package]] [[package]]
name = "flask-wtf" name = "flask-wtf"
version = "1.1.1" version = "1.1.2"
description = "Form rendering, validation, and CSRF protection for Flask with WTForms." description = "Form rendering, validation, and CSRF protection for Flask with WTForms."
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.8"
files = [ files = [
{file = "Flask-WTF-1.1.1.tar.gz", hash = "sha256:41c4244e9ae626d63bed42ae4785b90667b885b1535d5a4095e1f63060d12aa9"}, {file = "flask_wtf-1.1.2-py3-none-any.whl", hash = "sha256:134f45f3155ebdbb2b44fe8e5b498a0956d34a16b10a53fadcb7a865c0b3cea2"},
{file = "Flask_WTF-1.1.1-py3-none-any.whl", hash = "sha256:7887d6f1ebb3e17bf648647422f0944c9a469d0fcf63e3b66fb9a83037e38b2c"}, {file = "flask_wtf-1.1.2.tar.gz", hash = "sha256:b51cfa7ad14e03de432a6268e8341354939d0beebf30fce66f8617a93e55e2a0"},
] ]
[package.dependencies] [package.dependencies]
Flask = "*" flask = "*"
itsdangerous = "*" itsdangerous = "*"
WTForms = "*" wtforms = "*"
[package.extras] [package.extras]
email = ["email-validator"] email = ["email-validator"]
@ -389,18 +389,18 @@ files = [
[[package]] [[package]]
name = "importlib-resources" name = "importlib-resources"
version = "6.0.1" version = "6.1.0"
description = "Read resources from Python packages" description = "Read resources from Python packages"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "importlib_resources-6.0.1-py3-none-any.whl", hash = "sha256:134832a506243891221b88b4ae1213327eea96ceb4e407a00d790bb0626f45cf"}, {file = "importlib_resources-6.1.0-py3-none-any.whl", hash = "sha256:aa50258bbfa56d4e33fbd8aa3ef48ded10d1735f11532b8df95388cc6bdb7e83"},
{file = "importlib_resources-6.0.1.tar.gz", hash = "sha256:4359457e42708462b9626a04657c6208ad799ceb41e5c58c57ffa0e6a098a5d4"}, {file = "importlib_resources-6.1.0.tar.gz", hash = "sha256:9d48dcccc213325e810fd723e7fbb45ccb39f6cf5c31f00cf2b965f5f10f3cb9"},
] ]
[package.extras] [package.extras]
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff"] testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff", "zipp (>=3.17)"]
[[package]] [[package]]
name = "itsdangerous" name = "itsdangerous"
@ -548,22 +548,22 @@ totp = ["cryptography"]
[[package]] [[package]]
name = "psycopg2" name = "psycopg2"
version = "2.9.7" version = "2.9.8"
description = "psycopg2 - Python-PostgreSQL Database Adapter" description = "psycopg2 - Python-PostgreSQL Database Adapter"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
files = [ files = [
{file = "psycopg2-2.9.7-cp310-cp310-win32.whl", hash = "sha256:1a6a2d609bce44f78af4556bea0c62a5e7f05c23e5ea9c599e07678995609084"}, {file = "psycopg2-2.9.8-cp310-cp310-win32.whl", hash = "sha256:2f8594f92bbb5d8b59ffec04e2686c416401e2d4297de1193f8e75235937e71d"},
{file = "psycopg2-2.9.7-cp310-cp310-win_amd64.whl", hash = "sha256:b22ed9c66da2589a664e0f1ca2465c29b75aaab36fa209d4fb916025fb9119e5"}, {file = "psycopg2-2.9.8-cp310-cp310-win_amd64.whl", hash = "sha256:f9ecbf504c4eaff90139d5c9b95d47275f2b2651e14eba56392b4041fbf4c2b3"},
{file = "psycopg2-2.9.7-cp311-cp311-win32.whl", hash = "sha256:44d93a0109dfdf22fe399b419bcd7fa589d86895d3931b01fb321d74dadc68f1"}, {file = "psycopg2-2.9.8-cp311-cp311-win32.whl", hash = "sha256:65f81e72136d8b9ac8abf5206938d60f50da424149a43b6073f1546063c0565e"},
{file = "psycopg2-2.9.7-cp311-cp311-win_amd64.whl", hash = "sha256:91e81a8333a0037babfc9fe6d11e997a9d4dac0f38c43074886b0d9dead94fe9"}, {file = "psycopg2-2.9.8-cp311-cp311-win_amd64.whl", hash = "sha256:f7e62095d749359b7854143843f27edd7dccfcd3e1d833b880562aa5702d92b0"},
{file = "psycopg2-2.9.7-cp37-cp37m-win32.whl", hash = "sha256:d1210fcf99aae6f728812d1d2240afc1dc44b9e6cba526a06fb8134f969957c2"}, {file = "psycopg2-2.9.8-cp37-cp37m-win32.whl", hash = "sha256:81b21424023a290a40884c7f8b0093ba6465b59bd785c18f757e76945f65594c"},
{file = "psycopg2-2.9.7-cp37-cp37m-win_amd64.whl", hash = "sha256:e9b04cbef584310a1ac0f0d55bb623ca3244c87c51187645432e342de9ae81a8"}, {file = "psycopg2-2.9.8-cp37-cp37m-win_amd64.whl", hash = "sha256:67c2f32f3aba79afb15799575e77ee2db6b46b8acf943c21d34d02d4e1041d50"},
{file = "psycopg2-2.9.7-cp38-cp38-win32.whl", hash = "sha256:d5c5297e2fbc8068d4255f1e606bfc9291f06f91ec31b2a0d4c536210ac5c0a2"}, {file = "psycopg2-2.9.8-cp38-cp38-win32.whl", hash = "sha256:287a64ef168ef7fb9f382964705ff664b342bfff47e7242bf0a04ef203269dd5"},
{file = "psycopg2-2.9.7-cp38-cp38-win_amd64.whl", hash = "sha256:8275abf628c6dc7ec834ea63f6f3846bf33518907a2b9b693d41fd063767a866"}, {file = "psycopg2-2.9.8-cp38-cp38-win_amd64.whl", hash = "sha256:dcde3cad4920e29e74bf4e76c072649764914facb2069e6b7fa1ddbebcd49e9f"},
{file = "psycopg2-2.9.7-cp39-cp39-win32.whl", hash = "sha256:c7949770cafbd2f12cecc97dea410c514368908a103acf519f2a346134caa4d5"}, {file = "psycopg2-2.9.8-cp39-cp39-win32.whl", hash = "sha256:d4ad050ea50a16731d219c3a85e8f2debf49415a070f0b8331ccc96c81700d9b"},
{file = "psycopg2-2.9.7-cp39-cp39-win_amd64.whl", hash = "sha256:b6bd7d9d3a7a63faae6edf365f0ed0e9b0a1aaf1da3ca146e6b043fb3eb5d723"}, {file = "psycopg2-2.9.8-cp39-cp39-win_amd64.whl", hash = "sha256:d39bb3959788b2c9d7bf5ff762e29f436172b241cd7b47529baac77746fd7918"},
{file = "psycopg2-2.9.7.tar.gz", hash = "sha256:f00cc35bd7119f1fed17b85bd1007855194dde2cbd8de01ab8ebb17487440ad8"}, {file = "psycopg2-2.9.8.tar.gz", hash = "sha256:3da6488042a53b50933244085f3f91803f1b7271f970f3e5536efa69314f6a49"},
] ]
[[package]] [[package]]
@ -579,52 +579,52 @@ files = [
[[package]] [[package]]
name = "sqlalchemy" name = "sqlalchemy"
version = "2.0.20" version = "2.0.21"
description = "Database Abstraction Library" description = "Database Abstraction Library"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "SQLAlchemy-2.0.20-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759b51346aa388c2e606ee206c0bc6f15a5299f6174d1e10cadbe4530d3c7a98"}, {file = "SQLAlchemy-2.0.21-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1e7dc99b23e33c71d720c4ae37ebb095bebebbd31a24b7d99dfc4753d2803ede"},
{file = "SQLAlchemy-2.0.20-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1506e988ebeaaf316f183da601f24eedd7452e163010ea63dbe52dc91c7fc70e"}, {file = "SQLAlchemy-2.0.21-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7f0c4ee579acfe6c994637527c386d1c22eb60bc1c1d36d940d8477e482095d4"},
{file = "SQLAlchemy-2.0.20-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5768c268df78bacbde166b48be788b83dddaa2a5974b8810af422ddfe68a9bc8"}, {file = "SQLAlchemy-2.0.21-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f7d57a7e140efe69ce2d7b057c3f9a595f98d0bbdfc23fd055efdfbaa46e3a5"},
{file = "SQLAlchemy-2.0.20-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3f0dd6d15b6dc8b28a838a5c48ced7455c3e1fb47b89da9c79cc2090b072a50"}, {file = "SQLAlchemy-2.0.21-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca38746eac23dd7c20bec9278d2058c7ad662b2f1576e4c3dbfcd7c00cc48fa"},
{file = "SQLAlchemy-2.0.20-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:243d0fb261f80a26774829bc2cee71df3222587ac789b7eaf6555c5b15651eed"}, {file = "SQLAlchemy-2.0.21-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3cf229704074bce31f7f47d12883afee3b0a02bb233a0ba45ddbfe542939cca4"},
{file = "SQLAlchemy-2.0.20-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6eb6d77c31e1bf4268b4d61b549c341cbff9842f8e115ba6904249c20cb78a61"}, {file = "SQLAlchemy-2.0.21-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fb87f763b5d04a82ae84ccff25554ffd903baafba6698e18ebaf32561f2fe4aa"},
{file = "SQLAlchemy-2.0.20-cp310-cp310-win32.whl", hash = "sha256:bcb04441f370cbe6e37c2b8d79e4af9e4789f626c595899d94abebe8b38f9a4d"}, {file = "SQLAlchemy-2.0.21-cp310-cp310-win32.whl", hash = "sha256:89e274604abb1a7fd5c14867a412c9d49c08ccf6ce3e1e04fffc068b5b6499d4"},
{file = "SQLAlchemy-2.0.20-cp310-cp310-win_amd64.whl", hash = "sha256:d32b5ffef6c5bcb452723a496bad2d4c52b346240c59b3e6dba279f6dcc06c14"}, {file = "SQLAlchemy-2.0.21-cp310-cp310-win_amd64.whl", hash = "sha256:e36339a68126ffb708dc6d1948161cea2a9e85d7d7b0c54f6999853d70d44430"},
{file = "SQLAlchemy-2.0.20-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dd81466bdbc82b060c3c110b2937ab65ace41dfa7b18681fdfad2f37f27acdd7"}, {file = "SQLAlchemy-2.0.21-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bf8eebccc66829010f06fbd2b80095d7872991bfe8415098b9fe47deaaa58063"},
{file = "SQLAlchemy-2.0.20-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6fe7d61dc71119e21ddb0094ee994418c12f68c61b3d263ebaae50ea8399c4d4"}, {file = "SQLAlchemy-2.0.21-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b977bfce15afa53d9cf6a632482d7968477625f030d86a109f7bdfe8ce3c064a"},
{file = "SQLAlchemy-2.0.20-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4e571af672e1bb710b3cc1a9794b55bce1eae5aed41a608c0401885e3491179"}, {file = "SQLAlchemy-2.0.21-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ff3dc2f60dbf82c9e599c2915db1526d65415be323464f84de8db3e361ba5b9"},
{file = "SQLAlchemy-2.0.20-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3364b7066b3c7f4437dd345d47271f1251e0cfb0aba67e785343cdbdb0fff08c"}, {file = "SQLAlchemy-2.0.21-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44ac5c89b6896f4740e7091f4a0ff2e62881da80c239dd9408f84f75a293dae9"},
{file = "SQLAlchemy-2.0.20-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1be86ccea0c965a1e8cd6ccf6884b924c319fcc85765f16c69f1ae7148eba64b"}, {file = "SQLAlchemy-2.0.21-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:87bf91ebf15258c4701d71dcdd9c4ba39521fb6a37379ea68088ce8cd869b446"},
{file = "SQLAlchemy-2.0.20-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1d35d49a972649b5080557c603110620a86aa11db350d7a7cb0f0a3f611948a0"}, {file = "SQLAlchemy-2.0.21-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b69f1f754d92eb1cc6b50938359dead36b96a1dcf11a8670bff65fd9b21a4b09"},
{file = "SQLAlchemy-2.0.20-cp311-cp311-win32.whl", hash = "sha256:27d554ef5d12501898d88d255c54eef8414576f34672e02fe96d75908993cf53"}, {file = "SQLAlchemy-2.0.21-cp311-cp311-win32.whl", hash = "sha256:af520a730d523eab77d754f5cf44cc7dd7ad2d54907adeb3233177eeb22f271b"},
{file = "SQLAlchemy-2.0.20-cp311-cp311-win_amd64.whl", hash = "sha256:411e7f140200c02c4b953b3dbd08351c9f9818d2bd591b56d0fa0716bd014f1e"}, {file = "SQLAlchemy-2.0.21-cp311-cp311-win_amd64.whl", hash = "sha256:141675dae56522126986fa4ca713739d00ed3a6f08f3c2eb92c39c6dfec463ce"},
{file = "SQLAlchemy-2.0.20-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3c6aceebbc47db04f2d779db03afeaa2c73ea3f8dcd3987eb9efdb987ffa09a3"}, {file = "SQLAlchemy-2.0.21-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7614f1eab4336df7dd6bee05bc974f2b02c38d3d0c78060c5faa4cd1ca2af3b8"},
{file = "SQLAlchemy-2.0.20-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d3f175410a6db0ad96b10bfbb0a5530ecd4fcf1e2b5d83d968dd64791f810ed"}, {file = "SQLAlchemy-2.0.21-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d59cb9e20d79686aa473e0302e4a82882d7118744d30bb1dfb62d3c47141b3ec"},
{file = "SQLAlchemy-2.0.20-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea8186be85da6587456c9ddc7bf480ebad1a0e6dcbad3967c4821233a4d4df57"}, {file = "SQLAlchemy-2.0.21-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a95aa0672e3065d43c8aa80080cdd5cc40fe92dc873749e6c1cf23914c4b83af"},
{file = "SQLAlchemy-2.0.20-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c3d99ba99007dab8233f635c32b5cd24fb1df8d64e17bc7df136cedbea427897"}, {file = "SQLAlchemy-2.0.21-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8c323813963b2503e54d0944813cd479c10c636e3ee223bcbd7bd478bf53c178"},
{file = "SQLAlchemy-2.0.20-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:76fdfc0f6f5341987474ff48e7a66c3cd2b8a71ddda01fa82fedb180b961630a"}, {file = "SQLAlchemy-2.0.21-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:419b1276b55925b5ac9b4c7044e999f1787c69761a3c9756dec6e5c225ceca01"},
{file = "SQLAlchemy-2.0.20-cp37-cp37m-win32.whl", hash = "sha256:d3793dcf5bc4d74ae1e9db15121250c2da476e1af8e45a1d9a52b1513a393459"}, {file = "SQLAlchemy-2.0.21-cp37-cp37m-win32.whl", hash = "sha256:4615623a490e46be85fbaa6335f35cf80e61df0783240afe7d4f544778c315a9"},
{file = "SQLAlchemy-2.0.20-cp37-cp37m-win_amd64.whl", hash = "sha256:79fde625a0a55220d3624e64101ed68a059c1c1f126c74f08a42097a72ff66a9"}, {file = "SQLAlchemy-2.0.21-cp37-cp37m-win_amd64.whl", hash = "sha256:cca720d05389ab1a5877ff05af96551e58ba65e8dc65582d849ac83ddde3e231"},
{file = "SQLAlchemy-2.0.20-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:599ccd23a7146e126be1c7632d1d47847fa9f333104d03325c4e15440fc7d927"}, {file = "SQLAlchemy-2.0.21-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b4eae01faee9f2b17f08885e3f047153ae0416648f8e8c8bd9bc677c5ce64be9"},
{file = "SQLAlchemy-2.0.20-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1a58052b5a93425f656675673ef1f7e005a3b72e3f2c91b8acca1b27ccadf5f4"}, {file = "SQLAlchemy-2.0.21-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3eb7c03fe1cd3255811cd4e74db1ab8dca22074d50cd8937edf4ef62d758cdf4"},
{file = "SQLAlchemy-2.0.20-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79543f945be7a5ada9943d555cf9b1531cfea49241809dd1183701f94a748624"}, {file = "SQLAlchemy-2.0.21-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2d494b6a2a2d05fb99f01b84cc9af9f5f93bf3e1e5dbdafe4bed0c2823584c1"},
{file = "SQLAlchemy-2.0.20-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63e73da7fb030ae0a46a9ffbeef7e892f5def4baf8064786d040d45c1d6d1dc5"}, {file = "SQLAlchemy-2.0.21-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b19ae41ef26c01a987e49e37c77b9ad060c59f94d3b3efdfdbf4f3daaca7b5fe"},
{file = "SQLAlchemy-2.0.20-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3ce5e81b800a8afc870bb8e0a275d81957e16f8c4b62415a7b386f29a0cb9763"}, {file = "SQLAlchemy-2.0.21-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fc6b15465fabccc94bf7e38777d665b6a4f95efd1725049d6184b3a39fd54880"},
{file = "SQLAlchemy-2.0.20-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cb0d3e94c2a84215532d9bcf10229476ffd3b08f481c53754113b794afb62d14"}, {file = "SQLAlchemy-2.0.21-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:014794b60d2021cc8ae0f91d4d0331fe92691ae5467a00841f7130fe877b678e"},
{file = "SQLAlchemy-2.0.20-cp38-cp38-win32.whl", hash = "sha256:8dd77fd6648b677d7742d2c3cc105a66e2681cc5e5fb247b88c7a7b78351cf74"}, {file = "SQLAlchemy-2.0.21-cp38-cp38-win32.whl", hash = "sha256:0268256a34806e5d1c8f7ee93277d7ea8cc8ae391f487213139018b6805aeaf6"},
{file = "SQLAlchemy-2.0.20-cp38-cp38-win_amd64.whl", hash = "sha256:6f8a934f9dfdf762c844e5164046a9cea25fabbc9ec865c023fe7f300f11ca4a"}, {file = "SQLAlchemy-2.0.21-cp38-cp38-win_amd64.whl", hash = "sha256:73c079e21d10ff2be54a4699f55865d4b275fd6c8bd5d90c5b1ef78ae0197301"},
{file = "SQLAlchemy-2.0.20-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:26a3399eaf65e9ab2690c07bd5cf898b639e76903e0abad096cd609233ce5208"}, {file = "SQLAlchemy-2.0.21-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:785e2f2c1cb50d0a44e2cdeea5fd36b5bf2d79c481c10f3a88a8be4cfa2c4615"},
{file = "SQLAlchemy-2.0.20-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4cde2e1096cbb3e62002efdb7050113aa5f01718035ba9f29f9d89c3758e7e4e"}, {file = "SQLAlchemy-2.0.21-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c111cd40910ffcb615b33605fc8f8e22146aeb7933d06569ac90f219818345ef"},
{file = "SQLAlchemy-2.0.20-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1b09ba72e4e6d341bb5bdd3564f1cea6095d4c3632e45dc69375a1dbe4e26ec"}, {file = "SQLAlchemy-2.0.21-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9cba4e7369de663611ce7460a34be48e999e0bbb1feb9130070f0685e9a6b66"},
{file = "SQLAlchemy-2.0.20-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b74eeafaa11372627ce94e4dc88a6751b2b4d263015b3523e2b1e57291102f0"}, {file = "SQLAlchemy-2.0.21-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50a69067af86ec7f11a8e50ba85544657b1477aabf64fa447fd3736b5a0a4f67"},
{file = "SQLAlchemy-2.0.20-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:77d37c1b4e64c926fa3de23e8244b964aab92963d0f74d98cbc0783a9e04f501"}, {file = "SQLAlchemy-2.0.21-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ccb99c3138c9bde118b51a289d90096a3791658da9aea1754667302ed6564f6e"},
{file = "SQLAlchemy-2.0.20-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:eefebcc5c555803065128401a1e224a64607259b5eb907021bf9b175f315d2a6"}, {file = "SQLAlchemy-2.0.21-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:513fd5b6513d37e985eb5b7ed89da5fd9e72354e3523980ef00d439bc549c9e9"},
{file = "SQLAlchemy-2.0.20-cp39-cp39-win32.whl", hash = "sha256:3423dc2a3b94125094897118b52bdf4d37daf142cbcf26d48af284b763ab90e9"}, {file = "SQLAlchemy-2.0.21-cp39-cp39-win32.whl", hash = "sha256:f9fefd6298433b6e9188252f3bff53b9ff0443c8fde27298b8a2b19f6617eeb9"},
{file = "SQLAlchemy-2.0.20-cp39-cp39-win_amd64.whl", hash = "sha256:5ed61e3463021763b853628aef8bc5d469fe12d95f82c74ef605049d810f3267"}, {file = "SQLAlchemy-2.0.21-cp39-cp39-win_amd64.whl", hash = "sha256:2e617727fe4091cedb3e4409b39368f424934c7faa78171749f704b49b4bb4ce"},
{file = "SQLAlchemy-2.0.20-py3-none-any.whl", hash = "sha256:63a368231c53c93e2b67d0c5556a9836fdcd383f7e3026a39602aad775b14acf"}, {file = "SQLAlchemy-2.0.21-py3-none-any.whl", hash = "sha256:ea7da25ee458d8f404b93eb073116156fd7d8c2a776d8311534851f28277b4ce"},
{file = "SQLAlchemy-2.0.20.tar.gz", hash = "sha256:ca8a5ff2aa7f3ade6c498aaafce25b1eaeabe4e42b73e25519183e4566a16fc6"}, {file = "SQLAlchemy-2.0.21.tar.gz", hash = "sha256:05b971ab1ac2994a14c56b35eaaa91f86ba080e9ad481b20d99d77f381bb6258"},
] ]
[package.dependencies] [package.dependencies]

View File

@ -80,6 +80,10 @@ def create_app(test_config=None):
app.register_blueprint(bp_blog) app.register_blueprint(bp_blog)
from pyproger.errors import bp as bp_errors
app.register_blueprint(bp_errors)
@security.context_processor @security.context_processor
def security_context_processor(): def security_context_processor():
return dict( return dict(

View File

@ -1,20 +1,32 @@
import locale import locale
from flask import (redirect, render_template, render_template_string, request, from flask import (
session, url_for) abort,
current_app,
redirect,
render_template,
request,
session,
url_for,
)
from ..dbase.database import (get_all_posts_by_tag, get_paginated_posts, from ..dbase.database import (
get_post, get_tags) get_all_posts_by_tag,
get_paginated_posts,
get_post,
get_tags,
)
from .blog import bp from .blog import bp
locale.setlocale(locale.LC_ALL, "") locale.setlocale(locale.LC_ALL, "")
@bp.route("/", methods=["GET"], defaults={"page": 1}) @bp.route("/", methods=["GET"], defaults={"page": 1})
@bp.route("/<int:page>", methods=["GET"]) @bp.route("/<int:page>")
def index(page=1): def index(page=1):
session["back_url"] = request.url session["back_url"] = request.url
per_page = 2 per_page = current_app.config.get("POSTS_ON_PAGE")
posts, total_pages = get_paginated_posts(page, per_page) posts, total_pages = get_paginated_posts(page, per_page)
list_pages = [ list_pages = [
x for x in range(1, total_pages + 1) if x >= page - 2 and x <= page + 2 x for x in range(1, total_pages + 1) if x >= page - 2 and x <= page + 2
@ -36,6 +48,10 @@ def post(slug=None):
back_url = session.get("back_url") back_url = session.get("back_url")
if slug is not None: if slug is not None:
current_post = get_post(slug) current_post = get_post(slug)
if current_post is None:
return abort(404)
return render_template( return render_template(
"blog/postview.html", "blog/postview.html",
title=f"pyproger - {current_post.Post.title}", title=f"pyproger - {current_post.Post.title}",
@ -44,7 +60,7 @@ def post(slug=None):
back_url=back_url, back_url=back_url,
) )
else: else:
return render_template_string("noup") abort(404)
@bp.route("/tags/") @bp.route("/tags/")
@ -59,13 +75,17 @@ def get_all_tags():
@bp.route("/tag/", methods=["GET"], defaults={"page": 1}) @bp.route("/tag/", methods=["GET"], defaults={"page": 1})
@bp.route("/tag/<path:tag>", methods=["GET"]) @bp.route("/tag/<path:tag>")
def get_posts_by_tag(page=1): def get_posts_by_tag(page=1, tag=None):
if tag is None:
tag = request.args.get("tag") tag = request.args.get("tag")
if tag is None: if tag is None:
return redirect(url_for(".get_all_tags")) return redirect(url_for(".get_all_tags"))
per_page = 2 per_page = current_app.config.get("POSTS_ON_PAGE")
posts, total_pages = get_all_posts_by_tag(tag, page, per_page) posts, total_pages = get_all_posts_by_tag(tag, page, per_page)
if posts is None:
abort(404)
list_pages = [ list_pages = [
x for x in range(1, total_pages + 1) if x >= page - 2 and x <= page + 2 x for x in range(1, total_pages + 1) if x >= page - 2 and x <= page + 2
] ]
@ -78,3 +98,25 @@ def get_posts_by_tag(page=1):
total_pages=total_pages, total_pages=total_pages,
list_pages=list_pages, list_pages=list_pages,
) )
@bp.route("/about")
def about():
return render_template(
"blog/page.html",
title="pyproger - О сайте",
menu_title="pyproger",
content_head="О сайте",
content_body="описание",
)
@bp.route("/contacts")
def contacts():
return render_template(
"blog/page.html",
title="pyproger - Контакты",
menu_title="pyproger",
content_head="Контакты",
content_body="описание",
)

View File

@ -1,22 +1,22 @@
import os import os
# Тема оформления админ панели
FLASK_ADMIN_SWATCH = "slate" FLASK_ADMIN_SWATCH = "slate"
# Create secret key so we can use sessions
# python3: secrets.token_urlsafe() # python3: secrets.token_urlsafe()
SECRET_KEY = "hxfjbcfry52" SECRET_KEY = "hxfjbcfry52"
# Create in-memory database # Настройки подключения к бд
SQLALCHEMY_DATABASE_URI = "postgresql+psycopg2://pi3c:@localhost/pyproger" SQLALCHEMY_DATABASE_URI = "postgresql+psycopg2://pi3c:@localhost/pyproger"
# For debug - show every DB query # For debug - show every DB query
SQLALCHEMY_ECHO = False SQLALCHEMY_ECHO = False
# Flask-Security config # Настройки Flask-Security
SECURITY_URL_PREFIX = "/admin" SECURITY_URL_PREFIX = "/admin"
SECURITY_PASSWORD_HASH = "pbkdf2_sha512" SECURITY_PASSWORD_HASH = "pbkdf2_sha512"
SECURITY_PASSWORD_SALT = "ATGUOHAELKiubahiughaerGOJAEGj" SECURITY_PASSWORD_SALT = "ATGUOHAELKiubahiughaerGOJAEGj"
SECURITY_TRACKABLE = True SECURITY_TRACKABLE = True
# Flask-Security URLs, overridden because they don't put a / at the end
SECURITY_LOGIN_URL = "/login/" SECURITY_LOGIN_URL = "/login/"
SECURITY_LOGOUT_URL = "/logout/" SECURITY_LOGOUT_URL = "/logout/"
SECURITY_REGISTER_URL = "/register/" SECURITY_REGISTER_URL = "/register/"
@ -26,18 +26,23 @@ SECURITY_POST_LOGOUT_VIEW = "/admin/"
SECURITY_POST_REGISTER_VIEW = "/admin/" SECURITY_POST_REGISTER_VIEW = "/admin/"
SECURITY_POST_RESET_VIEW = "/admin/" SECURITY_POST_RESET_VIEW = "/admin/"
# Flask-Security features
SECURITY_REGISTERABLE = False SECURITY_REGISTERABLE = False
SECURITY_CHANGEABLE = True SECURITY_CHANGEABLE = True
SECURITY_RECOVERABLE = False SECURITY_RECOVERABLE = False
SQLALCHEMY_TRACK_MODIFICATIONS = False SQLALCHEMY_TRACK_MODIFICATIONS = False
# For demo - no email
SECURITY_SEND_REGISTER_EMAIL = False SECURITY_SEND_REGISTER_EMAIL = False
SECURITY_SEND_PASSWORD_CHANGE_EMAIL = False SECURITY_SEND_PASSWORD_CHANGE_EMAIL = False
SECURITY_SEND_PASSWORD_RESET_EMAIL = False SECURITY_SEND_PASSWORD_RESET_EMAIL = False
SECURITY_SEND_PASSWORD_RESET_NOTICE_EMAIL = False SECURITY_SEND_PASSWORD_RESET_NOTICE_EMAIL = False
# Flask-Babel # Настройки Flask-Babel необходимые для русификации админки
LANGUAGES = ["ru"] LANGUAGES = ["ru"]
BABEL_TRANSLATION_DIRECTORIES = os.path.join(os.path.curdir, "translations") BABEL_TRANSLATION_DIRECTORIES = os.path.join(os.path.curdir, "translations")
CKEDITOR_PKG_TYPE = "full"
CKEDITOR_SERVE_LOCAL = True
CKEDITOR_ENABLE_CODESNIPPET = True
CKEDITOR_CODE_THEME = "monokai_sublime"
# Настройки блога
POSTS_ON_PAGE = 6

View File

@ -1,5 +1,3 @@
from logging import error
from . import db from . import db
from .models import Post, Tag, User from .models import Post, Tag, User
@ -44,5 +42,6 @@ def get_all_posts_by_tag(tag, page, per_page):
total_pages = ( total_pages = (
posts_query.total // per_page + [0, 1][posts_query.total % per_page != 0] posts_query.total // per_page + [0, 1][posts_query.total % per_page != 0]
) )
print(posts_query) if posts_query.total == 0:
return None, None
return posts_query, total_pages return posts_query, total_pages

8
pyproger/errors/__init__.py Executable file
View File

@ -0,0 +1,8 @@
from flask import Blueprint
bp = Blueprint(
"errors",
__name__,
)
from . import routes

4
pyproger/errors/errors.py Executable file
View File

@ -0,0 +1,4 @@

15
pyproger/errors/routes.py Executable file
View File

@ -0,0 +1,15 @@
from flask import render_template, url_for
from . import bp
@bp.app_errorhandler(404)
def handle_404(err):
return (
render_template(
"errors/404.html",
title="pyproger - Страница не найдена",
menu_title="pyproger",
),
404,
)

View File

@ -3,12 +3,4 @@
{% block tail %} {% block tail %}
{{ super() }} {{ super() }}
{{ ckeditor.load() }} {{ ckeditor.load() }}
{#
if you have set the configuration variables more than CKEDITOR_SERVE_LOCAL and CKEDITOR_PKG_TYPE,
or you need to config the CKEditor textarea, use the line below to register the configuration.
The name value should be the name of the CKEditor form field.
#}
{{ ckeditor.config(name='Title') }}
{{ ckeditor.config(name='Text') }}
{% endblock %} {% endblock %}

View File

@ -25,7 +25,7 @@
<nav class="page-header navbar p-2 navbar-expand-lg bg-dark border-bottom border-bottom-dark rounded-bottom-4" <nav class="page-header navbar p-2 navbar-expand-lg bg-dark border-bottom border-bottom-dark rounded-bottom-4"
data-bs-theme="dark"> data-bs-theme="dark">
<div class="container-fluid"> <div class="container-fluid">
<a class="navbar-brand" href="{{ url_for(".index")}}"> <a class="navbar-brand" href="{{ url_for("bp_blog.index")}}">
{% block menu_title %} {% block menu_title %}
{% endblock menu_title %}</a> {% endblock menu_title %}</a>
@ -34,8 +34,24 @@
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<div class="collapse navbar-collapse" id="navbarSupportedContent"> <div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="text-body navbar-nav me-auto mb-2 mb-lg-0"> <ul class="text-body navbar-nav me-auto mb-2 mb-lg-0 p-3">
text <li>
<a class="link-offset-2 link-underline link-underline-opacity-0 text-white"
href="{{ url_for('bp_blog.index')}}">Главная
</a>
</li>
<li>
<a class="link-offset-2 link-underline link-underline-opacity-0 text-white"
href="{{ url_for('bp_blog.get_all_tags')}}">Статьи по темам</a>
</li>
<li>
<a class="link-offset-2 link-underline link-underline-opacity-0 text-white"
href="{{ url_for('bp_blog.about') }}">О сайте</a>
</li>
<li>
<a class="link-offset-2 link-underline link-underline-opacity-0 text-white"
href="{{ url_for('bp_blog.contacts') }}">Контакты</a>
</li>
</ul> </ul>
</div> </div>
</div> </div>
@ -101,20 +117,20 @@
<ul class="list-unstyled mb-0"> <ul class="list-unstyled mb-0">
<li> <li>
<a class="link-offset-2 link-underline link-underline-opacity-0 text-white" <a class="link-offset-2 link-underline link-underline-opacity-0 text-white"
href="{{ url_for('.index')}}">Главная href="{{ url_for('bp_blog.index')}}">Главная
</a> </a>
</li> </li>
<li> <li>
<a class="link-offset-2 link-underline link-underline-opacity-0 text-white" <a class="link-offset-2 link-underline link-underline-opacity-0 text-white"
href="{{ url_for('.get_all_tags')}}">Тэги статей</a> href="{{ url_for('bp_blog.get_all_tags')}}">Статьи по темам</a>
</li> </li>
<li> <li>
<a class="link-offset-2 link-underline link-underline-opacity-0 text-white" <a class="link-offset-2 link-underline link-underline-opacity-0 text-white"
href="#!">О сайте</a> href="{{ url_for('bp_blog.about') }}">О сайте</a>
</li> </li>
<li> <li>
<a class="link-offset-2 link-underline link-underline-opacity-0 text-white" <a class="link-offset-2 link-underline link-underline-opacity-0 text-white"
href="#">Контакты</a> href="{{ url_for('bp_blog.contacts') }}">Контакты</a>
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -9,29 +9,30 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="container">
{% for p in posts %} {% for p in posts %}
<div class="containet-fluid p-3">
<a href="{{ url_for(".post", slug=p.Post.slug )}}" class="list-group-item list-group-item-action border-0"> <a href="{{ url_for(".post", slug=p.Post.slug )}}" class="list-group-item list-group-item-action border-0">
<h5 class="post-title">{{ p.Post.title | safe }}</h5> <h5 class="post-title">{{ p.Post.title | safe }}</h5>
{{ p.Post.description | safe}} {{ p.Post.description | safe}}
</a> </a>
<small> <small>
{% for t in p.Post.tags %} {% for t in p.Post.tags %}
<a class="link-offset-2 link-offset-3-hover link-underline link-underline-opacity-0 link-underline-opacity-75-hover" href="{{url_for(".get_posts_by_tag", tag=t.tag )}}"> <a class="link-offset-2 link-offset-3-hover link-underline link-underline-opacity-0 link-underline-opacity-75-hover" href="{{url_for(".get_posts_by_tag", tag=t.tag )}}">
#{{t.tag}} #{{t.tag}}
</a> </a>
{% endfor %}<br> {% endfor %}<br>
Опубликовал Опубликовал
<a href="mailto:{{p.User.email}}"> <a href="mailto:{{p.User.email}}">{{ p.User.username}}</a>
{{ p.User.username}}
</a>
{{ p.Post.create_datetime.strftime('%d %B, %Y') }} {{ p.Post.create_datetime.strftime('%d %B, %Y') }}
</small> </small>
</div>
{% endfor %} {% endfor %}
</div>
<nav class="container"> <nav class="container p-3">
<ul class="pagination justify-content-start" <ul class="pagination justify-content-center"
{% if posts.has_prev %} {% if posts.has_prev %}
<li class="page-item"><a class="page-link" href="{{ url_for(".index", page=posts.prev_num)}}">&lt;&lt;</a></li> <li class="page-item"><a class="page-link" href="{{ url_for(".index", page=posts.prev_num)}}">&lt;&lt;</a></li>
{% else %} {% else %}

View File

@ -0,0 +1,23 @@
{% extends 'blog/base.html' %}
{% block title %}
{{title}}
{% endblock %}
{% block menu_title %}
{{ menu_title }}
{% endblock %}
{% block content %}
<!-- Main Content-->
<div class="conteiner ">
<div class="conteiner-fluid text-center fs-2 p-3">
{{ content_head }}
</div>
<div class="conteiner-fluid text-start p-3">
{{ content_body | safe}}
</div>
</div>
{% endblock %}

View File

@ -9,14 +9,9 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="conteiner px-4 px-lg-5"> <div class="conteiner p-4 px-lg-5">
<br>
<h4>{{ post.Post.title | safe }}</h4> <h4>{{ post.Post.title | safe }}</h4>
{{ post.Post.text | safe }} {{ post.Post.text | safe }}
<a class="link-offset-2 link-offset-3-hover link-underline link-underline-opacity-0 link-underline-opacity-75-hover" href="{{ back_url }}">
Назад
</a>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,28 @@
{% extends 'blog/base.html' %}
{% block title %}{{title}}{% endblock title %}
{% block menu_title %} {{ menu_title }} {% endblock menu_title %}
{% block content %}
<div class="container-fluid p-3">
<div class="row text-center p-2">
<div class="fs-4">Страница не найдена</div>
</div>
<div class="row text-center p-1">
<strong class="fs-1">&lt;404&gt;</strong>
</div>
<div class="row text-center p-1">
<div class="fs-6">Запрашиваемая Вами страница не найдена</div>
<div class="fs-6">Можете выбрать статьи по темам, перейдя в соответствующий раздел
<a class="link-offset-2 link-offset-3-hover link-underline link-underline-opacity-0 link-underline-opacity-75-hover"
href="{{ url_for('bp_blog.get_all_tags')}}">по ссылке</a></div>
<div class="fs-6">или просмотреть все статьи на <a class="link-offset-2 link-offset-3-hover link-underline link-underline-opacity-0 link-underline-opacity-75-hover"
href="{{ url_for('bp_blog.index')}}">главной странице</a></div>
</div>
</div>
{% endblock content%}

View File

@ -1,11 +1,11 @@
import os import os
from flask import Blueprint, request from flask import Blueprint, current_app, request
from flask_babel import Babel from flask_babel import Babel
def get_locale(): def get_locale():
translations = ["ru"] translations = current_app.config.get("LANGUAGES")
return request.accept_languages.best_match(translations) return request.accept_languages.best_match(translations)