├── .gitattributes ├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── poetry.lock ├── pyproject.toml ├── sweetify ├── __init__.py ├── __version__.py ├── encoder.py ├── sweetify.py ├── templatetags │ ├── __init__.py │ └── sweetify.py └── views.py └── tests ├── __init__.py ├── test_settings.py └── test_sweetify.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *.cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # Jupyter Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # SageMath parsed files 79 | *.sage.py 80 | 81 | # Environments 82 | .env 83 | .venv 84 | .envrc 85 | env/ 86 | venv/ 87 | ENV/ 88 | 89 | # Spyder project settings 90 | .spyderproject 91 | .spyproject 92 | 93 | # Rope project settings 94 | .ropeproject 95 | 96 | # mkdocs documentation 97 | /site 98 | 99 | # mypy 100 | .mypy_cache/ 101 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | python: 4 | - 2.7 5 | - 3.6 6 | - nightly 7 | 8 | env: 9 | - DJANGO='django>=1.8.0,<1.9.0' 10 | - DJANGO='django>=1.10.0,<1.11.0' 11 | - DJANGO='django>=1.11.0,<2.0' 12 | - DJANGO='django>=2.1.0,<2.2.0' 13 | - DJANGO='https://github.com/django/django/archive/master.tar.gz' 14 | 15 | before_install: 16 | - pip install coveralls 17 | 18 | install: 19 | - pip install --upgrade poetry 20 | - poetry install -v 21 | - poetry run pip install $DJANGO 22 | 23 | script: 24 | - DJANGO_SETTINGS_MODULE=tests.test_settings poetry run pytest --cov=sweetify tests/ 25 | 26 | after_success: 27 | - coveralls 28 | 29 | matrix: 30 | exclude: 31 | - python: 2.7 32 | env: DJANGO='https://github.com/django/django/archive/master.tar.gz' 33 | - python: 2.7 34 | env: DJANGO='django>=2.1.0,<2.2.0' 35 | allow_failures: 36 | - env: DJANGO='https://github.com/django/django/archive/master.tar.gz' 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021, Atrox (https://atrox.dev) 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of Sweetify nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: install test 2 | 3 | install: 4 | @poetry install 5 | 6 | test: 7 | @DJANGO_SETTINGS_MODULE=tests.test_settings pytest 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sweetify - SweetAlert for Django 2 | 3 | [![Build Status](https://img.shields.io/travis/Atrox/sweetify-django.svg?style=flat-square)](https://travis-ci.org/Atrox/sweetify-django) 4 | [![Latest Version](https://img.shields.io/pypi/v/sweetify.svg?style=flat-square)](https://pypi.python.org/pypi/sweetify) 5 | [![Coverage Status](https://img.shields.io/coveralls/Atrox/sweetify-django.svg?style=flat-square)](https://coveralls.io/r/Atrox/sweetify-django) 6 | 7 | **Sweetify** allows you to use [SweetAlert](http://t4t5.github.io/sweetalert/) or [SweetAlert2](https://github.com/limonte/sweetalert2) for your temporary messages. 8 | _See the examples below, to see how to use this library_ 9 | 10 | ## Installation 11 | 12 | **Note: This package does not provide the client-side files of SweetAlert. You have to provide them yourself.** 13 | 14 | Install the latest version with `pip`: 15 | 16 | ```bash 17 | pip install --upgrade sweetify 18 | ``` 19 | 20 | Then you have to add `sweetify` to your django apps: 21 | 22 | ```python 23 | INSTALLED_APPS = [ 24 | ... 25 | 'sweetify' 26 | ] 27 | ``` 28 | 29 | Next up you have to specify, in your settings, which library you are using (SweetAlert or SweetAlert2): 30 | 31 | ```python 32 | # possible options: 'sweetalert', 'sweetalert2' - default is 'sweetalert2' 33 | SWEETIFY_SWEETALERT_LIBRARY = 'sweetalert2' 34 | ``` 35 | 36 | Next add the following lines to the bottom of your layout/base template: 37 | 38 | ```jinja 39 | ... 40 | 41 | {% load sweetify %} 42 | {% sweetify %} 43 | 44 | 45 | 46 | ``` 47 | 48 | ## Usage 49 | 50 | You can now easily create alerts in your views with any of the following methods provided by **Sweetify**: 51 | 52 | ```python 53 | import sweetify 54 | 55 | # Base method with no type specified 56 | sweetify.sweetalert(self.request, 'Westworld is awesome', text='Really... if you have the chance - watch it!', persistent='I agree!') 57 | 58 | # Additional methods with the type already defined 59 | sweetify.info(self.request, 'Message sent', button='Ok', timer=3000) 60 | sweetify.success(self.request, 'You successfully changed your password') 61 | sweetify.error(self.request, 'Some error happened here - reload the site', persistent=':(') 62 | sweetify.warning(self.request, 'This is a warning... I guess') 63 | ``` 64 | 65 | We also support toast messages _(SweetAlert2 only)_ 66 | 67 | ```python 68 | import sweetify 69 | 70 | # Base method, default icon is set to success 71 | sweetify.toast(self.request, 'Cheers to new toast') 72 | 73 | sweetify.toast(self.request, 'Oops, something went wrong !', icon="error", timer=3000) 74 | sweetify.toast(self.request, 'Persistent toast that only goes away once clicked', icon='warning', persistent="Bye toast!") 75 | ``` 76 | 77 | Additionally, you can issue multiple alerts without reloading the page **ONLY** if you are using SweetAlerts 2. To do so, you must define your options in a dictionary: 78 | 79 | ```python 80 | import sweetify 81 | 82 | # Call two consecutive alerts (args1 is the options dict for the first alert and args2 the one for the second alert): 83 | sweetify.multiple(self.request, args1, args2) 84 | 85 | # Call five consecutive alerts: 86 | sweetify.multiple(self.request, args1, args2, args3, args4, args5) 87 | ``` 88 | 89 | ## Example Usage 90 | 91 | ```python 92 | import sweetify 93 | 94 | def test_view(request): 95 | sweetify.success(request, 'You did it', text='Good job! You successfully showed a SweetAlert message', persistent='Hell yeah') 96 | return redirect('/') 97 | ``` 98 | 99 | Example usage for multiple alerts: 100 | 101 | ```python 102 | import sweetify 103 | 104 | def test_view(request): 105 | args1 = dict(title='Test1', icon='info', text="Text placeholder1", button="Next") 106 | args2 = dict(title='Test2', icon='success', text="Text placeholder2", timer=5000, timerProgressBar='true', persistent="Close") 107 | sweetify.multiple(request, args1, args2) 108 | return redirect('/') 109 | ``` 110 | 111 | ## Replacement for SuccessMessageMixin 112 | 113 | Sweetify includes a drop-in replacement for `SuccessMessageMixin`. 114 | Just replace the Django mixin with Sweetify's `SweetifySuccessMixin` and you are good to go. 115 | 116 | ```python 117 | from sweetify.views import SweetifySuccessMixin 118 | 119 | class TestUpdateView(SweetifySuccessMixin, UpdateView): 120 | model = TestModel 121 | fields = ['text'] 122 | success_message = 'TestModel successfully updated!' 123 | ``` 124 | 125 | ## Options 126 | 127 | **By default, all alerts will dismiss after a sensible default number of seconds.** 128 | 129 | Default options set by **Sweetify**: 130 | 131 | ```python 132 | sweetify.DEFAULT_OPTS = { 133 | 'showConfirmButton': False, 134 | 'timer': 2500, 135 | 'allowOutsideClick': True, 136 | 'confirmButtonText': 'OK', 137 | } 138 | ``` 139 | 140 | The following special options provided by **Sweetify** are available: 141 | 142 | ```python 143 | # Shows the alert with a button, but will still close automatically 144 | sweetify.sweetalert(self.request, 'Title', button=True) 145 | sweetify.sweetalert(self.request, 'Title', button='Awesome!') # Custom text for the button 146 | 147 | # Shows the alert with a button and only closes if the button is pressed 148 | sweetify.sweetalert(self.request, 'Title', persistent=True) 149 | sweetify.sweetalert(self.request, 'Title', persistent='Awesome!') # Custom text for the button 150 | ``` 151 | 152 | You also can use any other available option that [SweetAlert accepts](http://t4t5.github.io/sweetalert/): 153 | 154 | ```python 155 | sweetify.info(self.request, 'Sweet!', text='Here is a custom image', imageUrl='images/thumbs-up.jpg', timer=5000) 156 | ``` 157 | 158 | If you use [CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) you can use the optional `nonce` parameter on the `sweetify` tag: 159 | 160 | ```jinja 161 | {% load sweetify %} 162 | 163 | 164 | {% sweetify nonce="XYZ" %} 165 | 166 | 167 | {% sweetify nonce=request.csp_nonce %} 168 | ``` 169 | 170 | ## Development 171 | 172 | Use the `Makefile`to execute common tasks: 173 | 174 | - Install dependencies 175 | 176 | ```shell 177 | $ make install 178 | ``` 179 | 180 | - Run all tests 181 | 182 | ```shell 183 | $ make test 184 | ``` 185 | 186 | ## Contributing 187 | 188 | Everyone is encouraged to help improve this project. Here are a few ways you can help: 189 | 190 | - [Report bugs](https://github.com/atrox/sweetify-django/issues) 191 | - Fix bugs and [submit pull requests](https://github.com/atrox/sweetify-django/pulls) 192 | - Write, clarify, or fix documentation 193 | - Suggest or add new features 194 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "asgiref" 3 | version = "3.4.1" 4 | description = "ASGI specs, helper code, and adapters" 5 | category = "dev" 6 | optional = false 7 | python-versions = ">=3.6" 8 | 9 | [package.dependencies] 10 | typing-extensions = {version = "*", markers = "python_version < \"3.8\""} 11 | 12 | [package.extras] 13 | tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"] 14 | 15 | [[package]] 16 | name = "atomicwrites" 17 | version = "1.4.0" 18 | description = "Atomic file writes." 19 | category = "dev" 20 | optional = false 21 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 22 | 23 | [[package]] 24 | name = "attrs" 25 | version = "21.4.0" 26 | description = "Classes Without Boilerplate" 27 | category = "dev" 28 | optional = false 29 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 30 | 31 | [package.extras] 32 | dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] 33 | docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] 34 | tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] 35 | tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] 36 | 37 | [[package]] 38 | name = "black" 39 | version = "21.12b0" 40 | description = "The uncompromising code formatter." 41 | category = "dev" 42 | optional = false 43 | python-versions = ">=3.6.2" 44 | 45 | [package.dependencies] 46 | click = ">=7.1.2" 47 | dataclasses = {version = ">=0.6", markers = "python_version < \"3.7\""} 48 | mypy-extensions = ">=0.4.3" 49 | pathspec = ">=0.9.0,<1" 50 | platformdirs = ">=2" 51 | tomli = ">=0.2.6,<2.0.0" 52 | typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} 53 | typing-extensions = [ 54 | {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}, 55 | {version = "!=3.10.0.1", markers = "python_version >= \"3.10\""}, 56 | ] 57 | 58 | [package.extras] 59 | colorama = ["colorama (>=0.4.3)"] 60 | d = ["aiohttp (>=3.7.4)"] 61 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 62 | python2 = ["typed-ast (>=1.4.3)"] 63 | uvloop = ["uvloop (>=0.15.2)"] 64 | 65 | [[package]] 66 | name = "click" 67 | version = "8.0.4" 68 | description = "Composable command line interface toolkit" 69 | category = "dev" 70 | optional = false 71 | python-versions = ">=3.6" 72 | 73 | [package.dependencies] 74 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 75 | importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} 76 | 77 | [[package]] 78 | name = "colorama" 79 | version = "0.4.4" 80 | description = "Cross-platform colored terminal text." 81 | category = "dev" 82 | optional = false 83 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 84 | 85 | [[package]] 86 | name = "coverage" 87 | version = "6.2" 88 | description = "Code coverage measurement for Python" 89 | category = "dev" 90 | optional = false 91 | python-versions = ">=3.6" 92 | 93 | [package.dependencies] 94 | tomli = {version = "*", optional = true, markers = "extra == \"toml\""} 95 | 96 | [package.extras] 97 | toml = ["tomli"] 98 | 99 | [[package]] 100 | name = "dataclasses" 101 | version = "0.8" 102 | description = "A backport of the dataclasses module for Python 3.6" 103 | category = "dev" 104 | optional = false 105 | python-versions = ">=3.6, <3.7" 106 | 107 | [[package]] 108 | name = "django" 109 | version = "3.2.13" 110 | description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design." 111 | category = "dev" 112 | optional = false 113 | python-versions = ">=3.6" 114 | 115 | [package.dependencies] 116 | asgiref = ">=3.3.2,<4" 117 | pytz = "*" 118 | sqlparse = ">=0.2.2" 119 | 120 | [package.extras] 121 | argon2 = ["argon2-cffi (>=19.1.0)"] 122 | bcrypt = ["bcrypt"] 123 | 124 | [[package]] 125 | name = "importlib-metadata" 126 | version = "4.8.3" 127 | description = "Read metadata from Python packages" 128 | category = "dev" 129 | optional = false 130 | python-versions = ">=3.6" 131 | 132 | [package.dependencies] 133 | typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} 134 | zipp = ">=0.5" 135 | 136 | [package.extras] 137 | docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] 138 | perf = ["ipython"] 139 | testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] 140 | 141 | [[package]] 142 | name = "iniconfig" 143 | version = "1.1.1" 144 | description = "iniconfig: brain-dead simple config-ini parsing" 145 | category = "dev" 146 | optional = false 147 | python-versions = "*" 148 | 149 | [[package]] 150 | name = "mypy-extensions" 151 | version = "0.4.3" 152 | description = "Experimental type system extensions for programs checked with the mypy typechecker." 153 | category = "dev" 154 | optional = false 155 | python-versions = "*" 156 | 157 | [[package]] 158 | name = "packaging" 159 | version = "21.3" 160 | description = "Core utilities for Python packages" 161 | category = "dev" 162 | optional = false 163 | python-versions = ">=3.6" 164 | 165 | [package.dependencies] 166 | pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" 167 | 168 | [[package]] 169 | name = "pathspec" 170 | version = "0.9.0" 171 | description = "Utility library for gitignore style pattern matching of file paths." 172 | category = "dev" 173 | optional = false 174 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" 175 | 176 | [[package]] 177 | name = "platformdirs" 178 | version = "2.4.0" 179 | description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 180 | category = "dev" 181 | optional = false 182 | python-versions = ">=3.6" 183 | 184 | [package.extras] 185 | docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] 186 | test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] 187 | 188 | [[package]] 189 | name = "pluggy" 190 | version = "1.0.0" 191 | description = "plugin and hook calling mechanisms for python" 192 | category = "dev" 193 | optional = false 194 | python-versions = ">=3.6" 195 | 196 | [package.dependencies] 197 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 198 | 199 | [package.extras] 200 | dev = ["pre-commit", "tox"] 201 | testing = ["pytest", "pytest-benchmark"] 202 | 203 | [[package]] 204 | name = "py" 205 | version = "1.11.0" 206 | description = "library with cross-python path, ini-parsing, io, code, log facilities" 207 | category = "dev" 208 | optional = false 209 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 210 | 211 | [[package]] 212 | name = "pyparsing" 213 | version = "3.0.7" 214 | description = "Python parsing module" 215 | category = "dev" 216 | optional = false 217 | python-versions = ">=3.6" 218 | 219 | [package.extras] 220 | diagrams = ["jinja2", "railroad-diagrams"] 221 | 222 | [[package]] 223 | name = "pytest" 224 | version = "6.2.5" 225 | description = "pytest: simple powerful testing with Python" 226 | category = "dev" 227 | optional = false 228 | python-versions = ">=3.6" 229 | 230 | [package.dependencies] 231 | atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} 232 | attrs = ">=19.2.0" 233 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 234 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 235 | iniconfig = "*" 236 | packaging = "*" 237 | pluggy = ">=0.12,<2.0" 238 | py = ">=1.8.2" 239 | toml = "*" 240 | 241 | [package.extras] 242 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] 243 | 244 | [[package]] 245 | name = "pytest-cov" 246 | version = "3.0.0" 247 | description = "Pytest plugin for measuring coverage." 248 | category = "dev" 249 | optional = false 250 | python-versions = ">=3.6" 251 | 252 | [package.dependencies] 253 | coverage = {version = ">=5.2.1", extras = ["toml"]} 254 | pytest = ">=4.6" 255 | 256 | [package.extras] 257 | testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] 258 | 259 | [[package]] 260 | name = "pytz" 261 | version = "2022.1" 262 | description = "World timezone definitions, modern and historical" 263 | category = "dev" 264 | optional = false 265 | python-versions = "*" 266 | 267 | [[package]] 268 | name = "sqlparse" 269 | version = "0.4.2" 270 | description = "A non-validating SQL parser." 271 | category = "dev" 272 | optional = false 273 | python-versions = ">=3.5" 274 | 275 | [[package]] 276 | name = "toml" 277 | version = "0.10.2" 278 | description = "Python Library for Tom's Obvious, Minimal Language" 279 | category = "dev" 280 | optional = false 281 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 282 | 283 | [[package]] 284 | name = "tomli" 285 | version = "1.2.3" 286 | description = "A lil' TOML parser" 287 | category = "dev" 288 | optional = false 289 | python-versions = ">=3.6" 290 | 291 | [[package]] 292 | name = "typed-ast" 293 | version = "1.5.3" 294 | description = "a fork of Python 2 and 3 ast modules with type comment support" 295 | category = "dev" 296 | optional = false 297 | python-versions = ">=3.6" 298 | 299 | [[package]] 300 | name = "typing-extensions" 301 | version = "4.1.1" 302 | description = "Backported and Experimental Type Hints for Python 3.6+" 303 | category = "dev" 304 | optional = false 305 | python-versions = ">=3.6" 306 | 307 | [[package]] 308 | name = "typing-extensions" 309 | version = "4.2.0" 310 | description = "Backported and Experimental Type Hints for Python 3.7+" 311 | category = "dev" 312 | optional = false 313 | python-versions = ">=3.7" 314 | 315 | [[package]] 316 | name = "zipp" 317 | version = "3.6.0" 318 | description = "Backport of pathlib-compatible object wrapper for zip files" 319 | category = "dev" 320 | optional = false 321 | python-versions = ">=3.6" 322 | 323 | [package.extras] 324 | docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] 325 | testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] 326 | 327 | [metadata] 328 | lock-version = "1.1" 329 | python-versions = '^3.6.2' 330 | content-hash = "0ff730d1044c815844885c661abfb2fe897ff79d67ea7f2c4fefd2d6d9fb27dd" 331 | 332 | [metadata.files] 333 | asgiref = [ 334 | {file = "asgiref-3.4.1-py3-none-any.whl", hash = "sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"}, 335 | {file = "asgiref-3.4.1.tar.gz", hash = "sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9"}, 336 | ] 337 | atomicwrites = [ 338 | {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, 339 | {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, 340 | ] 341 | attrs = [ 342 | {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, 343 | {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, 344 | ] 345 | black = [ 346 | {file = "black-21.12b0-py3-none-any.whl", hash = "sha256:a615e69ae185e08fdd73e4715e260e2479c861b5740057fde6e8b4e3b7dd589f"}, 347 | {file = "black-21.12b0.tar.gz", hash = "sha256:77b80f693a569e2e527958459634f18df9b0ba2625ba4e0c2d5da5be42e6f2b3"}, 348 | ] 349 | click = [ 350 | {file = "click-8.0.4-py3-none-any.whl", hash = "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1"}, 351 | {file = "click-8.0.4.tar.gz", hash = "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"}, 352 | ] 353 | colorama = [ 354 | {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, 355 | {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, 356 | ] 357 | coverage = [ 358 | {file = "coverage-6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6dbc1536e105adda7a6312c778f15aaabe583b0e9a0b0a324990334fd458c94b"}, 359 | {file = "coverage-6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:174cf9b4bef0db2e8244f82059a5a72bd47e1d40e71c68ab055425172b16b7d0"}, 360 | {file = "coverage-6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:92b8c845527eae547a2a6617d336adc56394050c3ed8a6918683646328fbb6da"}, 361 | {file = "coverage-6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c7912d1526299cb04c88288e148c6c87c0df600eca76efd99d84396cfe00ef1d"}, 362 | {file = "coverage-6.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5d2033d5db1d58ae2d62f095e1aefb6988af65b4b12cb8987af409587cc0739"}, 363 | {file = "coverage-6.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3feac4084291642165c3a0d9eaebedf19ffa505016c4d3db15bfe235718d4971"}, 364 | {file = "coverage-6.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:276651978c94a8c5672ea60a2656e95a3cce2a3f31e9fb2d5ebd4c215d095840"}, 365 | {file = "coverage-6.2-cp310-cp310-win32.whl", hash = "sha256:f506af4f27def639ba45789fa6fde45f9a217da0be05f8910458e4557eed020c"}, 366 | {file = "coverage-6.2-cp310-cp310-win_amd64.whl", hash = "sha256:3f7c17209eef285c86f819ff04a6d4cbee9b33ef05cbcaae4c0b4e8e06b3ec8f"}, 367 | {file = "coverage-6.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:13362889b2d46e8d9f97c421539c97c963e34031ab0cb89e8ca83a10cc71ac76"}, 368 | {file = "coverage-6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22e60a3ca5acba37d1d4a2ee66e051f5b0e1b9ac950b5b0cf4aa5366eda41d47"}, 369 | {file = "coverage-6.2-cp311-cp311-win_amd64.whl", hash = "sha256:b637c57fdb8be84e91fac60d9325a66a5981f8086c954ea2772efe28425eaf64"}, 370 | {file = "coverage-6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f467bbb837691ab5a8ca359199d3429a11a01e6dfb3d9dcc676dc035ca93c0a9"}, 371 | {file = "coverage-6.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2641f803ee9f95b1f387f3e8f3bf28d83d9b69a39e9911e5bfee832bea75240d"}, 372 | {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1219d760ccfafc03c0822ae2e06e3b1248a8e6d1a70928966bafc6838d3c9e48"}, 373 | {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9a2b5b52be0a8626fcbffd7e689781bf8c2ac01613e77feda93d96184949a98e"}, 374 | {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8e2c35a4c1f269704e90888e56f794e2d9c0262fb0c1b1c8c4ee44d9b9e77b5d"}, 375 | {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5d6b09c972ce9200264c35a1d53d43ca55ef61836d9ec60f0d44273a31aa9f17"}, 376 | {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e3db840a4dee542e37e09f30859f1612da90e1c5239a6a2498c473183a50e781"}, 377 | {file = "coverage-6.2-cp36-cp36m-win32.whl", hash = "sha256:4e547122ca2d244f7c090fe3f4b5a5861255ff66b7ab6d98f44a0222aaf8671a"}, 378 | {file = "coverage-6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:01774a2c2c729619760320270e42cd9e797427ecfddd32c2a7b639cdc481f3c0"}, 379 | {file = "coverage-6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb8b8ee99b3fffe4fd86f4c81b35a6bf7e4462cba019997af2fe679365db0c49"}, 380 | {file = "coverage-6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:619346d57c7126ae49ac95b11b0dc8e36c1dd49d148477461bb66c8cf13bb521"}, 381 | {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0a7726f74ff63f41e95ed3a89fef002916c828bb5fcae83b505b49d81a066884"}, 382 | {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cfd9386c1d6f13b37e05a91a8583e802f8059bebfccde61a418c5808dea6bbfa"}, 383 | {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:17e6c11038d4ed6e8af1407d9e89a2904d573be29d51515f14262d7f10ef0a64"}, 384 | {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c254b03032d5a06de049ce8bca8338a5185f07fb76600afff3c161e053d88617"}, 385 | {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dca38a21e4423f3edb821292e97cec7ad38086f84313462098568baedf4331f8"}, 386 | {file = "coverage-6.2-cp37-cp37m-win32.whl", hash = "sha256:600617008aa82032ddeace2535626d1bc212dfff32b43989539deda63b3f36e4"}, 387 | {file = "coverage-6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:bf154ba7ee2fd613eb541c2bc03d3d9ac667080a737449d1a3fb342740eb1a74"}, 388 | {file = "coverage-6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9afb5b746781fc2abce26193d1c817b7eb0e11459510fba65d2bd77fe161d9e"}, 389 | {file = "coverage-6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edcada2e24ed68f019175c2b2af2a8b481d3d084798b8c20d15d34f5c733fa58"}, 390 | {file = "coverage-6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9c8c4283e17690ff1a7427123ffb428ad6a52ed720d550e299e8291e33184dc"}, 391 | {file = "coverage-6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f614fc9956d76d8a88a88bb41ddc12709caa755666f580af3a688899721efecd"}, 392 | {file = "coverage-6.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9365ed5cce5d0cf2c10afc6add145c5037d3148585b8ae0e77cc1efdd6aa2953"}, 393 | {file = "coverage-6.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8bdfe9ff3a4ea37d17f172ac0dff1e1c383aec17a636b9b35906babc9f0f5475"}, 394 | {file = "coverage-6.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:63c424e6f5b4ab1cf1e23a43b12f542b0ec2e54f99ec9f11b75382152981df57"}, 395 | {file = "coverage-6.2-cp38-cp38-win32.whl", hash = "sha256:49dbff64961bc9bdd2289a2bda6a3a5a331964ba5497f694e2cbd540d656dc1c"}, 396 | {file = "coverage-6.2-cp38-cp38-win_amd64.whl", hash = "sha256:9a29311bd6429be317c1f3fe4bc06c4c5ee45e2fa61b2a19d4d1d6111cb94af2"}, 397 | {file = "coverage-6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03b20e52b7d31be571c9c06b74746746d4eb82fc260e594dc662ed48145e9efd"}, 398 | {file = "coverage-6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:215f8afcc02a24c2d9a10d3790b21054b58d71f4b3c6f055d4bb1b15cecce685"}, 399 | {file = "coverage-6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a4bdeb0a52d1d04123b41d90a4390b096f3ef38eee35e11f0b22c2d031222c6c"}, 400 | {file = "coverage-6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c332d8f8d448ded473b97fefe4a0983265af21917d8b0cdcb8bb06b2afe632c3"}, 401 | {file = "coverage-6.2-cp39-cp39-win32.whl", hash = "sha256:6e1394d24d5938e561fbeaa0cd3d356207579c28bd1792f25a068743f2d5b282"}, 402 | {file = "coverage-6.2-cp39-cp39-win_amd64.whl", hash = "sha256:86f2e78b1eff847609b1ca8050c9e1fa3bd44ce755b2ec30e70f2d3ba3844644"}, 403 | {file = "coverage-6.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:5829192582c0ec8ca4a2532407bc14c2f338d9878a10442f5d03804a95fac9de"}, 404 | {file = "coverage-6.2.tar.gz", hash = "sha256:e2cad8093172b7d1595b4ad66f24270808658e11acf43a8f95b41276162eb5b8"}, 405 | ] 406 | dataclasses = [ 407 | {file = "dataclasses-0.8-py3-none-any.whl", hash = "sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf"}, 408 | {file = "dataclasses-0.8.tar.gz", hash = "sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97"}, 409 | ] 410 | django = [ 411 | {file = "Django-3.2.13-py3-none-any.whl", hash = "sha256:b896ca61edc079eb6bbaa15cf6071eb69d6aac08cce5211583cfb41515644fdf"}, 412 | {file = "Django-3.2.13.tar.gz", hash = "sha256:6d93497a0a9bf6ba0e0b1a29cccdc40efbfc76297255b1309b3a884a688ec4b6"}, 413 | ] 414 | importlib-metadata = [ 415 | {file = "importlib_metadata-4.8.3-py3-none-any.whl", hash = "sha256:65a9576a5b2d58ca44d133c42a241905cc45e34d2c06fd5ba2bafa221e5d7b5e"}, 416 | {file = "importlib_metadata-4.8.3.tar.gz", hash = "sha256:766abffff765960fcc18003801f7044eb6755ffae4521c8e8ce8e83b9c9b0668"}, 417 | ] 418 | iniconfig = [ 419 | {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, 420 | {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, 421 | ] 422 | mypy-extensions = [ 423 | {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, 424 | {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, 425 | ] 426 | packaging = [ 427 | {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, 428 | {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, 429 | ] 430 | pathspec = [ 431 | {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, 432 | {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, 433 | ] 434 | platformdirs = [ 435 | {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"}, 436 | {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"}, 437 | ] 438 | pluggy = [ 439 | {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, 440 | {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, 441 | ] 442 | py = [ 443 | {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, 444 | {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, 445 | ] 446 | pyparsing = [ 447 | {file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"}, 448 | {file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"}, 449 | ] 450 | pytest = [ 451 | {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, 452 | {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, 453 | ] 454 | pytest-cov = [ 455 | {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, 456 | {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, 457 | ] 458 | pytz = [ 459 | {file = "pytz-2022.1-py2.py3-none-any.whl", hash = "sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c"}, 460 | {file = "pytz-2022.1.tar.gz", hash = "sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7"}, 461 | ] 462 | sqlparse = [ 463 | {file = "sqlparse-0.4.2-py3-none-any.whl", hash = "sha256:48719e356bb8b42991bdbb1e8b83223757b93789c00910a616a071910ca4a64d"}, 464 | {file = "sqlparse-0.4.2.tar.gz", hash = "sha256:0c00730c74263a94e5a9919ade150dfc3b19c574389985446148402998287dae"}, 465 | ] 466 | toml = [ 467 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, 468 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, 469 | ] 470 | tomli = [ 471 | {file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"}, 472 | {file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"}, 473 | ] 474 | typed-ast = [ 475 | {file = "typed_ast-1.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ad3b48cf2b487be140072fb86feff36801487d4abb7382bb1929aaac80638ea"}, 476 | {file = "typed_ast-1.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:542cd732351ba8235f20faa0fc7398946fe1a57f2cdb289e5497e1e7f48cfedb"}, 477 | {file = "typed_ast-1.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc2c11ae59003d4a26dda637222d9ae924387f96acae9492df663843aefad55"}, 478 | {file = "typed_ast-1.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd5df1313915dbd70eaaa88c19030b441742e8b05e6103c631c83b75e0435ccc"}, 479 | {file = "typed_ast-1.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:e34f9b9e61333ecb0f7d79c21c28aa5cd63bec15cb7e1310d7d3da6ce886bc9b"}, 480 | {file = "typed_ast-1.5.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f818c5b81966d4728fec14caa338e30a70dfc3da577984d38f97816c4b3071ec"}, 481 | {file = "typed_ast-1.5.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3042bfc9ca118712c9809201f55355479cfcdc17449f9f8db5e744e9625c6805"}, 482 | {file = "typed_ast-1.5.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4fff9fdcce59dc61ec1b317bdb319f8f4e6b69ebbe61193ae0a60c5f9333dc49"}, 483 | {file = "typed_ast-1.5.3-cp36-cp36m-win_amd64.whl", hash = "sha256:8e0b8528838ffd426fea8d18bde4c73bcb4167218998cc8b9ee0a0f2bfe678a6"}, 484 | {file = "typed_ast-1.5.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ef1d96ad05a291f5c36895d86d1375c0ee70595b90f6bb5f5fdbee749b146db"}, 485 | {file = "typed_ast-1.5.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed44e81517364cb5ba367e4f68fca01fba42a7a4690d40c07886586ac267d9b9"}, 486 | {file = "typed_ast-1.5.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f60d9de0d087454c91b3999a296d0c4558c1666771e3460621875021bf899af9"}, 487 | {file = "typed_ast-1.5.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9e237e74fd321a55c90eee9bc5d44be976979ad38a29bbd734148295c1ce7617"}, 488 | {file = "typed_ast-1.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ee852185964744987609b40aee1d2eb81502ae63ee8eef614558f96a56c1902d"}, 489 | {file = "typed_ast-1.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:27e46cdd01d6c3a0dd8f728b6a938a6751f7bd324817501c15fb056307f918c6"}, 490 | {file = "typed_ast-1.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d64dabc6336ddc10373922a146fa2256043b3b43e61f28961caec2a5207c56d5"}, 491 | {file = "typed_ast-1.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8cdf91b0c466a6c43f36c1964772918a2c04cfa83df8001ff32a89e357f8eb06"}, 492 | {file = "typed_ast-1.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:9cc9e1457e1feb06b075c8ef8aeb046a28ec351b1958b42c7c31c989c841403a"}, 493 | {file = "typed_ast-1.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e20d196815eeffb3d76b75223e8ffed124e65ee62097e4e73afb5fec6b993e7a"}, 494 | {file = "typed_ast-1.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:37e5349d1d5de2f4763d534ccb26809d1c24b180a477659a12c4bde9dd677d74"}, 495 | {file = "typed_ast-1.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9f1a27592fac87daa4e3f16538713d705599b0a27dfe25518b80b6b017f0a6d"}, 496 | {file = "typed_ast-1.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8831479695eadc8b5ffed06fdfb3e424adc37962a75925668deeb503f446c0a3"}, 497 | {file = "typed_ast-1.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:20d5118e494478ef2d3a2702d964dae830aedd7b4d3b626d003eea526be18718"}, 498 | {file = "typed_ast-1.5.3.tar.gz", hash = "sha256:27f25232e2dd0edfe1f019d6bfaaf11e86e657d9bdb7b0956db95f560cceb2b3"}, 499 | ] 500 | typing-extensions = [ 501 | {file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"}, 502 | {file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"}, 503 | {file = "typing_extensions-4.2.0-py3-none-any.whl", hash = "sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708"}, 504 | {file = "typing_extensions-4.2.0.tar.gz", hash = "sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376"}, 505 | ] 506 | zipp = [ 507 | {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"}, 508 | {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"}, 509 | ] 510 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = 'sweetify' 3 | version = '2.3.1' 4 | description = 'SweetAlert integration for Django' 5 | authors = ['Atrox '] 6 | license = 'BSD-3-Clause' 7 | 8 | readme = 'README.md' 9 | 10 | homepage = 'https://github.com/Atrox/sweetify-django' 11 | repository = 'https://github.com/Atrox/sweetify-django' 12 | 13 | keywords = ['sweetalert', 'django', 'messages'] 14 | 15 | 16 | [tool.poetry.dependencies] 17 | python = '^3.6.2' 18 | 19 | [tool.poetry.dev-dependencies] 20 | pytest = '^6.2' 21 | pytest-cov = '^3.0' 22 | django = "^3.2" 23 | black = "^21.11b1" 24 | 25 | [build-system] 26 | requires = ['poetry>=0.12'] 27 | build-backend = 'poetry.masonry.api' 28 | 29 | [tool.black] 30 | line-length = 120 31 | target-version = ['py37'] 32 | include = '\.pyi?$' 33 | -------------------------------------------------------------------------------- /sweetify/__init__.py: -------------------------------------------------------------------------------- 1 | from .sweetify import * 2 | -------------------------------------------------------------------------------- /sweetify/__version__.py: -------------------------------------------------------------------------------- 1 | # _____ _ _ __ 2 | # / ___| | | (_) / _| 3 | # \ `--. __ __ ___ ___ | |_ _ | |_ _ _ 4 | # `--. \\ \ /\ / // _ \ / _ \| __|| || _|| | | | 5 | # /\__/ / \ V V /| __/| __/| |_ | || | | |_| | 6 | # \____/ \_/\_/ \___| \___| \__||_||_| \__, | 7 | # __/ | 8 | # |___/ 9 | 10 | __version__ = "2.3.1" 11 | -------------------------------------------------------------------------------- /sweetify/encoder.py: -------------------------------------------------------------------------------- 1 | from django.core.serializers.json import DjangoJSONEncoder 2 | from django.utils.encoding import force_str 3 | from django.utils.functional import Promise 4 | 5 | 6 | class LazyEncoder(DjangoJSONEncoder): 7 | def default(self, obj): 8 | if isinstance(obj, Promise): 9 | return force_str(obj) 10 | return super(LazyEncoder, self).default(obj) 11 | -------------------------------------------------------------------------------- /sweetify/sweetify.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from django.conf import settings 4 | 5 | from .encoder import LazyEncoder 6 | 7 | DEFAULT_OPTS = { 8 | "showConfirmButton": False, 9 | "timer": 2500, 10 | "allowOutsideClick": True, 11 | "confirmButtonText": "OK", 12 | } 13 | 14 | 15 | def _flash_config(request, opts): 16 | request.session["sweetify"] = json.dumps(opts, cls=LazyEncoder) 17 | 18 | 19 | def _flash_multiple_configs(request, json_data): 20 | request.session["sweetify"] = json_data 21 | 22 | 23 | def _treat_data(opts): 24 | button = opts.pop("button", None) 25 | if button: 26 | opts["showConfirmButton"] = True 27 | 28 | if isinstance(button, str): 29 | opts["confirmButtonText"] = button 30 | 31 | persistent = opts.pop("persistent", None) 32 | if persistent: 33 | opts["showConfirmButton"] = True 34 | opts["allowOutsideClick"] = False 35 | opts["timer"] = None 36 | 37 | if isinstance(persistent, str): 38 | opts["confirmButtonText"] = persistent 39 | 40 | # sweetalert changes 41 | if getattr(settings, "SWEETIFY_SWEETALERT_LIBRARY", "sweetalert2") == "sweetalert": 42 | opts["closeOnClickOutside"] = opts.pop("allowOutsideClick", None) 43 | 44 | if opts.pop("showConfirmButton", False): 45 | opts["button"] = opts["confirmButtonText"] 46 | else: 47 | opts["button"] = False 48 | return opts 49 | 50 | 51 | def sweetalert(request, title, **kwargs): 52 | opts = DEFAULT_OPTS.copy() 53 | opts.update(kwargs) 54 | opts["title"] = title 55 | opts = _treat_data(opts) 56 | _flash_config(request, opts) 57 | 58 | 59 | def toast(request, title, icon="success", **kwargs): 60 | if getattr(settings, "SWEETIFY_SWEETALERT_LIBRARY", "sweetalert2") == "sweetalert": 61 | raise RuntimeError("toasts are currently not supported in sweetalert") 62 | 63 | kwargs["icon"] = icon 64 | kwargs["toast"] = True 65 | kwargs.setdefault("position", "top-end") 66 | kwargs.setdefault("timerProgressBar", True) 67 | return sweetalert(request, title, **kwargs) 68 | 69 | 70 | def info(request, title, **kwargs): 71 | kwargs["icon"] = "info" 72 | return sweetalert(request, title, **kwargs) 73 | 74 | 75 | def success(request, title, **kwargs): 76 | kwargs["icon"] = "success" 77 | return sweetalert(request, title, **kwargs) 78 | 79 | 80 | def error(request, title, **kwargs): 81 | kwargs["icon"] = "error" 82 | return sweetalert(request, title, **kwargs) 83 | 84 | 85 | def warning(request, title, **kwargs): 86 | kwargs["icon"] = "warning" 87 | return sweetalert(request, title, **kwargs) 88 | 89 | 90 | def multiple(request, *args): 91 | optsls = [] 92 | for dictionary in args: 93 | opts = DEFAULT_OPTS.copy() 94 | opts.update(dictionary) 95 | opts = _treat_data(opts) 96 | optsls.append(json.dumps(opts, cls=LazyEncoder)) 97 | _flash_multiple_configs(request, optsls) 98 | -------------------------------------------------------------------------------- /sweetify/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Atrox/sweetify-django/293fe5f38f1d25cbaa271362901eb9bab65a6172/sweetify/templatetags/__init__.py -------------------------------------------------------------------------------- /sweetify/templatetags/sweetify.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | from django.conf import settings 3 | from django.utils.safestring import mark_safe 4 | 5 | register = template.Library() 6 | 7 | 8 | @register.simple_tag(takes_context=True) 9 | def sweetify(context, nonce=None): 10 | opts = context.request.session.pop("sweetify", None) 11 | library = getattr(settings, "SWEETIFY_SWEETALERT_LIBRARY", "sweetalert2") 12 | 13 | if not opts: 14 | return "" 15 | 16 | if isinstance(opts, list): 17 | if library == "sweetalert": 18 | raise RuntimeError("multiple alerts are currently not supported in sweetalert") 19 | 20 | script = concatenate(opts) 21 | else: 22 | if library == "sweetalert2": 23 | script = "Swal.fire({})".format(opts) 24 | else: 25 | script = "swal({})".format(opts) 26 | 27 | if nonce: 28 | return mark_safe("""""".format(nonce, script)) 29 | else: 30 | return mark_safe("""""".format(script)) 31 | 32 | 33 | def concatenate(list): 34 | i = 0 35 | length = len(list) 36 | script = "Swal.fire({})" 37 | for opts in list: 38 | if i == 0: 39 | script = script.format(opts) 40 | i = i + 1 41 | elif i < length: 42 | script += ".then((result) => {{if (result.value) {{Swal.fire({}".format(opts) 43 | script += ")" 44 | for k in range(length - 1): 45 | script += "}})" 46 | return script 47 | -------------------------------------------------------------------------------- /sweetify/views.py: -------------------------------------------------------------------------------- 1 | import sweetify 2 | 3 | 4 | class SweetifySuccessMixin(object): 5 | """ 6 | Adds a sweetalert success message on successful form submission. 7 | Drop-in replacement for django's SuccessMessageMixin. 8 | """ 9 | 10 | success_message = "" 11 | sweetify_options = {} 12 | 13 | def form_valid(self, form): 14 | response = super(SweetifySuccessMixin, self).form_valid(form) 15 | success_message = self.get_success_message(form.cleaned_data) 16 | if success_message: 17 | sweetify.success(self.request, success_message, **self.get_sweetify_options()) 18 | return response 19 | 20 | def get_success_message(self, cleaned_data): 21 | return self.success_message % cleaned_data 22 | 23 | def get_sweetify_options(self): 24 | return self.sweetify_options 25 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Atrox/sweetify-django/293fe5f38f1d25cbaa271362901eb9bab65a6172/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | BASE_DIR = os.path.dirname(__file__) 4 | 5 | INSTALLED_APPS = ( 6 | 'django.contrib.auth', 7 | 'django.contrib.sessions', 8 | 'django.contrib.contenttypes', 9 | 'django.contrib.admin', 10 | 'sweetify', 11 | ) 12 | 13 | DATABASES = { 14 | 'default': { 15 | 'ENGINE': 'django.db.backends.sqlite3', 16 | 'NAME': ':memory:' 17 | } 18 | } 19 | 20 | MIDDLEWARE_CLASSES = ( 21 | 'django.middleware.common.CommonMiddleware', 22 | 'django.contrib.sessions.middleware.SessionMiddleware', 23 | ) 24 | 25 | ROOT_URLCONF = 'crispy_forms.tests.urls' 26 | SECRET_KEY = 'secretkey' 27 | SITE_ROOT = os.path.dirname(os.path.abspath(__file__)) 28 | 29 | # sweetify 30 | SWEETIFY_SWEETALERT_LIBRARY = 'sweetalert2' 31 | -------------------------------------------------------------------------------- /tests/test_sweetify.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from django.test import SimpleTestCase, RequestFactory 4 | 5 | import sweetify 6 | 7 | 8 | class SimpleTest(SimpleTestCase): 9 | def setUp(self): 10 | super(SimpleTest, self).setUp() 11 | 12 | self.request = RequestFactory().get('/fake-path') 13 | self.request.session = {} 14 | 15 | def test_general_sweetalert(self): 16 | title = 'Test Sweetify' 17 | sweetify.sweetalert(self.request, title) 18 | 19 | session_opts = json.loads(self.request.session['sweetify']) 20 | 21 | opts = sweetify.DEFAULT_OPTS.copy() 22 | opts.update({'title': title}) 23 | assert session_opts == opts 24 | --------------------------------------------------------------------------------