djmvc 0.0.1__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.
- djmvc-0.0.1/.gitignore +7 -0
- djmvc-0.0.1/PKG-INFO +167 -0
- djmvc-0.0.1/README.md +136 -0
- djmvc-0.0.1/manage.py +23 -0
- djmvc-0.0.1/pyproject.toml +85 -0
- djmvc-0.0.1/setup.cfg +4 -0
- djmvc-0.0.1/src/djmvc/__init__.py +4 -0
- djmvc-0.0.1/src/djmvc/admin.py +3 -0
- djmvc-0.0.1/src/djmvc/apps.py +5 -0
- djmvc-0.0.1/src/djmvc/clonable.py +11 -0
- djmvc-0.0.1/src/djmvc/controller.py +83 -0
- djmvc-0.0.1/src/djmvc/management/commands/show_urls.py +211 -0
- djmvc-0.0.1/src/djmvc/model.py +39 -0
- djmvc-0.0.1/src/djmvc/models.py +3 -0
- djmvc-0.0.1/src/djmvc/route.py +42 -0
- djmvc-0.0.1/src/djmvc/templatetags/eval.py +154 -0
- djmvc-0.0.1/src/djmvc/tests.py +3 -0
- djmvc-0.0.1/src/djmvc/view.py +40 -0
- djmvc-0.0.1/src/djmvc/views/create.py +13 -0
- djmvc-0.0.1/src/djmvc/views/delete.py +15 -0
- djmvc-0.0.1/src/djmvc/views/detail.py +93 -0
- djmvc-0.0.1/src/djmvc/views/form.py +28 -0
- djmvc-0.0.1/src/djmvc/views/generic.py +7 -0
- djmvc-0.0.1/src/djmvc/views/list.py +19 -0
- djmvc-0.0.1/src/djmvc/views/modelform.py +6 -0
- djmvc-0.0.1/src/djmvc/views/tables2.py +106 -0
- djmvc-0.0.1/src/djmvc/views/template.py +42 -0
- djmvc-0.0.1/src/djmvc/views/update.py +14 -0
- djmvc-0.0.1/src/djmvc.egg-info/PKG-INFO +167 -0
- djmvc-0.0.1/src/djmvc.egg-info/SOURCES.txt +71 -0
- djmvc-0.0.1/src/djmvc.egg-info/dependency_links.txt +1 -0
- djmvc-0.0.1/src/djmvc.egg-info/requires.txt +14 -0
- djmvc-0.0.1/src/djmvc.egg-info/scm_file_list.json +67 -0
- djmvc-0.0.1/src/djmvc.egg-info/scm_version.json +8 -0
- djmvc-0.0.1/src/djmvc.egg-info/top_level.txt +4 -0
- djmvc-0.0.1/src/djmvc_auth/__init__.py +0 -0
- djmvc-0.0.1/src/djmvc_auth/admin.py +3 -0
- djmvc-0.0.1/src/djmvc_auth/apps.py +5 -0
- djmvc-0.0.1/src/djmvc_auth/controller.py +64 -0
- djmvc-0.0.1/src/djmvc_auth/models.py +3 -0
- djmvc-0.0.1/src/djmvc_auth/tests.py +3 -0
- djmvc-0.0.1/src/djmvc_auth/views.py +40 -0
- djmvc-0.0.1/src/djmvc_bulma/__init__.py +0 -0
- djmvc-0.0.1/src/djmvc_bulma/admin.py +3 -0
- djmvc-0.0.1/src/djmvc_bulma/apps.py +14 -0
- djmvc-0.0.1/src/djmvc_bulma/models.py +3 -0
- djmvc-0.0.1/src/djmvc_bulma/static/djmvc_bulma/css/style.css +30 -0
- djmvc-0.0.1/src/djmvc_bulma/static/djmvc_bulma/js/hamburger.js +70 -0
- djmvc-0.0.1/src/djmvc_bulma/static/djmvc_bulma/js/hamburger.test.js +226 -0
- djmvc-0.0.1/src/djmvc_bulma/templates/djmvc/_actions_column.html +48 -0
- djmvc-0.0.1/src/djmvc_bulma/templates/djmvc/_tables2.html +142 -0
- djmvc-0.0.1/src/djmvc_bulma/templates/djmvc/base.html +85 -0
- djmvc-0.0.1/src/djmvc_bulma/templates/djmvc/detail.html +49 -0
- djmvc-0.0.1/src/djmvc_bulma/templates/djmvc/form.html +74 -0
- djmvc-0.0.1/src/djmvc_bulma/templates/djmvc/list.html +44 -0
- djmvc-0.0.1/src/djmvc_bulma/templates/home.html +32 -0
- djmvc-0.0.1/src/djmvc_bulma/tests.py +3 -0
- djmvc-0.0.1/src/djmvc_bulma/views.py +3 -0
- djmvc-0.0.1/src/djmvc_example/__init__.py +0 -0
- djmvc-0.0.1/src/djmvc_example/asgi.py +16 -0
- djmvc-0.0.1/src/djmvc_example/example_urls.py +35 -0
- djmvc-0.0.1/src/djmvc_example/migrations/0001_initial.py +132 -0
- djmvc-0.0.1/src/djmvc_example/migrations/0002_test_users.py +27 -0
- djmvc-0.0.1/src/djmvc_example/migrations/__init__.py +0 -0
- djmvc-0.0.1/src/djmvc_example/models.py +5 -0
- djmvc-0.0.1/src/djmvc_example/settings.py +129 -0
- djmvc-0.0.1/src/djmvc_example/urls.py +44 -0
- djmvc-0.0.1/src/djmvc_example/wsgi.py +16 -0
- djmvc-0.0.1/tests/test_controller.py +37 -0
- djmvc-0.0.1/tests/test_eval.py +20 -0
- djmvc-0.0.1/tests/test_routing.py +45 -0
- djmvc-0.0.1/tests/test_view.py +19 -0
- djmvc-0.0.1/tests/views/test_template.py +18 -0
djmvc-0.0.1/.gitignore
ADDED
djmvc-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: djmvc
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Django CRUD utilities
|
|
5
|
+
Author-email: Your Name <your.email@example.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Requires-Python: >=3.8
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: django>=5.1
|
|
20
|
+
Requires-Dist: django-tables2>=2.7
|
|
21
|
+
Requires-Dist: django-crispy-forms>=2.0
|
|
22
|
+
Provides-Extra: bulma
|
|
23
|
+
Requires-Dist: django-crispy-forms>=2.0; extra == "bulma"
|
|
24
|
+
Requires-Dist: crispy-bulma>=0.1; extra == "bulma"
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest-django>=4.5; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
29
|
+
Requires-Dist: black>=23.0; extra == "dev"
|
|
30
|
+
Requires-Dist: ruff>=0.1; extra == "dev"
|
|
31
|
+
|
|
32
|
+
# Faster Django development
|
|
33
|
+
|
|
34
|
+
Get more out of less with a few design patterns:
|
|
35
|
+
|
|
36
|
+
- Skip manual URL routing with MVC pattern and sane defaults
|
|
37
|
+
- Dynamic navigation menus with tagged views
|
|
38
|
+
- Skip `get_context_data` overrides by consuming the `view` object directly in
|
|
39
|
+
templates
|
|
40
|
+
- `{% eval %}` template tag to skip making template tags every time you want to
|
|
41
|
+
call a function or method (like in Jinja2)
|
|
42
|
+
- Secure by default: views allow only superusers by default, it's up to you to
|
|
43
|
+
open permissions as-needed.
|
|
44
|
+
|
|
45
|
+
Install with:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
pip install djmvc[bulma]
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Routing
|
|
52
|
+
|
|
53
|
+
Skip manual URL routing definition by nesting views and controllers:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from djmvc.controller import Controller
|
|
57
|
+
from djmvc.view import View
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# definition by sub-classing
|
|
61
|
+
class SubController(Controller):
|
|
62
|
+
name = 'sub-controller'
|
|
63
|
+
routes = [
|
|
64
|
+
View.clone(
|
|
65
|
+
name='sub-view',
|
|
66
|
+
)
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# or just define by cloning
|
|
71
|
+
Site = Controller.clone(
|
|
72
|
+
name='controller',
|
|
73
|
+
routes=[
|
|
74
|
+
View.clone(
|
|
75
|
+
name='view',
|
|
76
|
+
),
|
|
77
|
+
SubController,
|
|
78
|
+
]
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# define some importable url patterns to include
|
|
82
|
+
urlpatterns = Site().urlpatterns
|
|
83
|
+
|
|
84
|
+
# it will define reverseable urls
|
|
85
|
+
assert reverse('controller:view') == '/controller/view/'
|
|
86
|
+
assert reverse('controller:sub-controller:sub-view') == '/controller/sub-controller/sub-view/'
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Templates
|
|
90
|
+
|
|
91
|
+
Ain't no way I'm defining get_context_data for everything I add to a view, I
|
|
92
|
+
consider that adding a method or property to the view should make it directly
|
|
93
|
+
accessible from the template, as such, the default TemplateMixin provides a
|
|
94
|
+
`get_context_data` which already returns the `view` as context object.
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from djmvc.views import generic
|
|
98
|
+
|
|
99
|
+
class MyView(generic.TemplateView):
|
|
100
|
+
@property
|
|
101
|
+
def something(self):
|
|
102
|
+
return Some.thing()
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Allows directly that in the template: `{{ view.something }}`.
|
|
106
|
+
|
|
107
|
+
Also, ain't no way I'm defining a templatetag for everything, loved the liberty
|
|
108
|
+
Jinja2 gave me, taking it back with Django engine with the eval tag provided by
|
|
109
|
+
djmvc.
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
class MyView(generic.TemplateView):
|
|
113
|
+
def something(self, some_var, user):
|
|
114
|
+
return Some.thing(some_var, user)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Becomes available as:
|
|
118
|
+
|
|
119
|
+
```jinja2
|
|
120
|
+
{% load eval %}
|
|
121
|
+
{% eval view.some_method "some test var" view.request.user as result %}
|
|
122
|
+
{{ result }}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Dynamic menus
|
|
126
|
+
|
|
127
|
+
Add any tags you like to your views:
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
class YourView(generic.TemplateView):
|
|
131
|
+
tags = ['topbar']
|
|
132
|
+
|
|
133
|
+
def has_permission(self):
|
|
134
|
+
return self.request.user.is_authenticated # the default
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
And use the `get_tagged_view()` method of the controller to get all views
|
|
138
|
+
tagged `"topbar"` as such:
|
|
139
|
+
|
|
140
|
+
```jinja2
|
|
141
|
+
{% eval view.root_controller.get_tagged_views 'topbar' request=request as topbar_menu %}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Here, we're passing the `request` kwarg to `get_tagged_views()` which will
|
|
145
|
+
instanciate the view object with passed kwargs and call `has_permission()`
|
|
146
|
+
prior to returning the tagged view.
|
|
147
|
+
|
|
148
|
+
It also works with per-object permissions as such:
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
class YourModelDetailView(generic.DetailView):
|
|
152
|
+
tags = ['object']
|
|
153
|
+
|
|
154
|
+
def has_permission(self):
|
|
155
|
+
return (
|
|
156
|
+
self.request.user.is_superuser
|
|
157
|
+
or self.object.owner == self.request.user
|
|
158
|
+
)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Which will make that view show conditionnaly based on the request user, so that
|
|
162
|
+
you can get all the views authorized for a user on a given object in the
|
|
163
|
+
"object" tag as such:
|
|
164
|
+
|
|
165
|
+
```jinja2
|
|
166
|
+
{% eval view.root_controller.get_tagged_views 'object' request=request object=object as object_menu %}
|
|
167
|
+
```
|
djmvc-0.0.1/README.md
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Faster Django development
|
|
2
|
+
|
|
3
|
+
Get more out of less with a few design patterns:
|
|
4
|
+
|
|
5
|
+
- Skip manual URL routing with MVC pattern and sane defaults
|
|
6
|
+
- Dynamic navigation menus with tagged views
|
|
7
|
+
- Skip `get_context_data` overrides by consuming the `view` object directly in
|
|
8
|
+
templates
|
|
9
|
+
- `{% eval %}` template tag to skip making template tags every time you want to
|
|
10
|
+
call a function or method (like in Jinja2)
|
|
11
|
+
- Secure by default: views allow only superusers by default, it's up to you to
|
|
12
|
+
open permissions as-needed.
|
|
13
|
+
|
|
14
|
+
Install with:
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
pip install djmvc[bulma]
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Routing
|
|
21
|
+
|
|
22
|
+
Skip manual URL routing definition by nesting views and controllers:
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
from djmvc.controller import Controller
|
|
26
|
+
from djmvc.view import View
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# definition by sub-classing
|
|
30
|
+
class SubController(Controller):
|
|
31
|
+
name = 'sub-controller'
|
|
32
|
+
routes = [
|
|
33
|
+
View.clone(
|
|
34
|
+
name='sub-view',
|
|
35
|
+
)
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# or just define by cloning
|
|
40
|
+
Site = Controller.clone(
|
|
41
|
+
name='controller',
|
|
42
|
+
routes=[
|
|
43
|
+
View.clone(
|
|
44
|
+
name='view',
|
|
45
|
+
),
|
|
46
|
+
SubController,
|
|
47
|
+
]
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# define some importable url patterns to include
|
|
51
|
+
urlpatterns = Site().urlpatterns
|
|
52
|
+
|
|
53
|
+
# it will define reverseable urls
|
|
54
|
+
assert reverse('controller:view') == '/controller/view/'
|
|
55
|
+
assert reverse('controller:sub-controller:sub-view') == '/controller/sub-controller/sub-view/'
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Templates
|
|
59
|
+
|
|
60
|
+
Ain't no way I'm defining get_context_data for everything I add to a view, I
|
|
61
|
+
consider that adding a method or property to the view should make it directly
|
|
62
|
+
accessible from the template, as such, the default TemplateMixin provides a
|
|
63
|
+
`get_context_data` which already returns the `view` as context object.
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from djmvc.views import generic
|
|
67
|
+
|
|
68
|
+
class MyView(generic.TemplateView):
|
|
69
|
+
@property
|
|
70
|
+
def something(self):
|
|
71
|
+
return Some.thing()
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Allows directly that in the template: `{{ view.something }}`.
|
|
75
|
+
|
|
76
|
+
Also, ain't no way I'm defining a templatetag for everything, loved the liberty
|
|
77
|
+
Jinja2 gave me, taking it back with Django engine with the eval tag provided by
|
|
78
|
+
djmvc.
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
class MyView(generic.TemplateView):
|
|
82
|
+
def something(self, some_var, user):
|
|
83
|
+
return Some.thing(some_var, user)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Becomes available as:
|
|
87
|
+
|
|
88
|
+
```jinja2
|
|
89
|
+
{% load eval %}
|
|
90
|
+
{% eval view.some_method "some test var" view.request.user as result %}
|
|
91
|
+
{{ result }}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Dynamic menus
|
|
95
|
+
|
|
96
|
+
Add any tags you like to your views:
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
class YourView(generic.TemplateView):
|
|
100
|
+
tags = ['topbar']
|
|
101
|
+
|
|
102
|
+
def has_permission(self):
|
|
103
|
+
return self.request.user.is_authenticated # the default
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
And use the `get_tagged_view()` method of the controller to get all views
|
|
107
|
+
tagged `"topbar"` as such:
|
|
108
|
+
|
|
109
|
+
```jinja2
|
|
110
|
+
{% eval view.root_controller.get_tagged_views 'topbar' request=request as topbar_menu %}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Here, we're passing the `request` kwarg to `get_tagged_views()` which will
|
|
114
|
+
instanciate the view object with passed kwargs and call `has_permission()`
|
|
115
|
+
prior to returning the tagged view.
|
|
116
|
+
|
|
117
|
+
It also works with per-object permissions as such:
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
class YourModelDetailView(generic.DetailView):
|
|
121
|
+
tags = ['object']
|
|
122
|
+
|
|
123
|
+
def has_permission(self):
|
|
124
|
+
return (
|
|
125
|
+
self.request.user.is_superuser
|
|
126
|
+
or self.object.owner == self.request.user
|
|
127
|
+
)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Which will make that view show conditionnaly based on the request user, so that
|
|
131
|
+
you can get all the views authorized for a user on a given object in the
|
|
132
|
+
"object" tag as such:
|
|
133
|
+
|
|
134
|
+
```jinja2
|
|
135
|
+
{% eval view.root_controller.get_tagged_views 'object' request=request object=object as object_menu %}
|
|
136
|
+
```
|
djmvc-0.0.1/manage.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Django's command-line utility for administrative tasks."""
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main():
|
|
9
|
+
"""Run administrative tasks."""
|
|
10
|
+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djmvc_example.settings")
|
|
11
|
+
try:
|
|
12
|
+
from django.core.management import execute_from_command_line
|
|
13
|
+
except ImportError as exc:
|
|
14
|
+
raise ImportError(
|
|
15
|
+
"Couldn't import Django. Are you sure it's installed and "
|
|
16
|
+
"available on your PYTHONPATH environment variable? Did you "
|
|
17
|
+
"forget to activate a virtual environment?"
|
|
18
|
+
) from exc
|
|
19
|
+
execute_from_command_line(sys.argv)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
if __name__ == "__main__":
|
|
23
|
+
main()
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=64", "setuptools-scm>=8"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "djmvc"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Django CRUD utilities"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Your Name", email = "your.email@example.com"}
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.8",
|
|
21
|
+
"Programming Language :: Python :: 3.9",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.14",
|
|
26
|
+
]
|
|
27
|
+
dependencies = [
|
|
28
|
+
"django>=5.1",
|
|
29
|
+
"django-tables2>=2.7",
|
|
30
|
+
"django-crispy-forms>=2.0",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.optional-dependencies]
|
|
34
|
+
bulma = [
|
|
35
|
+
"django-crispy-forms>=2.0",
|
|
36
|
+
"crispy-bulma>=0.1",
|
|
37
|
+
]
|
|
38
|
+
dev = [
|
|
39
|
+
"pytest>=7.0",
|
|
40
|
+
"pytest-django>=4.5",
|
|
41
|
+
"pytest-cov>=4.0",
|
|
42
|
+
"black>=23.0",
|
|
43
|
+
"ruff>=0.1",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[tool.setuptools]
|
|
47
|
+
package-dir = {"" = "src"}
|
|
48
|
+
|
|
49
|
+
[tool.setuptools.packages.find]
|
|
50
|
+
where = ["src"]
|
|
51
|
+
include = ["*"]
|
|
52
|
+
namespaces = false
|
|
53
|
+
|
|
54
|
+
[tool.setuptools_scm]
|
|
55
|
+
# Git-based versioning configuration
|
|
56
|
+
# This will automatically determine the version from git tags
|
|
57
|
+
# Tag format should be: v1.0.0, v0.1.0, etc.
|
|
58
|
+
fallback_version = "0.0.0"
|
|
59
|
+
|
|
60
|
+
[tool.pytest.ini_options]
|
|
61
|
+
testpaths = ["tests"]
|
|
62
|
+
python_files = ["test_*.py"]
|
|
63
|
+
python_classes = ["Test*"]
|
|
64
|
+
python_functions = ["test_*"]
|
|
65
|
+
DJANGO_SETTINGS_MODULE = "djmvc_example.settings"
|
|
66
|
+
pythonpath = ["src/djmvc_example"]
|
|
67
|
+
addopts = [
|
|
68
|
+
"--reuse-db",
|
|
69
|
+
"--strict-markers",
|
|
70
|
+
"-ra",
|
|
71
|
+
]
|
|
72
|
+
markers = [
|
|
73
|
+
"bulma: marks tests specific to the Bulma frontend",
|
|
74
|
+
"bootstrap: marks tests specific to the Bootstrap frontend",
|
|
75
|
+
"django_db: mark test that requires Django database",
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
[tool.black]
|
|
79
|
+
line-length = 88
|
|
80
|
+
target-version = ['py38']
|
|
81
|
+
include = '\.pyi?$'
|
|
82
|
+
|
|
83
|
+
[tool.ruff]
|
|
84
|
+
line-length = 88
|
|
85
|
+
target-version = "py38"
|
djmvc-0.0.1/setup.cfg
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
class Clonable:
|
|
4
|
+
@classmethod
|
|
5
|
+
def clone(cls, *mixins, **attributes):
|
|
6
|
+
"""Return a subclass with the given attributes.
|
|
7
|
+
|
|
8
|
+
If a model is found, it will prefix the class name with the model.
|
|
9
|
+
"""
|
|
10
|
+
name = cls.__name__
|
|
11
|
+
return type(name, (cls,) + mixins, attributes)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
from django.urls import include, path
|
|
2
|
+
|
|
3
|
+
from .clonable import Clonable
|
|
4
|
+
from .route import Route
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Routes(list):
|
|
8
|
+
def __init__(self, controller, routes):
|
|
9
|
+
self.controller = controller
|
|
10
|
+
|
|
11
|
+
for route in routes:
|
|
12
|
+
route.controller = self.controller
|
|
13
|
+
|
|
14
|
+
routes = [
|
|
15
|
+
route() if isinstance(route, type) else route
|
|
16
|
+
for route in routes
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
super().__init__(routes)
|
|
20
|
+
|
|
21
|
+
def __getitem__(self, codename_or_index):
|
|
22
|
+
try:
|
|
23
|
+
return super().__getitem__(codename_or_index)
|
|
24
|
+
except TypeError:
|
|
25
|
+
for route in self:
|
|
26
|
+
if route.codename == codename_or_index:
|
|
27
|
+
return route
|
|
28
|
+
raise Exception()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class Controller(Clonable, Route):
|
|
32
|
+
def __init__(self, *args, **kwargs):
|
|
33
|
+
super().__init__(*args, **kwargs)
|
|
34
|
+
if not isinstance(self.routes, Routes):
|
|
35
|
+
self.routes = Routes(self, self.routes)
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def codename(self):
|
|
39
|
+
return super().codename.replace('controller', '')
|
|
40
|
+
|
|
41
|
+
def get_tagged_views(self, tag, **kwargs):
|
|
42
|
+
def process(controller):
|
|
43
|
+
views = []
|
|
44
|
+
for route in controller.routes:
|
|
45
|
+
if isinstance(route, Controller):
|
|
46
|
+
views += process(route)
|
|
47
|
+
continue
|
|
48
|
+
|
|
49
|
+
if tag not in getattr(route, 'tags', []):
|
|
50
|
+
continue
|
|
51
|
+
|
|
52
|
+
view = type(route)(**kwargs)
|
|
53
|
+
if view.has_permission():
|
|
54
|
+
views.append(view)
|
|
55
|
+
return views
|
|
56
|
+
return process(self)
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def root(self):
|
|
60
|
+
controller = getattr(self, 'controller', None)
|
|
61
|
+
if not controller:
|
|
62
|
+
return self
|
|
63
|
+
|
|
64
|
+
while hasattr(controller, 'controller'):
|
|
65
|
+
controller = controller.controller
|
|
66
|
+
return controller
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def urlpatterns(self):
|
|
70
|
+
patterns = []
|
|
71
|
+
|
|
72
|
+
for route in self.routes:
|
|
73
|
+
patterns += route.urlpatterns
|
|
74
|
+
|
|
75
|
+
return [
|
|
76
|
+
path(
|
|
77
|
+
self.urlpath,
|
|
78
|
+
include(
|
|
79
|
+
(patterns, self.urlname),
|
|
80
|
+
namespace=self.urlname,
|
|
81
|
+
),
|
|
82
|
+
)
|
|
83
|
+
]
|