Lucas
2 years ago
20 changed files with 753 additions and 1 deletions
@ -0,0 +1,10 @@ |
|||
.venv |
|||
venv |
|||
.env |
|||
*.sqlite3 |
|||
media |
|||
*__pycache__ |
|||
.idea |
|||
*.code-workspace |
|||
.report.json |
|||
report.json |
@ -1,3 +1,3 @@ |
|||
# softdelete |
|||
|
|||
soft delete example |
|||
softdelete example |
@ -0,0 +1,22 @@ |
|||
#!/usr/bin/env python |
|||
"""Django's command-line utility for administrative tasks.""" |
|||
import os |
|||
import sys |
|||
|
|||
|
|||
def main(): |
|||
"""Run administrative tasks.""" |
|||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "softdelete.settings") |
|||
try: |
|||
from django.core.management import execute_from_command_line |
|||
except ImportError as exc: |
|||
raise ImportError( |
|||
"Couldn't import Django. Are you sure it's installed and " |
|||
"available on your PYTHONPATH environment variable? Did you " |
|||
"forget to activate a virtual environment?" |
|||
) from exc |
|||
execute_from_command_line(sys.argv) |
|||
|
|||
|
|||
if __name__ == "__main__": |
|||
main() |
@ -0,0 +1,329 @@ |
|||
# This file is automatically @generated by Poetry 1.4.0 and should not be changed by hand. |
|||
|
|||
[[package]] |
|||
name = "asgiref" |
|||
version = "3.6.0" |
|||
description = "ASGI specs, helper code, and adapters" |
|||
category = "main" |
|||
optional = false |
|||
python-versions = ">=3.7" |
|||
files = [ |
|||
{file = "asgiref-3.6.0-py3-none-any.whl", hash = "sha256:71e68008da809b957b7ee4b43dbccff33d1b23519fb8344e33f049897077afac"}, |
|||
{file = "asgiref-3.6.0.tar.gz", hash = "sha256:9567dfe7bd8d3c8c892227827c41cce860b368104c3431da67a0c5a65a949506"}, |
|||
] |
|||
|
|||
[package.extras] |
|||
tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] |
|||
|
|||
[[package]] |
|||
name = "attrs" |
|||
version = "22.2.0" |
|||
description = "Classes Without Boilerplate" |
|||
category = "dev" |
|||
optional = false |
|||
python-versions = ">=3.6" |
|||
files = [ |
|||
{file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"}, |
|||
{file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"}, |
|||
] |
|||
|
|||
[package.extras] |
|||
cov = ["attrs[tests]", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"] |
|||
dev = ["attrs[docs,tests]"] |
|||
docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope.interface"] |
|||
tests = ["attrs[tests-no-zope]", "zope.interface"] |
|||
tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy (>=0.971,<0.990)", "mypy (>=0.971,<0.990)", "pympler", "pympler", "pytest (>=4.3.0)", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-mypy-plugins", "pytest-xdist[psutil]", "pytest-xdist[psutil]"] |
|||
|
|||
[[package]] |
|||
name = "black" |
|||
version = "23.1.0" |
|||
description = "The uncompromising code formatter." |
|||
category = "dev" |
|||
optional = false |
|||
python-versions = ">=3.7" |
|||
files = [ |
|||
{file = "black-23.1.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:b6a92a41ee34b883b359998f0c8e6eb8e99803aa8bf3123bf2b2e6fec505a221"}, |
|||
{file = "black-23.1.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:57c18c5165c1dbe291d5306e53fb3988122890e57bd9b3dcb75f967f13411a26"}, |
|||
{file = "black-23.1.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:9880d7d419bb7e709b37e28deb5e68a49227713b623c72b2b931028ea65f619b"}, |
|||
{file = "black-23.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e6663f91b6feca5d06f2ccd49a10f254f9298cc1f7f49c46e498a0771b507104"}, |
|||
{file = "black-23.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:9afd3f493666a0cd8f8df9a0200c6359ac53940cbde049dcb1a7eb6ee2dd7074"}, |
|||
{file = "black-23.1.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:bfffba28dc52a58f04492181392ee380e95262af14ee01d4bc7bb1b1c6ca8d27"}, |
|||
{file = "black-23.1.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c1c476bc7b7d021321e7d93dc2cbd78ce103b84d5a4cf97ed535fbc0d6660648"}, |
|||
{file = "black-23.1.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:382998821f58e5c8238d3166c492139573325287820963d2f7de4d518bd76958"}, |
|||
{file = "black-23.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bf649fda611c8550ca9d7592b69f0637218c2369b7744694c5e4902873b2f3a"}, |
|||
{file = "black-23.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:121ca7f10b4a01fd99951234abdbd97728e1240be89fde18480ffac16503d481"}, |
|||
{file = "black-23.1.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:a8471939da5e824b891b25751955be52ee7f8a30a916d570a5ba8e0f2eb2ecad"}, |
|||
{file = "black-23.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8178318cb74f98bc571eef19068f6ab5613b3e59d4f47771582f04e175570ed8"}, |
|||
{file = "black-23.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:a436e7881d33acaf2536c46a454bb964a50eff59b21b51c6ccf5a40601fbef24"}, |
|||
{file = "black-23.1.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:a59db0a2094d2259c554676403fa2fac3473ccf1354c1c63eccf7ae65aac8ab6"}, |
|||
{file = "black-23.1.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:0052dba51dec07ed029ed61b18183942043e00008ec65d5028814afaab9a22fd"}, |
|||
{file = "black-23.1.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:49f7b39e30f326a34b5c9a4213213a6b221d7ae9d58ec70df1c4a307cf2a1580"}, |
|||
{file = "black-23.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:162e37d49e93bd6eb6f1afc3e17a3d23a823042530c37c3c42eeeaf026f38468"}, |
|||
{file = "black-23.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b70eb40a78dfac24842458476135f9b99ab952dd3f2dab738c1881a9b38b753"}, |
|||
{file = "black-23.1.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:a29650759a6a0944e7cca036674655c2f0f63806ddecc45ed40b7b8aa314b651"}, |
|||
{file = "black-23.1.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:bb460c8561c8c1bec7824ecbc3ce085eb50005883a6203dcfb0122e95797ee06"}, |
|||
{file = "black-23.1.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:c91dfc2c2a4e50df0026f88d2215e166616e0c80e86004d0003ece0488db2739"}, |
|||
{file = "black-23.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a951cc83ab535d248c89f300eccbd625e80ab880fbcfb5ac8afb5f01a258ac9"}, |
|||
{file = "black-23.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:0680d4380db3719ebcfb2613f34e86c8e6d15ffeabcf8ec59355c5e7b85bb555"}, |
|||
{file = "black-23.1.0-py3-none-any.whl", hash = "sha256:7a0f701d314cfa0896b9001df70a530eb2472babb76086344e688829efd97d32"}, |
|||
{file = "black-23.1.0.tar.gz", hash = "sha256:b0bd97bea8903f5a2ba7219257a44e3f1f9d00073d6cc1add68f0beec69692ac"}, |
|||
] |
|||
|
|||
[package.dependencies] |
|||
click = ">=8.0.0" |
|||
mypy-extensions = ">=0.4.3" |
|||
packaging = ">=22.0" |
|||
pathspec = ">=0.9.0" |
|||
platformdirs = ">=2" |
|||
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} |
|||
|
|||
[package.extras] |
|||
colorama = ["colorama (>=0.4.3)"] |
|||
d = ["aiohttp (>=3.7.4)"] |
|||
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] |
|||
uvloop = ["uvloop (>=0.15.2)"] |
|||
|
|||
[[package]] |
|||
name = "click" |
|||
version = "8.1.3" |
|||
description = "Composable command line interface toolkit" |
|||
category = "dev" |
|||
optional = false |
|||
python-versions = ">=3.7" |
|||
files = [ |
|||
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, |
|||
{file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, |
|||
] |
|||
|
|||
[package.dependencies] |
|||
colorama = {version = "*", markers = "platform_system == \"Windows\""} |
|||
|
|||
[[package]] |
|||
name = "colorama" |
|||
version = "0.4.6" |
|||
description = "Cross-platform colored terminal text." |
|||
category = "dev" |
|||
optional = false |
|||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" |
|||
files = [ |
|||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, |
|||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, |
|||
] |
|||
|
|||
[[package]] |
|||
name = "django" |
|||
version = "4.1.7" |
|||
description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." |
|||
category = "main" |
|||
optional = false |
|||
python-versions = ">=3.8" |
|||
files = [ |
|||
{file = "Django-4.1.7-py3-none-any.whl", hash = "sha256:f2f431e75adc40039ace496ad3b9f17227022e8b11566f4b363da44c7e44761e"}, |
|||
{file = "Django-4.1.7.tar.gz", hash = "sha256:44f714b81c5f190d9d2ddad01a532fe502fa01c4cb8faf1d081f4264ed15dcd8"}, |
|||
] |
|||
|
|||
[package.dependencies] |
|||
asgiref = ">=3.5.2,<4" |
|||
sqlparse = ">=0.2.2" |
|||
tzdata = {version = "*", markers = "sys_platform == \"win32\""} |
|||
|
|||
[package.extras] |
|||
argon2 = ["argon2-cffi (>=19.1.0)"] |
|||
bcrypt = ["bcrypt"] |
|||
|
|||
[[package]] |
|||
name = "exceptiongroup" |
|||
version = "1.1.1" |
|||
description = "Backport of PEP 654 (exception groups)" |
|||
category = "dev" |
|||
optional = false |
|||
python-versions = ">=3.7" |
|||
files = [ |
|||
{file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, |
|||
{file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, |
|||
] |
|||
|
|||
[package.extras] |
|||
test = ["pytest (>=6)"] |
|||
|
|||
[[package]] |
|||
name = "iniconfig" |
|||
version = "2.0.0" |
|||
description = "brain-dead simple config-ini parsing" |
|||
category = "dev" |
|||
optional = false |
|||
python-versions = ">=3.7" |
|||
files = [ |
|||
{file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, |
|||
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, |
|||
] |
|||
|
|||
[[package]] |
|||
name = "isort" |
|||
version = "5.12.0" |
|||
description = "A Python utility / library to sort Python imports." |
|||
category = "dev" |
|||
optional = false |
|||
python-versions = ">=3.8.0" |
|||
files = [ |
|||
{file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, |
|||
{file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, |
|||
] |
|||
|
|||
[package.extras] |
|||
colors = ["colorama (>=0.4.3)"] |
|||
pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] |
|||
plugins = ["setuptools"] |
|||
requirements-deprecated-finder = ["pip-api", "pipreqs"] |
|||
|
|||
[[package]] |
|||
name = "mypy-extensions" |
|||
version = "1.0.0" |
|||
description = "Type system extensions for programs checked with the mypy type checker." |
|||
category = "dev" |
|||
optional = false |
|||
python-versions = ">=3.5" |
|||
files = [ |
|||
{file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, |
|||
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, |
|||
] |
|||
|
|||
[[package]] |
|||
name = "packaging" |
|||
version = "23.0" |
|||
description = "Core utilities for Python packages" |
|||
category = "dev" |
|||
optional = false |
|||
python-versions = ">=3.7" |
|||
files = [ |
|||
{file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"}, |
|||
{file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"}, |
|||
] |
|||
|
|||
[[package]] |
|||
name = "pathspec" |
|||
version = "0.11.1" |
|||
description = "Utility library for gitignore style pattern matching of file paths." |
|||
category = "dev" |
|||
optional = false |
|||
python-versions = ">=3.7" |
|||
files = [ |
|||
{file = "pathspec-0.11.1-py3-none-any.whl", hash = "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"}, |
|||
{file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"}, |
|||
] |
|||
|
|||
[[package]] |
|||
name = "platformdirs" |
|||
version = "3.1.1" |
|||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." |
|||
category = "dev" |
|||
optional = false |
|||
python-versions = ">=3.7" |
|||
files = [ |
|||
{file = "platformdirs-3.1.1-py3-none-any.whl", hash = "sha256:e5986afb596e4bb5bde29a79ac9061aa955b94fca2399b7aaac4090860920dd8"}, |
|||
{file = "platformdirs-3.1.1.tar.gz", hash = "sha256:024996549ee88ec1a9aa99ff7f8fc819bb59e2c3477b410d90a16d32d6e707aa"}, |
|||
] |
|||
|
|||
[package.extras] |
|||
docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"] |
|||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] |
|||
|
|||
[[package]] |
|||
name = "pluggy" |
|||
version = "1.0.0" |
|||
description = "plugin and hook calling mechanisms for python" |
|||
category = "dev" |
|||
optional = false |
|||
python-versions = ">=3.6" |
|||
files = [ |
|||
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, |
|||
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, |
|||
] |
|||
|
|||
[package.extras] |
|||
dev = ["pre-commit", "tox"] |
|||
testing = ["pytest", "pytest-benchmark"] |
|||
|
|||
[[package]] |
|||
name = "pytest" |
|||
version = "7.2.2" |
|||
description = "pytest: simple powerful testing with Python" |
|||
category = "dev" |
|||
optional = false |
|||
python-versions = ">=3.7" |
|||
files = [ |
|||
{file = "pytest-7.2.2-py3-none-any.whl", hash = "sha256:130328f552dcfac0b1cec75c12e3f005619dc5f874f0a06e8ff7263f0ee6225e"}, |
|||
{file = "pytest-7.2.2.tar.gz", hash = "sha256:c99ab0c73aceb050f68929bc93af19ab6db0558791c6a0715723abe9d0ade9d4"}, |
|||
] |
|||
|
|||
[package.dependencies] |
|||
attrs = ">=19.2.0" |
|||
colorama = {version = "*", markers = "sys_platform == \"win32\""} |
|||
exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} |
|||
iniconfig = "*" |
|||
packaging = "*" |
|||
pluggy = ">=0.12,<2.0" |
|||
tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} |
|||
|
|||
[package.extras] |
|||
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] |
|||
|
|||
[[package]] |
|||
name = "pytest-django" |
|||
version = "4.5.2" |
|||
description = "A Django plugin for pytest." |
|||
category = "dev" |
|||
optional = false |
|||
python-versions = ">=3.5" |
|||
files = [ |
|||
{file = "pytest-django-4.5.2.tar.gz", hash = "sha256:d9076f759bb7c36939dbdd5ae6633c18edfc2902d1a69fdbefd2426b970ce6c2"}, |
|||
{file = "pytest_django-4.5.2-py3-none-any.whl", hash = "sha256:c60834861933773109334fe5a53e83d1ef4828f2203a1d6a0fa9972f4f75ab3e"}, |
|||
] |
|||
|
|||
[package.dependencies] |
|||
pytest = ">=5.4.0" |
|||
|
|||
[package.extras] |
|||
docs = ["sphinx", "sphinx-rtd-theme"] |
|||
testing = ["Django", "django-configurations (>=2.0)"] |
|||
|
|||
[[package]] |
|||
name = "sqlparse" |
|||
version = "0.4.3" |
|||
description = "A non-validating SQL parser." |
|||
category = "main" |
|||
optional = false |
|||
python-versions = ">=3.5" |
|||
files = [ |
|||
{file = "sqlparse-0.4.3-py3-none-any.whl", hash = "sha256:0323c0ec29cd52bceabc1b4d9d579e311f3e4961b98d174201d5622a23b85e34"}, |
|||
{file = "sqlparse-0.4.3.tar.gz", hash = "sha256:69ca804846bb114d2ec380e4360a8a340db83f0ccf3afceeb1404df028f57268"}, |
|||
] |
|||
|
|||
[[package]] |
|||
name = "tomli" |
|||
version = "2.0.1" |
|||
description = "A lil' TOML parser" |
|||
category = "dev" |
|||
optional = false |
|||
python-versions = ">=3.7" |
|||
files = [ |
|||
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, |
|||
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, |
|||
] |
|||
|
|||
[[package]] |
|||
name = "tzdata" |
|||
version = "2022.7" |
|||
description = "Provider of IANA time zone data" |
|||
category = "main" |
|||
optional = false |
|||
python-versions = ">=2" |
|||
files = [ |
|||
{file = "tzdata-2022.7-py2.py3-none-any.whl", hash = "sha256:2b88858b0e3120792a3c0635c23daf36a7d7eeeca657c323da299d2094402a0d"}, |
|||
{file = "tzdata-2022.7.tar.gz", hash = "sha256:fe5f866eddd8b96e9fcba978f8e503c909b19ea7efda11e52e39494bad3a7bfa"}, |
|||
] |
|||
|
|||
[metadata] |
|||
lock-version = "2.0" |
|||
python-versions = "^3.10" |
|||
content-hash = "223855818db7aacf34624f5e82bede078933eb9ca1ec911c1989b7519f292538" |
@ -0,0 +1,69 @@ |
|||
[tool.poetry] |
|||
name = "softdelete" |
|||
version = "0.1.0" |
|||
description = "" |
|||
authors = ["Lucas F. <lucas@lucasf.dev>"] |
|||
readme = "README.md" |
|||
|
|||
[tool.poetry.dependencies] |
|||
python = "^3.10" |
|||
django = "^4.1.7" |
|||
|
|||
|
|||
[tool.poetry.group.dev.dependencies] |
|||
pytest = "^7.2.2" |
|||
pytest-django = "^4.5.2" |
|||
black = "^23.1.0" |
|||
isort = "^5.12.0" |
|||
|
|||
[build-system] |
|||
requires = ["poetry-core"] |
|||
build-backend = "poetry.core.masonry.api" |
|||
|
|||
[tool.isort] |
|||
profile = "black" |
|||
line_length = 89 |
|||
multi_line_output = 2 |
|||
include_trailing_comma = false |
|||
extend_skip = ''' |
|||
/( |
|||
\.git |
|||
| \.hg |
|||
| \.mypy_cache |
|||
| \.tox |
|||
| \.venv |
|||
| _build |
|||
| buck-out |
|||
| build |
|||
| dist |
|||
| migrations |
|||
)/ |
|||
''' |
|||
|
|||
[tool.black] |
|||
line-length = 79 |
|||
include = '\.pyi?$' |
|||
force-exclude = ''' |
|||
/( |
|||
\.git |
|||
| \.hg |
|||
| \.mypy_cache |
|||
| \.tox |
|||
| \.venv |
|||
| _build |
|||
| buck-out |
|||
| build |
|||
| dist |
|||
| migrations |
|||
)/ |
|||
''' |
|||
|
|||
[tool.pytest.ini_options] |
|||
minversion = "6.0" |
|||
DJANGO_SETTINGS_MODULE = "softdelete.settings" |
|||
python_files = [ |
|||
"tests.py", |
|||
"test_*.py", |
|||
"*_tests.py", |
|||
] |
|||
|
@ -0,0 +1,14 @@ |
|||
# How to use |
|||
|
|||
```shell |
|||
git clone https://git.lucasf.dev/public/softdelete.git && \ |
|||
cd softdelete && \ |
|||
poetry shell && \ |
|||
poetry install |
|||
``` |
|||
|
|||
# How to test |
|||
|
|||
```shell |
|||
pytest --nomigrations |
|||
``` |
@ -0,0 +1,3 @@ |
|||
from django.contrib import admin |
|||
|
|||
# Register your models here. |
@ -0,0 +1,7 @@ |
|||
from django.apps import AppConfig |
|||
|
|||
|
|||
class CoreConfig(AppConfig): |
|||
default_auto_field = "django.db.models.BigAutoField" |
|||
name = "core" |
|||
default = False |
@ -0,0 +1,22 @@ |
|||
from django.db import models |
|||
|
|||
|
|||
class BaseManager(models.Manager): |
|||
def find(self, pk): |
|||
qs = self.get_queryset().filter(pk=pk) |
|||
|
|||
if qs: |
|||
return qs.first() |
|||
|
|||
return None |
|||
|
|||
|
|||
class BaseDeletedManager(models.Manager): |
|||
def all(self): |
|||
return self.get_queryset().filter(is_deleted=True) |
|||
|
|||
def since(self, date): |
|||
return self.get_queryset().filter(deleted_at__gte=date) |
|||
|
|||
def until(self, date): |
|||
return self.get_queryset().filter(deleted_at__lte=date) |
@ -0,0 +1,34 @@ |
|||
import uuid |
|||
|
|||
from django.db import models |
|||
from django.utils.translation import gettext_lazy as _ |
|||
from django.utils import timezone |
|||
|
|||
from .managers import BaseDeletedManager, BaseManager |
|||
|
|||
class ModelBase(models.Model): |
|||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) |
|||
|
|||
created_at = models.DateTimeField( |
|||
_("Created at"), auto_now=False, auto_now_add=True |
|||
) |
|||
modified_at = models.DateTimeField( |
|||
_("Modified at"), auto_now=True, auto_now_add=False |
|||
) |
|||
deleted_at = models.DateField(_("Deleted at"), null=True, blank=True) |
|||
is_deleted = models.BooleanField(_("Is Deleted"), default=False) |
|||
|
|||
objects = BaseManager() |
|||
|
|||
deleted_objects = BaseDeletedManager() |
|||
|
|||
def delete(self): |
|||
self.is_deleted = True |
|||
self.deleted_at = timezone.now() |
|||
self.save() |
|||
|
|||
def hard_delete(self): |
|||
return super().delete() |
|||
|
|||
class Meta: |
|||
abstract = True |
@ -0,0 +1,60 @@ |
|||
from datetime import datetime, timedelta |
|||
|
|||
from django.db import models |
|||
from django.test import TestCase |
|||
from django.utils import timezone |
|||
|
|||
from softdelete.apps.core.models import ModelBase |
|||
|
|||
|
|||
class MyModel(ModelBase): |
|||
name = models.CharField("Name", max_length=50) |
|||
|
|||
def __str__(self): |
|||
return self.name |
|||
|
|||
|
|||
class ModelTest(TestCase): |
|||
def setUp(self): |
|||
self.obj = MyModel.objects.create(name="Something") |
|||
|
|||
def test_delete_must_set_is_deleted_as_true(self): |
|||
self.obj.delete() |
|||
self.assertTrue(self.obj.is_deleted) |
|||
|
|||
def test_delete_at_must_be_instance_of_datetime(self): |
|||
self.obj.delete() |
|||
self.assertIsInstance(self.obj.deleted_at, datetime) |
|||
|
|||
|
|||
class ManagerTest(TestCase): |
|||
def setUp(self): |
|||
self.create_some() |
|||
|
|||
def test_all_must_return_all_deleted_objects(self): |
|||
self.objs[0].delete() |
|||
self.assertEqual(MyModel.deleted_objects.all().count(), 1) |
|||
|
|||
def test_since_must_return_all_deleted_since_some_date(self): |
|||
self.delete_some() |
|||
date = timezone.now() - timedelta(days=4) |
|||
ob = MyModel.deleted_objects.since(date=date) |
|||
self.assertEqual(ob.count(), 2) |
|||
|
|||
def test_until_must_return_all_deleted_until_some_date(self): |
|||
self.delete_some() |
|||
date = timezone.now() - timedelta(days=2) |
|||
ob = MyModel.deleted_objects.until(date=date) |
|||
self.assertEqual(ob.count(), 1) |
|||
|
|||
def create_some(self): |
|||
objs = [] |
|||
for x in range(0, 5): |
|||
objs.append(MyModel(name=f"Something_{x}")) |
|||
self.objs = MyModel.objects.bulk_create(objs) |
|||
|
|||
def delete_some(self): |
|||
for x in range(0, 2): |
|||
self.objs[x].delete() |
|||
self.objs[x].deleted_at = timezone.now() - timedelta(days=x + 1) |
|||
self.objs[x].save() |
@ -0,0 +1,3 @@ |
|||
from django.shortcuts import render |
|||
|
|||
# Create your views here. |
@ -0,0 +1,16 @@ |
|||
""" |
|||
ASGI config for softdelete project. |
|||
|
|||
It exposes the ASGI callable as a module-level variable named ``application``. |
|||
|
|||
For more information on this file, see |
|||
https://docs.djangoproject.com/en/4.1/howto/deployment/asgi/ |
|||
""" |
|||
|
|||
import os |
|||
|
|||
from django.core.asgi import get_asgi_application |
|||
|
|||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "softdelete.settings") |
|||
|
|||
application = get_asgi_application() |
@ -0,0 +1,126 @@ |
|||
""" |
|||
Django settings for softdelete project. |
|||
|
|||
Generated by 'django-admin startproject' using Django 4.1.7. |
|||
|
|||
For more information on this file, see |
|||
https://docs.djangoproject.com/en/4.1/topics/settings/ |
|||
|
|||
For the full list of settings and their values, see |
|||
https://docs.djangoproject.com/en/4.1/ref/settings/ |
|||
""" |
|||
|
|||
from pathlib import Path |
|||
|
|||
# Build paths inside the project like this: BASE_DIR / 'subdir'. |
|||
BASE_DIR = Path(__file__).resolve().parent.parent |
|||
|
|||
|
|||
# Quick-start development settings - unsuitable for production |
|||
# See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/ |
|||
|
|||
# SECURITY WARNING: keep the secret key used in production secret! |
|||
SECRET_KEY = ( |
|||
"django-insecure-m1mz5tt*et%v*cw0&+_ac-%2e%hyks+-eh75fxue*o%#_z-2ca" |
|||
) |
|||
|
|||
# SECURITY WARNING: don't run with debug turned on in production! |
|||
DEBUG = True |
|||
|
|||
ALLOWED_HOSTS = [] |
|||
|
|||
|
|||
# Application definition |
|||
|
|||
INSTALLED_APPS = [ |
|||
"django.contrib.admin", |
|||
"django.contrib.auth", |
|||
"django.contrib.contenttypes", |
|||
"django.contrib.sessions", |
|||
"django.contrib.messages", |
|||
"django.contrib.staticfiles", |
|||
"softdelete.apps.core", |
|||
] |
|||
|
|||
MIDDLEWARE = [ |
|||
"django.middleware.security.SecurityMiddleware", |
|||
"django.contrib.sessions.middleware.SessionMiddleware", |
|||
"django.middleware.common.CommonMiddleware", |
|||
"django.middleware.csrf.CsrfViewMiddleware", |
|||
"django.contrib.auth.middleware.AuthenticationMiddleware", |
|||
"django.contrib.messages.middleware.MessageMiddleware", |
|||
"django.middleware.clickjacking.XFrameOptionsMiddleware", |
|||
] |
|||
|
|||
ROOT_URLCONF = "softdelete.urls" |
|||
|
|||
TEMPLATES = [ |
|||
{ |
|||
"BACKEND": "django.template.backends.django.DjangoTemplates", |
|||
"DIRS": [], |
|||
"APP_DIRS": True, |
|||
"OPTIONS": { |
|||
"context_processors": [ |
|||
"django.template.context_processors.debug", |
|||
"django.template.context_processors.request", |
|||
"django.contrib.auth.context_processors.auth", |
|||
"django.contrib.messages.context_processors.messages", |
|||
], |
|||
}, |
|||
}, |
|||
] |
|||
|
|||
WSGI_APPLICATION = "softdelete.wsgi.application" |
|||
|
|||
|
|||
# Database |
|||
# https://docs.djangoproject.com/en/4.1/ref/settings/#databases |
|||
|
|||
DATABASES = { |
|||
"default": { |
|||
"ENGINE": "django.db.backends.sqlite3", |
|||
"NAME": BASE_DIR / "db.sqlite3", |
|||
} |
|||
} |
|||
|
|||
|
|||
# Password validation |
|||
# https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators |
|||
|
|||
AUTH_PASSWORD_VALIDATORS = [ |
|||
{ |
|||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", |
|||
}, |
|||
{ |
|||
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", |
|||
}, |
|||
{ |
|||
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", |
|||
}, |
|||
{ |
|||
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", |
|||
}, |
|||
] |
|||
|
|||
|
|||
# Internationalization |
|||
# https://docs.djangoproject.com/en/4.1/topics/i18n/ |
|||
|
|||
LANGUAGE_CODE = "en-us" |
|||
|
|||
TIME_ZONE = "UTC" |
|||
|
|||
USE_I18N = True |
|||
|
|||
USE_TZ = True |
|||
|
|||
|
|||
# Static files (CSS, JavaScript, Images) |
|||
# https://docs.djangoproject.com/en/4.1/howto/static-files/ |
|||
|
|||
STATIC_URL = "static/" |
|||
|
|||
# Default primary key field type |
|||
# https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field |
|||
|
|||
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" |
@ -0,0 +1,21 @@ |
|||
"""softdelete URL Configuration |
|||
|
|||
The `urlpatterns` list routes URLs to views. For more information please see: |
|||
https://docs.djangoproject.com/en/4.1/topics/http/urls/ |
|||
Examples: |
|||
Function views |
|||
1. Add an import: from my_app import views |
|||
2. Add a URL to urlpatterns: path('', views.home, name='home') |
|||
Class-based views |
|||
1. Add an import: from other_app.views import Home |
|||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') |
|||
Including another URLconf |
|||
1. Import the include() function: from django.urls import include, path |
|||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) |
|||
""" |
|||
from django.contrib import admin |
|||
from django.urls import path |
|||
|
|||
urlpatterns = [ |
|||
path("admin/", admin.site.urls), |
|||
] |
@ -0,0 +1,16 @@ |
|||
""" |
|||
WSGI config for softdelete project. |
|||
|
|||
It exposes the WSGI callable as a module-level variable named ``application``. |
|||
|
|||
For more information on this file, see |
|||
https://docs.djangoproject.com/en/4.1/howto/deployment/wsgi/ |
|||
""" |
|||
|
|||
import os |
|||
|
|||
from django.core.wsgi import get_wsgi_application |
|||
|
|||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "softdelete.settings") |
|||
|
|||
application = get_wsgi_application() |
Loading…
Reference in new issue