maykin-common 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- maykin_common-0.1.0/LICENSE +7 -0
- maykin_common-0.1.0/MANIFEST.in +9 -0
- maykin_common-0.1.0/PKG-INFO +149 -0
- maykin_common-0.1.0/README.rst +96 -0
- maykin_common-0.1.0/maykin_common/__init__.py +0 -0
- maykin_common-0.1.0/maykin_common/apps.py +9 -0
- maykin_common-0.1.0/maykin_common/checks.py +78 -0
- maykin_common-0.1.0/maykin_common/context_processors.py +18 -0
- maykin_common-0.1.0/maykin_common/django_two_factor_auth.py +20 -0
- maykin_common-0.1.0/maykin_common/management/__init__.py +0 -0
- maykin_common-0.1.0/maykin_common/management/commands/__init__.py +0 -0
- maykin_common-0.1.0/maykin_common/management/commands/clear_cache.py +16 -0
- maykin_common-0.1.0/maykin_common/migration_operations.py +61 -0
- maykin_common-0.1.0/maykin_common/mixins.py +92 -0
- maykin_common-0.1.0/maykin_common/pdf.py +120 -0
- maykin_common-0.1.0/maykin_common/py.typed +0 -0
- maykin_common-0.1.0/maykin_common/settings.py +34 -0
- maykin_common-0.1.0/maykin_common/static/maykin_common/css/version.css +17 -0
- maykin_common-0.1.0/maykin_common/templates/maykin_common/includes/version_info.html +12 -0
- maykin_common-0.1.0/maykin_common/templatetags/__init__.py +0 -0
- maykin_common-0.1.0/maykin_common/templatetags/maykin_common.py +46 -0
- maykin_common-0.1.0/maykin_common/views.py +25 -0
- maykin_common-0.1.0/maykin_common.egg-info/PKG-INFO +149 -0
- maykin_common-0.1.0/maykin_common.egg-info/SOURCES.txt +27 -0
- maykin_common-0.1.0/maykin_common.egg-info/dependency_links.txt +1 -0
- maykin_common-0.1.0/maykin_common.egg-info/requires.txt +25 -0
- maykin_common-0.1.0/maykin_common.egg-info/top_level.txt +1 -0
- maykin_common-0.1.0/pyproject.toml +137 -0
- maykin_common-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2025 Maykin Media
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
include *.rst
|
|
2
|
+
include LICENSE
|
|
3
|
+
include maykin_common/py.typed
|
|
4
|
+
recursive-include maykin_common *.html
|
|
5
|
+
recursive-include maykin_common *.css
|
|
6
|
+
recursive-include maykin_common *.txt
|
|
7
|
+
recursive-include maykin_common *.po
|
|
8
|
+
global-exclude __pycache__
|
|
9
|
+
global-exclude *.py[co]
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: maykin-common
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Re-usable utilities for Maykin Django projects, centrally maintained instead of in default-project.
|
|
5
|
+
Author-email: Maykin Media <support@maykinmedia.nl>
|
|
6
|
+
License: Copyright 2025 Maykin Media
|
|
7
|
+
|
|
8
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
11
|
+
|
|
12
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
13
|
+
|
|
14
|
+
Project-URL: Homepage, https://github.com/maykinmedia/django-common
|
|
15
|
+
Project-URL: Documentation, http://maykin-django-common.readthedocs.io/en/latest/
|
|
16
|
+
Project-URL: Bug Tracker, https://github.com/maykinmedia/django-common/issues
|
|
17
|
+
Project-URL: Source Code, https://github.com/maykinmedia/django-common
|
|
18
|
+
Keywords: django,maykin,boilerplate
|
|
19
|
+
Classifier: Development Status :: 3 - Alpha
|
|
20
|
+
Classifier: Framework :: Django
|
|
21
|
+
Classifier: Framework :: Django :: 4.2
|
|
22
|
+
Classifier: Framework :: Django :: 5.2
|
|
23
|
+
Classifier: Intended Audience :: Developers
|
|
24
|
+
Classifier: Operating System :: Unix
|
|
25
|
+
Classifier: Operating System :: MacOS
|
|
26
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
27
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
28
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
29
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
30
|
+
Requires-Python: >=3.12
|
|
31
|
+
Description-Content-Type: text/x-rst
|
|
32
|
+
License-File: LICENSE
|
|
33
|
+
Requires-Dist: django>=4.2
|
|
34
|
+
Provides-Extra: tests
|
|
35
|
+
Requires-Dist: pytest; extra == "tests"
|
|
36
|
+
Requires-Dist: pytest-django; extra == "tests"
|
|
37
|
+
Requires-Dist: pytest-cov; extra == "tests"
|
|
38
|
+
Requires-Dist: tox; extra == "tests"
|
|
39
|
+
Requires-Dist: ruff; extra == "tests"
|
|
40
|
+
Provides-Extra: docs
|
|
41
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
42
|
+
Requires-Dist: sphinx-rtd-theme; extra == "docs"
|
|
43
|
+
Provides-Extra: release
|
|
44
|
+
Requires-Dist: bump-my-version; extra == "release"
|
|
45
|
+
Provides-Extra: pdf
|
|
46
|
+
Requires-Dist: weasyprint; extra == "pdf"
|
|
47
|
+
Provides-Extra: mfa
|
|
48
|
+
Requires-Dist: django-admin-index; extra == "mfa"
|
|
49
|
+
Requires-Dist: maykin-2fa; extra == "mfa"
|
|
50
|
+
Provides-Extra: axes
|
|
51
|
+
Requires-Dist: django-axes; extra == "axes"
|
|
52
|
+
Dynamic: license-file
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
Welcome to maykin-common's documentation!
|
|
57
|
+
=================================================
|
|
58
|
+
|
|
59
|
+
:Version: 0.1.0
|
|
60
|
+
:Source: https://github.com/maykinmedia/django-common
|
|
61
|
+
:Keywords: ``<keywords>``
|
|
62
|
+
:PythonVersion: 3.12
|
|
63
|
+
|
|
64
|
+
|build-status| |code-quality| |ruff| |coverage| |docs|
|
|
65
|
+
|
|
66
|
+
|python-versions| |django-versions| |pypi-version|
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
<One liner describing the project>
|
|
70
|
+
|
|
71
|
+
.. contents::
|
|
72
|
+
|
|
73
|
+
.. section-numbering::
|
|
74
|
+
|
|
75
|
+
Features
|
|
76
|
+
========
|
|
77
|
+
|
|
78
|
+
* ...
|
|
79
|
+
* ...
|
|
80
|
+
|
|
81
|
+
Installation
|
|
82
|
+
============
|
|
83
|
+
|
|
84
|
+
Requirements
|
|
85
|
+
------------
|
|
86
|
+
|
|
87
|
+
* Python 3.12 or above
|
|
88
|
+
* Django 4.2 or newer
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
Install
|
|
92
|
+
-------
|
|
93
|
+
|
|
94
|
+
.. code-block:: bash
|
|
95
|
+
|
|
96
|
+
pip install maykin-common
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
Usage
|
|
100
|
+
=====
|
|
101
|
+
|
|
102
|
+
<document or refer to docs>
|
|
103
|
+
|
|
104
|
+
Local development
|
|
105
|
+
=================
|
|
106
|
+
|
|
107
|
+
To install and develop the library locally, use::
|
|
108
|
+
|
|
109
|
+
.. code-block:: bash
|
|
110
|
+
|
|
111
|
+
pip install -e .[tests,coverage,docs,release]
|
|
112
|
+
|
|
113
|
+
When running management commands via ``django-admin``, make sure to add the root
|
|
114
|
+
directory to the python path (or use ``python -m django <command>``):
|
|
115
|
+
|
|
116
|
+
.. code-block:: bash
|
|
117
|
+
|
|
118
|
+
export PYTHONPATH=. DJANGO_SETTINGS_MODULE=testapp.settings
|
|
119
|
+
django-admin check
|
|
120
|
+
# or other commands like:
|
|
121
|
+
# django-admin makemessages -l nl
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
.. |build-status| image:: https://github.com/maykinmedia/django-common/workflows/Run%20CI/badge.svg
|
|
125
|
+
:alt: Build status
|
|
126
|
+
:target: https://github.com/maykinmedia/django-common/actions?query=workflow%3A%22Run+CI%22
|
|
127
|
+
|
|
128
|
+
.. |code-quality| image:: https://github.com/maykinmedia/django-common/workflows/Code%20quality%20checks/badge.svg
|
|
129
|
+
:alt: Code quality checks
|
|
130
|
+
:target: https://github.com/maykinmedia/django-common/actions?query=workflow%3A%22Code+quality+checks%22
|
|
131
|
+
|
|
132
|
+
.. |ruff| image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
|
|
133
|
+
:target: https://github.com/astral-sh/ruff
|
|
134
|
+
:alt: Ruff
|
|
135
|
+
|
|
136
|
+
.. |coverage| image:: https://codecov.io/gh/maykinmedia/django-common/branch/main/graph/badge.svg
|
|
137
|
+
:target: https://codecov.io/gh/maykinmedia/django-common
|
|
138
|
+
:alt: Coverage status
|
|
139
|
+
|
|
140
|
+
.. |docs| image:: https://readthedocs.org/projects/django-common/badge/?version=latest
|
|
141
|
+
:target: https://maykin-django-common.readthedocs.io/en/latest/?badge=latest
|
|
142
|
+
:alt: Documentation Status
|
|
143
|
+
|
|
144
|
+
.. |python-versions| image:: https://img.shields.io/pypi/pyversions/django-common.svg
|
|
145
|
+
|
|
146
|
+
.. |django-versions| image:: https://img.shields.io/pypi/djversions/django-common.svg
|
|
147
|
+
|
|
148
|
+
.. |pypi-version| image:: https://img.shields.io/pypi/v/django-common.svg
|
|
149
|
+
:target: https://pypi.org/project/django-common/
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
Welcome to maykin-common's documentation!
|
|
4
|
+
=================================================
|
|
5
|
+
|
|
6
|
+
:Version: 0.1.0
|
|
7
|
+
:Source: https://github.com/maykinmedia/django-common
|
|
8
|
+
:Keywords: ``<keywords>``
|
|
9
|
+
:PythonVersion: 3.12
|
|
10
|
+
|
|
11
|
+
|build-status| |code-quality| |ruff| |coverage| |docs|
|
|
12
|
+
|
|
13
|
+
|python-versions| |django-versions| |pypi-version|
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
<One liner describing the project>
|
|
17
|
+
|
|
18
|
+
.. contents::
|
|
19
|
+
|
|
20
|
+
.. section-numbering::
|
|
21
|
+
|
|
22
|
+
Features
|
|
23
|
+
========
|
|
24
|
+
|
|
25
|
+
* ...
|
|
26
|
+
* ...
|
|
27
|
+
|
|
28
|
+
Installation
|
|
29
|
+
============
|
|
30
|
+
|
|
31
|
+
Requirements
|
|
32
|
+
------------
|
|
33
|
+
|
|
34
|
+
* Python 3.12 or above
|
|
35
|
+
* Django 4.2 or newer
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
Install
|
|
39
|
+
-------
|
|
40
|
+
|
|
41
|
+
.. code-block:: bash
|
|
42
|
+
|
|
43
|
+
pip install maykin-common
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
Usage
|
|
47
|
+
=====
|
|
48
|
+
|
|
49
|
+
<document or refer to docs>
|
|
50
|
+
|
|
51
|
+
Local development
|
|
52
|
+
=================
|
|
53
|
+
|
|
54
|
+
To install and develop the library locally, use::
|
|
55
|
+
|
|
56
|
+
.. code-block:: bash
|
|
57
|
+
|
|
58
|
+
pip install -e .[tests,coverage,docs,release]
|
|
59
|
+
|
|
60
|
+
When running management commands via ``django-admin``, make sure to add the root
|
|
61
|
+
directory to the python path (or use ``python -m django <command>``):
|
|
62
|
+
|
|
63
|
+
.. code-block:: bash
|
|
64
|
+
|
|
65
|
+
export PYTHONPATH=. DJANGO_SETTINGS_MODULE=testapp.settings
|
|
66
|
+
django-admin check
|
|
67
|
+
# or other commands like:
|
|
68
|
+
# django-admin makemessages -l nl
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
.. |build-status| image:: https://github.com/maykinmedia/django-common/workflows/Run%20CI/badge.svg
|
|
72
|
+
:alt: Build status
|
|
73
|
+
:target: https://github.com/maykinmedia/django-common/actions?query=workflow%3A%22Run+CI%22
|
|
74
|
+
|
|
75
|
+
.. |code-quality| image:: https://github.com/maykinmedia/django-common/workflows/Code%20quality%20checks/badge.svg
|
|
76
|
+
:alt: Code quality checks
|
|
77
|
+
:target: https://github.com/maykinmedia/django-common/actions?query=workflow%3A%22Code+quality+checks%22
|
|
78
|
+
|
|
79
|
+
.. |ruff| image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
|
|
80
|
+
:target: https://github.com/astral-sh/ruff
|
|
81
|
+
:alt: Ruff
|
|
82
|
+
|
|
83
|
+
.. |coverage| image:: https://codecov.io/gh/maykinmedia/django-common/branch/main/graph/badge.svg
|
|
84
|
+
:target: https://codecov.io/gh/maykinmedia/django-common
|
|
85
|
+
:alt: Coverage status
|
|
86
|
+
|
|
87
|
+
.. |docs| image:: https://readthedocs.org/projects/django-common/badge/?version=latest
|
|
88
|
+
:target: https://maykin-django-common.readthedocs.io/en/latest/?badge=latest
|
|
89
|
+
:alt: Documentation Status
|
|
90
|
+
|
|
91
|
+
.. |python-versions| image:: https://img.shields.io/pypi/pyversions/django-common.svg
|
|
92
|
+
|
|
93
|
+
.. |django-versions| image:: https://img.shields.io/pypi/djversions/django-common.svg
|
|
94
|
+
|
|
95
|
+
.. |pypi-version| image:: https://img.shields.io/pypi/v/django-common.svg
|
|
96
|
+
:target: https://pypi.org/project/django-common/
|
|
File without changes
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from django.conf import settings
|
|
4
|
+
from django.core.checks import Error, Warning, register
|
|
5
|
+
from django.forms import ModelForm
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_subclasses(cls):
|
|
9
|
+
for subclass in cls.__subclasses__():
|
|
10
|
+
yield from get_subclasses(subclass)
|
|
11
|
+
yield subclass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@register()
|
|
15
|
+
def check_modelform_exclude(app_configs, **kwargs):
|
|
16
|
+
"""
|
|
17
|
+
Check that ModelForms use Meta.fields instead of Meta.exclude.
|
|
18
|
+
|
|
19
|
+
ModelForm.Meta.exclude is dangerous because it doesn't protect against
|
|
20
|
+
fields that are added later. Explicit white-listing is safer and prevents
|
|
21
|
+
bugs such as IMA #645.
|
|
22
|
+
|
|
23
|
+
This check piggy-backs on all form modules to be imported during Django
|
|
24
|
+
startup. It won't cover forms that are defined on the fly such as in
|
|
25
|
+
formset factories.
|
|
26
|
+
"""
|
|
27
|
+
errors = []
|
|
28
|
+
|
|
29
|
+
for form in get_subclasses(ModelForm):
|
|
30
|
+
# ok, fields is defined
|
|
31
|
+
if form._meta.fields or getattr(form.Meta, "fields", None):
|
|
32
|
+
continue
|
|
33
|
+
|
|
34
|
+
# no `.fields` defined, so scream loud enough to prevent this
|
|
35
|
+
errors.append(
|
|
36
|
+
Error(
|
|
37
|
+
f"ModelForm {form.__module__}.{form.__name__} with Meta.exclude "
|
|
38
|
+
"detected, this is a bad practice",
|
|
39
|
+
hint="Use ModelForm.Meta.fields instead",
|
|
40
|
+
obj=form,
|
|
41
|
+
id="maykin.E001",
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
return errors
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@register
|
|
49
|
+
def check_missing_init_files(app_configs, **kwargs):
|
|
50
|
+
"""
|
|
51
|
+
Check that all packages have __init__.py files.
|
|
52
|
+
|
|
53
|
+
If they don't, the code will still run, but tests aren't picked up by the
|
|
54
|
+
test runner, for example.
|
|
55
|
+
"""
|
|
56
|
+
errors = []
|
|
57
|
+
|
|
58
|
+
for dirpath, _, filenames in os.walk(settings.DJANGO_PROJECT_DIR):
|
|
59
|
+
dirname = os.path.split(dirpath)[1]
|
|
60
|
+
if dirname == "__pycache__":
|
|
61
|
+
continue
|
|
62
|
+
|
|
63
|
+
if "__init__.py" in filenames:
|
|
64
|
+
continue
|
|
65
|
+
|
|
66
|
+
extensions = [os.path.splitext(fn)[1] for fn in filenames]
|
|
67
|
+
if ".py" not in extensions:
|
|
68
|
+
continue
|
|
69
|
+
|
|
70
|
+
errors.append(
|
|
71
|
+
Warning(
|
|
72
|
+
f"Directory {dirpath} does not contain an `__init__.py` file {dirname}",
|
|
73
|
+
hint="Consider adding this module to make sure tests are picked up",
|
|
74
|
+
id="maykin.W001",
|
|
75
|
+
)
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
return errors
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from django.conf import settings as django_settings
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def settings(request):
|
|
5
|
+
""" """
|
|
6
|
+
public_settings = (
|
|
7
|
+
"GOOGLE_ANALYTICS_ID",
|
|
8
|
+
"PROJECT_NAME",
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
context = {
|
|
12
|
+
"settings": {k: getattr(django_settings, k, None) for k in public_settings},
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if hasattr(django_settings, "SENTRY_CONFIG"):
|
|
16
|
+
context.update(dsn=django_settings.SENTRY_CONFIG.get("public_dsn", ""))
|
|
17
|
+
|
|
18
|
+
return context
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from django_admin_index.utils import (
|
|
2
|
+
should_display_dropdown_menu as default_should_display_dropdown_menu,
|
|
3
|
+
)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def should_display_dropdown_menu(request) -> bool:
|
|
7
|
+
"""
|
|
8
|
+
Overrides the default `should_display_dropdown_menu` from `django_admin_index.utils`
|
|
9
|
+
to:
|
|
10
|
+
- not display the dropdown in `maykin_2fa` views.
|
|
11
|
+
- only display the dropdown to verified users.
|
|
12
|
+
"""
|
|
13
|
+
default = default_should_display_dropdown_menu(request)
|
|
14
|
+
|
|
15
|
+
# never display the dropdown in two-factor admin views
|
|
16
|
+
if request.resolver_match.view_name.startswith("maykin_2fa:"):
|
|
17
|
+
return False
|
|
18
|
+
|
|
19
|
+
# do not display the dropdown until the user is verified.
|
|
20
|
+
return default and request.user.is_verified()
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from django.core.cache import DEFAULT_CACHE_ALIAS, caches
|
|
2
|
+
from django.core.management import BaseCommand
|
|
3
|
+
from django.utils.translation import gettext as _
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Command(BaseCommand):
|
|
7
|
+
help = "Clears the Django cache"
|
|
8
|
+
|
|
9
|
+
def add_arguments(self, parser):
|
|
10
|
+
parser.add_argument("--alias", help=_("Clear given cache only"))
|
|
11
|
+
|
|
12
|
+
def handle(self, *args, **options):
|
|
13
|
+
alias = options["alias"]
|
|
14
|
+
cache = caches[alias] if alias else caches[DEFAULT_CACHE_ALIAS]
|
|
15
|
+
|
|
16
|
+
cache.clear()
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from django.db import migrations, router
|
|
2
|
+
|
|
3
|
+
RESET_SQL = """
|
|
4
|
+
SELECT 'SELECT SETVAL(' ||
|
|
5
|
+
quote_literal(quote_ident(PGT.schemaname) || '.' || quote_ident(S.relname)) ||
|
|
6
|
+
', COALESCE(MAX(' ||quote_ident(C.attname)|| '), 1) ) FROM ' ||
|
|
7
|
+
quote_ident(PGT.schemaname)|| '.'||quote_ident(T.relname)|| ';'
|
|
8
|
+
FROM pg_class AS S,
|
|
9
|
+
pg_depend AS D,
|
|
10
|
+
pg_class AS T,
|
|
11
|
+
pg_attribute AS C,
|
|
12
|
+
pg_tables AS PGT
|
|
13
|
+
WHERE S.relkind = 'S'
|
|
14
|
+
AND S.oid = D.objid
|
|
15
|
+
AND D.refobjid = T.oid
|
|
16
|
+
AND D.refobjid = C.attrelid
|
|
17
|
+
AND D.refobjsubid = C.attnum
|
|
18
|
+
AND T.relname = PGT.tablename
|
|
19
|
+
ORDER BY S.relname;
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ResetSequences(migrations.RunSQL):
|
|
24
|
+
"""
|
|
25
|
+
Run the reset_sequences SQL.
|
|
26
|
+
|
|
27
|
+
Resetting the Postgres sequences makes sure you don't get Integrity Errors
|
|
28
|
+
when creating objects because of failing unique constraints. This happens
|
|
29
|
+
when you create records explicitly with PKs, bypassing the database PK
|
|
30
|
+
generation. Resetting the sequences makes sure the sequences are aware of
|
|
31
|
+
the used PKs.
|
|
32
|
+
|
|
33
|
+
Usage:
|
|
34
|
+
|
|
35
|
+
>>> from maykin_common.migration_operations import ResetSequences
|
|
36
|
+
>>> class Migration(migrations.Migration):
|
|
37
|
+
... dependencies = (...)
|
|
38
|
+
... operations = [
|
|
39
|
+
... ResetSequences(),
|
|
40
|
+
... ]
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
reversible = True
|
|
44
|
+
|
|
45
|
+
def __init__(self, *args, **kwargs):
|
|
46
|
+
super().__init__(None, *args, **kwargs)
|
|
47
|
+
|
|
48
|
+
def database_forwards(self, app_label, schema_editor, from_state, to_state) -> None:
|
|
49
|
+
if router.allow_migrate(
|
|
50
|
+
schema_editor.connection.alias, app_label, **self.hints
|
|
51
|
+
):
|
|
52
|
+
with schema_editor.connection.cursor() as cursor:
|
|
53
|
+
cursor.execute(RESET_SQL)
|
|
54
|
+
rows = cursor.fetchall()
|
|
55
|
+
|
|
56
|
+
sql = "\n".join(x[0] for x in rows)
|
|
57
|
+
|
|
58
|
+
self._run_sql(schema_editor, sql)
|
|
59
|
+
|
|
60
|
+
def database_backwards(self, *args, **kwargs) -> None:
|
|
61
|
+
pass
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from time import time
|
|
2
|
+
|
|
3
|
+
from django.core.cache import caches
|
|
4
|
+
from django.core.exceptions import PermissionDenied
|
|
5
|
+
from django.http import HttpRequest
|
|
6
|
+
|
|
7
|
+
from axes.helpers import get_client_ip_address
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ThrottleMixin:
|
|
11
|
+
"""
|
|
12
|
+
A very simple throttling implementation with, hopefully, sane defaults.
|
|
13
|
+
|
|
14
|
+
You can specifiy the amount of visits (throttle_visits) a view can get,
|
|
15
|
+
for a specific period (in seconds) throttle_period.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
# n visits per period (in seconds)
|
|
19
|
+
throttle_visits = 100
|
|
20
|
+
throttle_period = 60**2 # in seconds
|
|
21
|
+
throttle_403 = True
|
|
22
|
+
throttle_name = "default"
|
|
23
|
+
|
|
24
|
+
# get and options should always be fast. By default
|
|
25
|
+
# do not throttle them.
|
|
26
|
+
throttle_methods = ["post", "put", "patch", "delete", "head", "trace"]
|
|
27
|
+
|
|
28
|
+
request: HttpRequest
|
|
29
|
+
|
|
30
|
+
def get_throttle_cache(self):
|
|
31
|
+
return caches["default"]
|
|
32
|
+
|
|
33
|
+
def get_throttle_identifier(self):
|
|
34
|
+
user = getattr(self, "user_cache", self.request.user)
|
|
35
|
+
return str(user.id)
|
|
36
|
+
|
|
37
|
+
def create_throttle_key(self):
|
|
38
|
+
"""
|
|
39
|
+
:rtype string Use as key to save the last access
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
f"throttling_{self.get_throttle_identifier()}_"
|
|
44
|
+
f"{self.throttle_name}_{self.get_throttle_window()}"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
def get_throttle_window(self):
|
|
48
|
+
"""
|
|
49
|
+
round down to the throttle_period, which is then used to create the key.
|
|
50
|
+
"""
|
|
51
|
+
current_time = int(time())
|
|
52
|
+
return current_time - (current_time % self.throttle_period)
|
|
53
|
+
|
|
54
|
+
def get_visits_in_window(self):
|
|
55
|
+
cache = self.get_throttle_cache()
|
|
56
|
+
key = self.create_throttle_key()
|
|
57
|
+
|
|
58
|
+
initial_visits = 1
|
|
59
|
+
stored = cache.add(key, initial_visits, self.throttle_period)
|
|
60
|
+
if stored:
|
|
61
|
+
visits = initial_visits
|
|
62
|
+
else:
|
|
63
|
+
try:
|
|
64
|
+
visits = cache.incr(key)
|
|
65
|
+
except ValueError:
|
|
66
|
+
visits = initial_visits
|
|
67
|
+
return visits
|
|
68
|
+
|
|
69
|
+
def should_be_throttled(self):
|
|
70
|
+
if self.throttle_methods == "all":
|
|
71
|
+
return True
|
|
72
|
+
return self.request.method.lower() in self.throttle_methods
|
|
73
|
+
|
|
74
|
+
def dispatch(self, request, *args, **kwargs):
|
|
75
|
+
if self.throttle_403:
|
|
76
|
+
if (
|
|
77
|
+
self.should_be_throttled()
|
|
78
|
+
and self.get_visits_in_window() > self.throttle_visits
|
|
79
|
+
):
|
|
80
|
+
raise PermissionDenied
|
|
81
|
+
|
|
82
|
+
return super().dispatch(request, *args, **kwargs)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class IPThrottleMixin(ThrottleMixin):
|
|
86
|
+
"""
|
|
87
|
+
Same behavior as ThrottleMixin except it limits the amount of tries
|
|
88
|
+
per IP-address a user can access a certain view.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
def get_throttle_identifier(self):
|
|
92
|
+
return get_client_ip_address(self.request)
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utilities for PDF rendering from HTML using WeasyPrint.
|
|
3
|
+
|
|
4
|
+
Note that you need to add https://pypi.org/project/weasyprint/ to your dependencies
|
|
5
|
+
if you want to make use of HTML-to-PDF rendering. This is not included by default as
|
|
6
|
+
it's quite heavy and requires OS-level dependencies.
|
|
7
|
+
|
|
8
|
+
This module exposes the public function :func:`render_to_pdf` which renders a template
|
|
9
|
+
with a context into a PDF document (bytes output). You can use "external" stylesheets
|
|
10
|
+
in these templates, and they will be resolved through django's staticfiles machinery
|
|
11
|
+
by the custom :class:`UrlFetcher`.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import logging
|
|
15
|
+
import mimetypes
|
|
16
|
+
from io import BytesIO
|
|
17
|
+
from pathlib import PurePosixPath
|
|
18
|
+
from urllib.parse import urljoin, urlparse
|
|
19
|
+
|
|
20
|
+
from django.conf import settings
|
|
21
|
+
from django.contrib.staticfiles import finders
|
|
22
|
+
from django.contrib.staticfiles.storage import staticfiles_storage
|
|
23
|
+
from django.core.files.storage import FileSystemStorage, default_storage
|
|
24
|
+
from django.template.loader import render_to_string
|
|
25
|
+
from django.utils.module_loading import import_string
|
|
26
|
+
|
|
27
|
+
import weasyprint
|
|
28
|
+
|
|
29
|
+
from maykin_common.settings import get_setting
|
|
30
|
+
|
|
31
|
+
logger = logging.getLogger(__name__)
|
|
32
|
+
|
|
33
|
+
__all__ = ["render_to_pdf"]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def get_base_url(*args, **kwargs) -> str:
|
|
37
|
+
"""
|
|
38
|
+
Get the base URL where the project is served.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
if pdf_base_url_function := get_setting("PDF_BASE_URL_FUNCTION"):
|
|
42
|
+
return import_string(pdf_base_url_function)(*args, **kwargs)
|
|
43
|
+
|
|
44
|
+
raise NotImplementedError("You must implement 'get_base_url'.")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class UrlFetcher:
|
|
48
|
+
"""
|
|
49
|
+
URL fetcher that skips the network for /static/* files.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(self):
|
|
53
|
+
self.static_url = self._get_fully_qualified_url(settings.STATIC_URL)
|
|
54
|
+
is_static_local_storage = issubclass(
|
|
55
|
+
staticfiles_storage.__class__, FileSystemStorage
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
self.media_url = self._get_fully_qualified_url(settings.MEDIA_URL)
|
|
59
|
+
is_media_local_storage = issubclass(
|
|
60
|
+
default_storage.__class__, FileSystemStorage
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
self.candidates = (
|
|
64
|
+
(self.static_url, staticfiles_storage, is_static_local_storage),
|
|
65
|
+
(self.media_url, default_storage, is_media_local_storage),
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
@staticmethod
|
|
69
|
+
def _get_fully_qualified_url(setting: str):
|
|
70
|
+
fully_qualified_url = setting
|
|
71
|
+
if not urlparse(setting).netloc:
|
|
72
|
+
fully_qualified_url = urljoin(get_base_url(), setting)
|
|
73
|
+
return urlparse(fully_qualified_url)
|
|
74
|
+
|
|
75
|
+
def __call__(self, url: str) -> dict:
|
|
76
|
+
orig_url = url
|
|
77
|
+
parsed_url = urlparse(url)
|
|
78
|
+
|
|
79
|
+
candidate = self.get_match_candidate(parsed_url)
|
|
80
|
+
if candidate is not None:
|
|
81
|
+
base_url, storage = candidate
|
|
82
|
+
path = PurePosixPath(parsed_url.path).relative_to(base_url.path)
|
|
83
|
+
|
|
84
|
+
absolute_path = None
|
|
85
|
+
if storage.exists(path):
|
|
86
|
+
absolute_path = storage.path(path)
|
|
87
|
+
elif settings.DEBUG and storage is staticfiles_storage:
|
|
88
|
+
# use finders so that it works in dev too, we already check that it's
|
|
89
|
+
# using filesystem storage earlier
|
|
90
|
+
absolute_path = finders.find(str(path))
|
|
91
|
+
|
|
92
|
+
if absolute_path is None:
|
|
93
|
+
logger.error("Could not resolve path '%s'", path)
|
|
94
|
+
return weasyprint.default_url_fetcher(orig_url)
|
|
95
|
+
|
|
96
|
+
content_type, encoding = mimetypes.guess_type(absolute_path)
|
|
97
|
+
result = dict(
|
|
98
|
+
mime_type=content_type,
|
|
99
|
+
encoding=encoding,
|
|
100
|
+
redirected_url=orig_url,
|
|
101
|
+
filename=path.parts[-1],
|
|
102
|
+
)
|
|
103
|
+
with open(absolute_path, "rb") as f:
|
|
104
|
+
result["file_obj"] = BytesIO(f.read())
|
|
105
|
+
return result
|
|
106
|
+
return weasyprint.default_url_fetcher(orig_url)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def render_to_pdf(template_name: str, context: dict) -> tuple[str, bytes]:
|
|
110
|
+
"""
|
|
111
|
+
Render a (HTML) template to PDF with the given context.
|
|
112
|
+
"""
|
|
113
|
+
rendered_html = render_to_string(template_name, context=context)
|
|
114
|
+
html_object = weasyprint.HTML(
|
|
115
|
+
string=rendered_html,
|
|
116
|
+
url_fetcher=UrlFetcher(),
|
|
117
|
+
base_url=get_base_url(),
|
|
118
|
+
)
|
|
119
|
+
pdf: bytes = html_object.write_pdf()
|
|
120
|
+
return rendered_html, pdf
|
|
File without changes
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from django.conf import settings
|
|
2
|
+
|
|
3
|
+
GOOGLE_ANALYTICS_ID = None
|
|
4
|
+
"""Google analytics id added to the request context"""
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
SHOW_ENVIRONMENT = False
|
|
8
|
+
"""enable or disable the `environment_info` template tag."""
|
|
9
|
+
|
|
10
|
+
ENVIRONMENT_BACKGROUND_COLOR = "orange"
|
|
11
|
+
"""The background color of the `environment_info` template tag."""
|
|
12
|
+
|
|
13
|
+
ENVIRONMENT_FOREGROUND_COLOR = "black"
|
|
14
|
+
"""The foreground color of the `environment_info` template tag."""
|
|
15
|
+
|
|
16
|
+
ENVIRONMENT_LABEL = None
|
|
17
|
+
"""The foreground color of the `environment_info` template tag."""
|
|
18
|
+
|
|
19
|
+
RELEASE = None
|
|
20
|
+
"""The environment label shown in the `version_info` template tag."""
|
|
21
|
+
|
|
22
|
+
GIT_SHA = None
|
|
23
|
+
"""The git hash shown in the `version_info` template tag."""
|
|
24
|
+
|
|
25
|
+
PDF_BASE_URL_FUNCTION = None
|
|
26
|
+
"""
|
|
27
|
+
Function that returns the base url needed to download/resolve custom fonts and/or any
|
|
28
|
+
image URLs included in the document to render.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_setting(name: str):
|
|
33
|
+
default = globals()[name]
|
|
34
|
+
return getattr(settings, name, default)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
.version {
|
|
2
|
+
color: var(--body-fg);
|
|
3
|
+
padding: 0 30px;
|
|
4
|
+
font-size: smaller;
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.env-info {
|
|
10
|
+
display: block;
|
|
11
|
+
align-self: center;
|
|
12
|
+
padding-inline: 8px;
|
|
13
|
+
padding-block: 5px;
|
|
14
|
+
background-color: var(--admin-env-info-background-color, var(--secondary));
|
|
15
|
+
color: var(--admin-env-info-color, var(--primary-fg));
|
|
16
|
+
border-radius: 4px;
|
|
17
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{% load i18n static %}
|
|
2
|
+
<link rel="stylesheet" href="{% static 'css/maykin_common.css' %}">
|
|
3
|
+
<div class="version" title="Git SHA: {{ GIT_SHA|default:'' }}">
|
|
4
|
+
<span class="version__release">
|
|
5
|
+
{% blocktrans %}version {{ RELEASE }}{% endblocktrans %}
|
|
6
|
+
</span>
|
|
7
|
+
{% if GIT_SHA %}
|
|
8
|
+
<code class="version__git-hash">
|
|
9
|
+
{% blocktrans %}GIT SHA: {{ GIT_SHA }}{% endblocktrans %}
|
|
10
|
+
</code>
|
|
11
|
+
{% endif %}
|
|
12
|
+
</div>
|
|
File without changes
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from django import template
|
|
4
|
+
from django.utils.html import escape, format_html, mark_safe
|
|
5
|
+
|
|
6
|
+
from maykin_common.settings import get_setting
|
|
7
|
+
|
|
8
|
+
register = template.Library()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@register.inclusion_tag("maykin_common/includes/version_info.html")
|
|
12
|
+
def show_version_info():
|
|
13
|
+
"""
|
|
14
|
+
Template that displays version info.
|
|
15
|
+
"""
|
|
16
|
+
return {
|
|
17
|
+
"RELEASE": get_setting("RELEASE"),
|
|
18
|
+
"GIT_SHA": get_setting("GIT_SHA"),
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@register.simple_tag(takes_context=True)
|
|
23
|
+
def show_environment_info(context: dict[str, Any]) -> str:
|
|
24
|
+
"""
|
|
25
|
+
Template that show the current ENVIRONMENT to an authenticated user.
|
|
26
|
+
|
|
27
|
+
Returns an empty string if SHOW_ENVIRONMENT is set to `False`
|
|
28
|
+
"""
|
|
29
|
+
if not get_setting("SHOW_ENVIRONMENT"):
|
|
30
|
+
return ""
|
|
31
|
+
if (user := context.get("user")) is None or not user.is_authenticated:
|
|
32
|
+
return ""
|
|
33
|
+
|
|
34
|
+
style_tokens = {
|
|
35
|
+
"background-color": get_setting("ENVIRONMENT_BACKGROUND_COLOR"),
|
|
36
|
+
"color": get_setting("ENVIRONMENT_FOREGROUND_COLOR"),
|
|
37
|
+
}
|
|
38
|
+
_inline_style_bits = [
|
|
39
|
+
f"--admin-env-info-{key}: {value}".format(key=key, value=escape(value))
|
|
40
|
+
for key, value in style_tokens.items()
|
|
41
|
+
]
|
|
42
|
+
return format_html(
|
|
43
|
+
"""<div class="env-info" style="{style}">{label}</div>""",
|
|
44
|
+
label=get_setting("ENVIRONMENT_LABEL"),
|
|
45
|
+
style=mark_safe("; ".join(_inline_style_bits)),
|
|
46
|
+
)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from django import http
|
|
2
|
+
from django.template import TemplateDoesNotExist, loader
|
|
3
|
+
from django.views.decorators.csrf import requires_csrf_token
|
|
4
|
+
from django.views.defaults import ERROR_500_TEMPLATE_NAME
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@requires_csrf_token
|
|
8
|
+
def server_error(
|
|
9
|
+
request, template_name=ERROR_500_TEMPLATE_NAME
|
|
10
|
+
) -> http.HttpResponseServerError:
|
|
11
|
+
"""
|
|
12
|
+
500 error handler.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
template = loader.get_template(template_name)
|
|
17
|
+
except TemplateDoesNotExist:
|
|
18
|
+
if template_name != ERROR_500_TEMPLATE_NAME:
|
|
19
|
+
# Reraise if it's a missing custom template.
|
|
20
|
+
raise
|
|
21
|
+
return http.HttpResponseServerError(
|
|
22
|
+
b"<h1>Server Error (500)</h1>", content_type="text/html"
|
|
23
|
+
)
|
|
24
|
+
context = {"request": request}
|
|
25
|
+
return http.HttpResponseServerError(template.render(context))
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: maykin-common
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Re-usable utilities for Maykin Django projects, centrally maintained instead of in default-project.
|
|
5
|
+
Author-email: Maykin Media <support@maykinmedia.nl>
|
|
6
|
+
License: Copyright 2025 Maykin Media
|
|
7
|
+
|
|
8
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
11
|
+
|
|
12
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
13
|
+
|
|
14
|
+
Project-URL: Homepage, https://github.com/maykinmedia/django-common
|
|
15
|
+
Project-URL: Documentation, http://maykin-django-common.readthedocs.io/en/latest/
|
|
16
|
+
Project-URL: Bug Tracker, https://github.com/maykinmedia/django-common/issues
|
|
17
|
+
Project-URL: Source Code, https://github.com/maykinmedia/django-common
|
|
18
|
+
Keywords: django,maykin,boilerplate
|
|
19
|
+
Classifier: Development Status :: 3 - Alpha
|
|
20
|
+
Classifier: Framework :: Django
|
|
21
|
+
Classifier: Framework :: Django :: 4.2
|
|
22
|
+
Classifier: Framework :: Django :: 5.2
|
|
23
|
+
Classifier: Intended Audience :: Developers
|
|
24
|
+
Classifier: Operating System :: Unix
|
|
25
|
+
Classifier: Operating System :: MacOS
|
|
26
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
27
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
28
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
29
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
30
|
+
Requires-Python: >=3.12
|
|
31
|
+
Description-Content-Type: text/x-rst
|
|
32
|
+
License-File: LICENSE
|
|
33
|
+
Requires-Dist: django>=4.2
|
|
34
|
+
Provides-Extra: tests
|
|
35
|
+
Requires-Dist: pytest; extra == "tests"
|
|
36
|
+
Requires-Dist: pytest-django; extra == "tests"
|
|
37
|
+
Requires-Dist: pytest-cov; extra == "tests"
|
|
38
|
+
Requires-Dist: tox; extra == "tests"
|
|
39
|
+
Requires-Dist: ruff; extra == "tests"
|
|
40
|
+
Provides-Extra: docs
|
|
41
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
42
|
+
Requires-Dist: sphinx-rtd-theme; extra == "docs"
|
|
43
|
+
Provides-Extra: release
|
|
44
|
+
Requires-Dist: bump-my-version; extra == "release"
|
|
45
|
+
Provides-Extra: pdf
|
|
46
|
+
Requires-Dist: weasyprint; extra == "pdf"
|
|
47
|
+
Provides-Extra: mfa
|
|
48
|
+
Requires-Dist: django-admin-index; extra == "mfa"
|
|
49
|
+
Requires-Dist: maykin-2fa; extra == "mfa"
|
|
50
|
+
Provides-Extra: axes
|
|
51
|
+
Requires-Dist: django-axes; extra == "axes"
|
|
52
|
+
Dynamic: license-file
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
Welcome to maykin-common's documentation!
|
|
57
|
+
=================================================
|
|
58
|
+
|
|
59
|
+
:Version: 0.1.0
|
|
60
|
+
:Source: https://github.com/maykinmedia/django-common
|
|
61
|
+
:Keywords: ``<keywords>``
|
|
62
|
+
:PythonVersion: 3.12
|
|
63
|
+
|
|
64
|
+
|build-status| |code-quality| |ruff| |coverage| |docs|
|
|
65
|
+
|
|
66
|
+
|python-versions| |django-versions| |pypi-version|
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
<One liner describing the project>
|
|
70
|
+
|
|
71
|
+
.. contents::
|
|
72
|
+
|
|
73
|
+
.. section-numbering::
|
|
74
|
+
|
|
75
|
+
Features
|
|
76
|
+
========
|
|
77
|
+
|
|
78
|
+
* ...
|
|
79
|
+
* ...
|
|
80
|
+
|
|
81
|
+
Installation
|
|
82
|
+
============
|
|
83
|
+
|
|
84
|
+
Requirements
|
|
85
|
+
------------
|
|
86
|
+
|
|
87
|
+
* Python 3.12 or above
|
|
88
|
+
* Django 4.2 or newer
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
Install
|
|
92
|
+
-------
|
|
93
|
+
|
|
94
|
+
.. code-block:: bash
|
|
95
|
+
|
|
96
|
+
pip install maykin-common
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
Usage
|
|
100
|
+
=====
|
|
101
|
+
|
|
102
|
+
<document or refer to docs>
|
|
103
|
+
|
|
104
|
+
Local development
|
|
105
|
+
=================
|
|
106
|
+
|
|
107
|
+
To install and develop the library locally, use::
|
|
108
|
+
|
|
109
|
+
.. code-block:: bash
|
|
110
|
+
|
|
111
|
+
pip install -e .[tests,coverage,docs,release]
|
|
112
|
+
|
|
113
|
+
When running management commands via ``django-admin``, make sure to add the root
|
|
114
|
+
directory to the python path (or use ``python -m django <command>``):
|
|
115
|
+
|
|
116
|
+
.. code-block:: bash
|
|
117
|
+
|
|
118
|
+
export PYTHONPATH=. DJANGO_SETTINGS_MODULE=testapp.settings
|
|
119
|
+
django-admin check
|
|
120
|
+
# or other commands like:
|
|
121
|
+
# django-admin makemessages -l nl
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
.. |build-status| image:: https://github.com/maykinmedia/django-common/workflows/Run%20CI/badge.svg
|
|
125
|
+
:alt: Build status
|
|
126
|
+
:target: https://github.com/maykinmedia/django-common/actions?query=workflow%3A%22Run+CI%22
|
|
127
|
+
|
|
128
|
+
.. |code-quality| image:: https://github.com/maykinmedia/django-common/workflows/Code%20quality%20checks/badge.svg
|
|
129
|
+
:alt: Code quality checks
|
|
130
|
+
:target: https://github.com/maykinmedia/django-common/actions?query=workflow%3A%22Code+quality+checks%22
|
|
131
|
+
|
|
132
|
+
.. |ruff| image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
|
|
133
|
+
:target: https://github.com/astral-sh/ruff
|
|
134
|
+
:alt: Ruff
|
|
135
|
+
|
|
136
|
+
.. |coverage| image:: https://codecov.io/gh/maykinmedia/django-common/branch/main/graph/badge.svg
|
|
137
|
+
:target: https://codecov.io/gh/maykinmedia/django-common
|
|
138
|
+
:alt: Coverage status
|
|
139
|
+
|
|
140
|
+
.. |docs| image:: https://readthedocs.org/projects/django-common/badge/?version=latest
|
|
141
|
+
:target: https://maykin-django-common.readthedocs.io/en/latest/?badge=latest
|
|
142
|
+
:alt: Documentation Status
|
|
143
|
+
|
|
144
|
+
.. |python-versions| image:: https://img.shields.io/pypi/pyversions/django-common.svg
|
|
145
|
+
|
|
146
|
+
.. |django-versions| image:: https://img.shields.io/pypi/djversions/django-common.svg
|
|
147
|
+
|
|
148
|
+
.. |pypi-version| image:: https://img.shields.io/pypi/v/django-common.svg
|
|
149
|
+
:target: https://pypi.org/project/django-common/
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.rst
|
|
4
|
+
pyproject.toml
|
|
5
|
+
maykin_common/__init__.py
|
|
6
|
+
maykin_common/apps.py
|
|
7
|
+
maykin_common/checks.py
|
|
8
|
+
maykin_common/context_processors.py
|
|
9
|
+
maykin_common/django_two_factor_auth.py
|
|
10
|
+
maykin_common/migration_operations.py
|
|
11
|
+
maykin_common/mixins.py
|
|
12
|
+
maykin_common/pdf.py
|
|
13
|
+
maykin_common/py.typed
|
|
14
|
+
maykin_common/settings.py
|
|
15
|
+
maykin_common/views.py
|
|
16
|
+
maykin_common.egg-info/PKG-INFO
|
|
17
|
+
maykin_common.egg-info/SOURCES.txt
|
|
18
|
+
maykin_common.egg-info/dependency_links.txt
|
|
19
|
+
maykin_common.egg-info/requires.txt
|
|
20
|
+
maykin_common.egg-info/top_level.txt
|
|
21
|
+
maykin_common/management/__init__.py
|
|
22
|
+
maykin_common/management/commands/__init__.py
|
|
23
|
+
maykin_common/management/commands/clear_cache.py
|
|
24
|
+
maykin_common/static/maykin_common/css/version.css
|
|
25
|
+
maykin_common/templates/maykin_common/includes/version_info.html
|
|
26
|
+
maykin_common/templatetags/__init__.py
|
|
27
|
+
maykin_common/templatetags/maykin_common.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
django>=4.2
|
|
2
|
+
|
|
3
|
+
[axes]
|
|
4
|
+
django-axes
|
|
5
|
+
|
|
6
|
+
[docs]
|
|
7
|
+
sphinx
|
|
8
|
+
sphinx-rtd-theme
|
|
9
|
+
|
|
10
|
+
[mfa]
|
|
11
|
+
django-admin-index
|
|
12
|
+
maykin-2fa
|
|
13
|
+
|
|
14
|
+
[pdf]
|
|
15
|
+
weasyprint
|
|
16
|
+
|
|
17
|
+
[release]
|
|
18
|
+
bump-my-version
|
|
19
|
+
|
|
20
|
+
[tests]
|
|
21
|
+
pytest
|
|
22
|
+
pytest-django
|
|
23
|
+
pytest-cov
|
|
24
|
+
tox
|
|
25
|
+
ruff
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
maykin_common
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "maykin-common"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Re-usable utilities for Maykin Django projects, centrally maintained instead of in default-project."
|
|
9
|
+
authors = [
|
|
10
|
+
{name = "Maykin Media", email = "support@maykinmedia.nl"}
|
|
11
|
+
]
|
|
12
|
+
readme = "README.rst"
|
|
13
|
+
license = {file = "LICENSE"}
|
|
14
|
+
keywords = ["django", "maykin", "boilerplate"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Framework :: Django",
|
|
18
|
+
"Framework :: Django :: 4.2",
|
|
19
|
+
"Framework :: Django :: 5.2",
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
"Operating System :: Unix",
|
|
22
|
+
"Operating System :: MacOS",
|
|
23
|
+
"Operating System :: Microsoft :: Windows",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.13",
|
|
26
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
27
|
+
]
|
|
28
|
+
requires-python = ">=3.12"
|
|
29
|
+
dependencies = [
|
|
30
|
+
"django>=4.2"
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Homepage = "https://github.com/maykinmedia/django-common"
|
|
35
|
+
Documentation = "http://maykin-django-common.readthedocs.io/en/latest/"
|
|
36
|
+
"Bug Tracker" = "https://github.com/maykinmedia/django-common/issues"
|
|
37
|
+
"Source Code" = "https://github.com/maykinmedia/django-common"
|
|
38
|
+
|
|
39
|
+
[project.optional-dependencies]
|
|
40
|
+
tests = [
|
|
41
|
+
"pytest",
|
|
42
|
+
"pytest-django",
|
|
43
|
+
"pytest-cov",
|
|
44
|
+
"tox",
|
|
45
|
+
"ruff",
|
|
46
|
+
# "pyright",
|
|
47
|
+
# "django-stubs",
|
|
48
|
+
]
|
|
49
|
+
docs = [
|
|
50
|
+
"sphinx",
|
|
51
|
+
"sphinx-rtd-theme",
|
|
52
|
+
]
|
|
53
|
+
release = [
|
|
54
|
+
"bump-my-version",
|
|
55
|
+
]
|
|
56
|
+
pdf = [
|
|
57
|
+
"weasyprint"
|
|
58
|
+
]
|
|
59
|
+
mfa = [
|
|
60
|
+
"django-admin-index",
|
|
61
|
+
"maykin-2fa"
|
|
62
|
+
]
|
|
63
|
+
axes = [
|
|
64
|
+
"django-axes"
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
[tool.setuptools.packages.find]
|
|
68
|
+
include = ["maykin_common*"]
|
|
69
|
+
namespaces = true
|
|
70
|
+
|
|
71
|
+
[tool.pytest.ini_options]
|
|
72
|
+
testpaths = ["tests"]
|
|
73
|
+
DJANGO_SETTINGS_MODULE = "testapp.settings"
|
|
74
|
+
|
|
75
|
+
[tool.bumpversion]
|
|
76
|
+
current_version = "0.1.0"
|
|
77
|
+
files = [
|
|
78
|
+
{filename = "pyproject.toml"},
|
|
79
|
+
{filename = "README.rst"},
|
|
80
|
+
{filename = "docs/conf.py"},
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
[tool.coverage.run]
|
|
84
|
+
branch = true
|
|
85
|
+
source = ["maykin_common"]
|
|
86
|
+
omit = [
|
|
87
|
+
# migrations run while django initializes the test db
|
|
88
|
+
"*/migrations/*",
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
[tool.coverage.report]
|
|
92
|
+
exclude_also = [
|
|
93
|
+
"if (typing\\.)?TYPE_CHECKING:",
|
|
94
|
+
"@(typing\\.)?overload",
|
|
95
|
+
"class .*\\(.*Protocol.*\\):",
|
|
96
|
+
"@(abc\\.)?abstractmethod",
|
|
97
|
+
"raise NotImplementedError",
|
|
98
|
+
"\\.\\.\\.",
|
|
99
|
+
"\\bpass$",
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
[tool.pyright]
|
|
103
|
+
include = [
|
|
104
|
+
"{{ project_name|lower }}",
|
|
105
|
+
"testapp",
|
|
106
|
+
"tests",
|
|
107
|
+
]
|
|
108
|
+
ignore = []
|
|
109
|
+
pythonVersion = "3.12"
|
|
110
|
+
pythonPlatform = "Linux"
|
|
111
|
+
|
|
112
|
+
[tool.ruff.lint]
|
|
113
|
+
extend-select = [
|
|
114
|
+
"UP", # pyupgrade
|
|
115
|
+
"DJ", # django
|
|
116
|
+
"LOG", # logging
|
|
117
|
+
"G",
|
|
118
|
+
"I", # isort
|
|
119
|
+
"E", # pycodestyle
|
|
120
|
+
"F", # pyflakes
|
|
121
|
+
"PERF",# perflint
|
|
122
|
+
"B", # flake8-bugbear
|
|
123
|
+
]
|
|
124
|
+
|
|
125
|
+
[tool.ruff.lint.isort]
|
|
126
|
+
combine-as-imports = true
|
|
127
|
+
section-order = [
|
|
128
|
+
"future",
|
|
129
|
+
"standard-library",
|
|
130
|
+
"django",
|
|
131
|
+
"third-party",
|
|
132
|
+
"first-party",
|
|
133
|
+
"local-folder",
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
[tool.ruff.lint.isort.sections]
|
|
137
|
+
"django" = ["django"]
|