accrete 0.0.104__py3-none-any.whl → 0.0.105__py3-none-any.whl
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.
- accrete/contrib/ui/__init__.py +6 -29
- accrete/contrib/ui/context.py +26 -297
- accrete/contrib/ui/filter.py +316 -324
- accrete/contrib/ui/middleware.py +44 -0
- accrete/contrib/ui/models.py +3 -0
- accrete/contrib/ui/static/bulma/README.md +4 -2
- accrete/contrib/ui/static/bulma/bulma.css +21551 -0
- accrete/contrib/ui/static/bulma/bulma.css.map +1 -0
- accrete/contrib/ui/static/bulma/bulma.scss +1 -1
- accrete/contrib/ui/static/bulma/css/bulma.css +1988 -2874
- accrete/contrib/ui/static/bulma/css/bulma.css.map +1 -1
- accrete/contrib/ui/static/bulma/css/bulma.min.css +2 -2
- accrete/contrib/ui/static/bulma/css/versions/bulma-no-dark-mode.css +19648 -0
- accrete/contrib/ui/static/bulma/css/versions/bulma-no-dark-mode.css.map +1 -0
- accrete/contrib/ui/static/bulma/css/versions/bulma-no-dark-mode.min.css +2 -2
- accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers-prefixed.css +11136 -0
- accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers-prefixed.css.map +1 -0
- accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers-prefixed.min.css +2 -2
- accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers.css +11136 -0
- accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers.css.map +1 -0
- accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers.min.css +2 -2
- accrete/contrib/ui/static/bulma/css/versions/bulma-prefixed.min.css +21550 -2
- accrete/contrib/ui/static/bulma/css/versions/bulma-prefixed.min.css.map +1 -1
- accrete/contrib/ui/static/bulma/css/versions/bulma-prefixed.min.min.css +3 -0
- accrete/contrib/ui/static/bulma/package.json +12 -11
- accrete/contrib/ui/static/bulma/sass/base/animations.css +15 -0
- accrete/contrib/ui/static/bulma/sass/base/animations.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/base/generic.css +196 -0
- accrete/contrib/ui/static/bulma/sass/base/generic.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/base/minireset.css +82 -0
- accrete/contrib/ui/static/bulma/sass/base/minireset.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/base/skeleton.css +113 -0
- accrete/contrib/ui/static/bulma/sass/base/skeleton.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/base/skeleton.scss +0 -12
- accrete/contrib/ui/static/bulma/sass/components/breadcrumb.css +108 -0
- accrete/contrib/ui/static/bulma/sass/components/breadcrumb.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/components/card.css +130 -0
- accrete/contrib/ui/static/bulma/sass/components/card.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/components/dropdown.css +119 -0
- accrete/contrib/ui/static/bulma/sass/components/dropdown.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/components/menu.css +119 -0
- accrete/contrib/ui/static/bulma/sass/components/menu.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/components/message.css +191 -0
- accrete/contrib/ui/static/bulma/sass/components/message.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/components/modal.css +194 -0
- accrete/contrib/ui/static/bulma/sass/components/modal.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/components/navbar.css +768 -0
- accrete/contrib/ui/static/bulma/sass/components/navbar.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/components/navbar.scss +41 -30
- accrete/contrib/ui/static/bulma/sass/components/pagination.css +302 -0
- accrete/contrib/ui/static/bulma/sass/components/pagination.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/components/panel.css +224 -0
- accrete/contrib/ui/static/bulma/sass/components/panel.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/components/panel.scss +2 -2
- accrete/contrib/ui/static/bulma/sass/components/tabs.css +192 -0
- accrete/contrib/ui/static/bulma/sass/components/tabs.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/elements/block.css +17 -0
- accrete/contrib/ui/static/bulma/sass/elements/block.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/elements/box.css +43 -0
- accrete/contrib/ui/static/bulma/sass/elements/box.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/elements/button.css +685 -0
- accrete/contrib/ui/static/bulma/sass/elements/button.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/elements/button.scss +9 -2
- accrete/contrib/ui/static/bulma/sass/elements/content.css +208 -0
- accrete/contrib/ui/static/bulma/sass/elements/content.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/elements/content.scss +8 -2
- accrete/contrib/ui/static/bulma/sass/elements/delete.css +60 -0
- accrete/contrib/ui/static/bulma/sass/elements/delete.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/elements/icon.css +51 -0
- accrete/contrib/ui/static/bulma/sass/elements/icon.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/elements/image.css +253 -0
- accrete/contrib/ui/static/bulma/sass/elements/image.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/elements/loader.css +14 -0
- accrete/contrib/ui/static/bulma/sass/elements/loader.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/elements/notification.css +213 -0
- accrete/contrib/ui/static/bulma/sass/elements/notification.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/elements/progress.css +119 -0
- accrete/contrib/ui/static/bulma/sass/elements/progress.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/elements/table.css +294 -0
- accrete/contrib/ui/static/bulma/sass/elements/table.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/elements/tag.css +252 -0
- accrete/contrib/ui/static/bulma/sass/elements/tag.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/elements/title.css +131 -0
- accrete/contrib/ui/static/bulma/sass/elements/title.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/form/checkbox-radio.css +72 -0
- accrete/contrib/ui/static/bulma/sass/form/checkbox-radio.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/form/checkbox-radio.scss +7 -3
- accrete/contrib/ui/static/bulma/sass/form/file.css +374 -0
- accrete/contrib/ui/static/bulma/sass/form/file.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/form/input-textarea.css +284 -0
- accrete/contrib/ui/static/bulma/sass/form/input-textarea.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/form/input-textarea.scss +0 -10
- accrete/contrib/ui/static/bulma/sass/form/select.css +347 -0
- accrete/contrib/ui/static/bulma/sass/form/select.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/form/select.scss +1 -0
- accrete/contrib/ui/static/bulma/sass/form/shared.css +48 -0
- accrete/contrib/ui/static/bulma/sass/form/shared.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/form/shared.scss +5 -1
- accrete/contrib/ui/static/bulma/sass/form/tools.css +356 -0
- accrete/contrib/ui/static/bulma/sass/form/tools.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/form/tools.scss +23 -12
- accrete/contrib/ui/static/bulma/sass/grid/columns-v2.css +1635 -0
- accrete/contrib/ui/static/bulma/sass/grid/columns-v2.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/grid/columns.css +1652 -0
- accrete/contrib/ui/static/bulma/sass/grid/columns.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/grid/columns.scss +109 -25
- accrete/contrib/ui/static/bulma/sass/grid/grid.css +3011 -0
- accrete/contrib/ui/static/bulma/sass/grid/grid.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/grid/grid.scss +3 -3
- accrete/contrib/ui/static/bulma/sass/helpers/aspect-ratio.css +61 -0
- accrete/contrib/ui/static/bulma/sass/helpers/aspect-ratio.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/helpers/border.css +17 -0
- accrete/contrib/ui/static/bulma/sass/helpers/border.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/helpers/color.css +5582 -0
- accrete/contrib/ui/static/bulma/sass/helpers/color.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/helpers/color.scss +166 -186
- accrete/contrib/ui/static/bulma/sass/helpers/flexbox.css +217 -0
- accrete/contrib/ui/static/bulma/sass/helpers/flexbox.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/helpers/float.css +37 -0
- accrete/contrib/ui/static/bulma/sass/helpers/float.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/helpers/gap.css +209 -0
- accrete/contrib/ui/static/bulma/sass/helpers/gap.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/helpers/other.css +34 -0
- accrete/contrib/ui/static/bulma/sass/helpers/other.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/helpers/overflow.css +65 -0
- accrete/contrib/ui/static/bulma/sass/helpers/overflow.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/helpers/position.css +45 -0
- accrete/contrib/ui/static/bulma/sass/helpers/position.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/helpers/spacing.css +489 -0
- accrete/contrib/ui/static/bulma/sass/helpers/spacing.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/helpers/typography.css +423 -0
- accrete/contrib/ui/static/bulma/sass/helpers/typography.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/helpers/visibility.css +485 -0
- accrete/contrib/ui/static/bulma/sass/helpers/visibility.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/layout/container.scss +16 -8
- accrete/contrib/ui/static/bulma/sass/layout/footer.css +9 -0
- accrete/contrib/ui/static/bulma/sass/layout/footer.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/layout/hero.css +534 -0
- accrete/contrib/ui/static/bulma/sass/layout/hero.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/layout/level.css +102 -0
- accrete/contrib/ui/static/bulma/sass/layout/level.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/layout/media.css +90 -0
- accrete/contrib/ui/static/bulma/sass/layout/media.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/layout/section.css +23 -0
- accrete/contrib/ui/static/bulma/sass/layout/section.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/layout/section.scss +4 -0
- accrete/contrib/ui/static/bulma/sass/themes/dark.css +3 -0
- accrete/contrib/ui/static/bulma/sass/themes/dark.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/themes/light.css +3 -0
- accrete/contrib/ui/static/bulma/sass/themes/light.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/themes/light.scss +1 -0
- accrete/contrib/ui/static/bulma/sass/themes/setup.css +3 -0
- accrete/contrib/ui/static/bulma/sass/themes/setup.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/utilities/controls.css +13 -0
- accrete/contrib/ui/static/bulma/sass/utilities/controls.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/utilities/css-variables.css +3 -0
- accrete/contrib/ui/static/bulma/sass/utilities/css-variables.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/utilities/css-variables.scss +3 -2
- accrete/contrib/ui/static/bulma/sass/utilities/derived-variables.css +3 -0
- accrete/contrib/ui/static/bulma/sass/utilities/derived-variables.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/utilities/extends.css +13 -0
- accrete/contrib/ui/static/bulma/sass/utilities/extends.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/utilities/functions.scss +2 -2
- accrete/contrib/ui/static/bulma/sass/utilities/initial-variables.css +3 -0
- accrete/contrib/ui/static/bulma/sass/utilities/initial-variables.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/utilities/initial-variables.scss +4 -4
- accrete/contrib/ui/static/bulma/sass/utilities/mixins.css +3 -0
- accrete/contrib/ui/static/bulma/sass/utilities/mixins.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/utilities/mixins.scss +1 -1
- accrete/contrib/ui/static/bulma/versions/bulma-no-dark-mode.css +19648 -0
- accrete/contrib/ui/static/bulma/versions/bulma-no-dark-mode.css.map +1 -0
- accrete/contrib/ui/static/bulma/versions/bulma-no-dark-mode.scss +2 -1
- accrete/contrib/ui/static/bulma/versions/bulma-no-helpers-prefixed.css +11136 -0
- accrete/contrib/ui/static/bulma/versions/bulma-no-helpers-prefixed.css.map +1 -0
- accrete/contrib/ui/static/bulma/versions/bulma-no-helpers-prefixed.scss +1 -1
- accrete/contrib/ui/static/bulma/versions/bulma-no-helpers.css +11136 -0
- accrete/contrib/ui/static/bulma/versions/bulma-no-helpers.css.map +1 -0
- accrete/contrib/ui/static/bulma/versions/bulma-no-helpers.scss +1 -1
- accrete/contrib/ui/static/bulma/versions/bulma-prefixed.css +21551 -0
- accrete/contrib/ui/static/bulma/versions/bulma-prefixed.css.map +1 -0
- accrete/contrib/ui/static/bulma/versions/bulma-prefixed.scss +1 -1
- accrete/contrib/ui/static/css/accrete.css +20757 -19997
- accrete/contrib/ui/static/css/accrete.css.map +1 -1
- accrete/contrib/ui/static/css/accrete.scss +185 -462
- accrete/contrib/ui/static/js/filter.js +97 -679
- accrete/contrib/ui/static/js/htmx.min.js +1 -1
- accrete/contrib/ui/templates/django/forms/widgets/date.html +5 -9
- accrete/contrib/ui/templates/django/forms/widgets/select.html +7 -5
- accrete/contrib/ui/templates/ui/content_right.html +7 -0
- accrete/contrib/ui/templates/ui/filter/filter.html +27 -0
- accrete/contrib/ui/templates/ui/filter/query_input.html +31 -0
- accrete/contrib/ui/templates/ui/filter/query_operator.html +11 -0
- accrete/contrib/ui/templates/ui/filter/query_params.html +106 -0
- accrete/contrib/ui/templates/ui/filter/query_tags.html +26 -0
- accrete/contrib/ui/templates/ui/layout.html +162 -233
- accrete/contrib/ui/templates/ui/list.html +39 -28
- accrete/contrib/ui/templates/ui/list_update.html +3 -0
- accrete/contrib/ui/templates/ui/message.html +13 -0
- accrete/contrib/ui/templates/ui/{partials/modal.html → modal.html} +9 -5
- accrete/contrib/ui/templates/ui/oob.html +3 -0
- accrete/contrib/ui/templates/ui/table.html +67 -71
- accrete/contrib/ui/templates/ui/table_row_update.html +14 -0
- accrete/contrib/ui/templates/ui/widgets/model_search_select.html +2 -2
- accrete/contrib/ui/templates/ui/widgets/model_search_select_multi.html +24 -13
- accrete/contrib/ui/templates/ui/widgets/model_search_select_options.html +1 -1
- accrete/contrib/ui/templatetags/{accrete_ui.py → ui.py} +67 -56
- accrete/contrib/ui/urls.py +3 -1
- accrete/contrib/ui/views.py +33 -3
- accrete/contrib/ui/widgets/__init__.py +1 -0
- accrete/contrib/ui/{forms/widgets.py → widgets/search_select.py} +7 -2
- accrete/contrib/user/templates/user/login.html +71 -23
- accrete/forms.py +0 -2
- accrete/managers.py +4 -4
- accrete/middleware.py +43 -66
- accrete/models.py +7 -1
- accrete/storage.py +4 -1
- accrete/tenant.py +9 -4
- accrete/utils/__init__.py +2 -0
- accrete/utils/models.py +9 -1
- accrete/utils/views.py +36 -20
- accrete/views.py +9 -5
- {accrete-0.0.104.dist-info → accrete-0.0.105.dist-info}/METADATA +3 -2
- accrete-0.0.105.dist-info/RECORD +370 -0
- {accrete-0.0.104.dist-info → accrete-0.0.105.dist-info}/WHEEL +1 -1
- accrete/contrib/ui/components/__init__.py +0 -1
- accrete/contrib/ui/components/search_select.py +0 -18
- accrete/contrib/ui/elements.py +0 -95
- accrete/contrib/ui/forms/__init__.py +0 -0
- accrete/contrib/ui/static/bulma/css/versions/bulma-no-dark-mode.min.css.map +0 -1
- accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers-prefixed.min.css.map +0 -1
- accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers.min.css.map +0 -1
- accrete/contrib/ui/static/bulma/sass/grid/columns-v2.scss +0 -957
- accrete/contrib/ui/static/js/ui.js +0 -30
- accrete/contrib/ui/templates/ui/dashboard.html +0 -7
- accrete/contrib/ui/templates/ui/detail.html +0 -19
- accrete/contrib/ui/templates/ui/form.html +0 -21
- accrete/contrib/ui/templates/ui/partials/filter.html +0 -146
- accrete/contrib/ui/templates/ui/partials/form_errors.html +0 -34
- accrete/contrib/ui/templates/ui/partials/header.html +0 -123
- accrete/contrib/ui/templates/ui/partials/modal_form.html +0 -46
- accrete/contrib/ui/templates/ui/partials/onchange_form.html +0 -1
- accrete/contrib/ui/templates/ui/partials/pagination_detail.html +0 -23
- accrete/contrib/ui/templates/ui/partials/pagination_list.html +0 -28
- accrete/contrib/ui/templates/ui/partials/table_field_tags.html +0 -17
- accrete/contrib/ui/templates/ui/widgets/model_search_select_multi_selected_options.html +0 -3
- accrete/contrib/ui/templates/ui/widgets/model_search_select_multi_tags.html +0 -6
- accrete-0.0.104.dist-info/RECORD +0 -238
- {accrete-0.0.104.dist-info → accrete-0.0.105.dist-info}/licenses/LICENSE +0 -0
accrete/managers.py
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
from django.db import models
|
2
2
|
from accrete.tenant import get_tenant
|
3
|
-
from accrete.annotation import AnnotationManagerMixin
|
4
3
|
|
5
4
|
|
6
|
-
class TenantManager(models.Manager
|
5
|
+
class TenantManager(models.Manager):
|
7
6
|
|
8
7
|
def get_queryset(self):
|
9
8
|
queryset = super().get_queryset()
|
10
|
-
|
9
|
+
tenant = get_tenant()
|
10
|
+
if tenant:
|
11
11
|
queryset = queryset.filter(tenant=tenant)
|
12
|
-
return queryset
|
12
|
+
return queryset
|
13
13
|
|
14
14
|
def bulk_create(
|
15
15
|
self,
|
accrete/middleware.py
CHANGED
@@ -1,92 +1,69 @@
|
|
1
|
+
import logging
|
2
|
+
|
1
3
|
from django.utils.deprecation import MiddlewareMixin
|
2
|
-
from
|
3
|
-
|
4
|
-
from
|
4
|
+
from accrete.tenant import set_tenant, set_member, get_tenant
|
5
|
+
|
6
|
+
from .models import Tenant, Member
|
5
7
|
|
6
|
-
|
8
|
+
_logger = logging.getLogger(__name__)
|
7
9
|
|
8
10
|
|
9
11
|
class TenantMiddleware(MiddlewareMixin):
|
10
12
|
@staticmethod
|
11
13
|
def get_tenant_id_from_request(request):
|
12
|
-
|
13
|
-
tenant_id
|
14
|
-
|
15
|
-
|
16
|
-
elif tenant := request.COOKIES.get('tenant_id'):
|
17
|
-
tenant_id = tenant
|
18
|
-
else:
|
19
|
-
tenant_id = None
|
20
|
-
|
14
|
+
tenant_id = (
|
15
|
+
request.GET.get('tenant_id')
|
16
|
+
or request.META.get('HTTP_X_TENANT_ID')
|
17
|
+
)
|
21
18
|
try:
|
22
19
|
tenant_id = int(tenant_id)
|
23
|
-
|
20
|
+
assert tenant_id > 0
|
21
|
+
except (ValueError, TypeError, AssertionError):
|
24
22
|
tenant_id = None
|
25
|
-
|
26
23
|
return tenant_id
|
27
24
|
|
28
25
|
def process_request(self, request):
|
29
26
|
request.tenant = None
|
30
27
|
request.member = None
|
28
|
+
set_member(False)
|
31
29
|
if request.path.startswith('/admin'):
|
30
|
+
return
|
31
|
+
if not request.user.is_authenticated:
|
32
32
|
set_member(None)
|
33
33
|
return
|
34
|
+
tenant_id = self.get_tenant_id_from_request(request)
|
35
|
+
tenant = Tenant.objects.none()
|
36
|
+
memberships = Member.objects.filter(
|
37
|
+
user=request.user, is_active=True, tenant__is_active=True
|
38
|
+
).prefetch_related('tenant', 'user').order_by('pk')
|
39
|
+
if tenant_id:
|
40
|
+
tenant = Tenant.objects.get(pk=tenant_id)
|
41
|
+
memberships = memberships.filter(tenant=tenant)
|
42
|
+
if memberships.count() == 1:
|
43
|
+
request.member = memberships.first()
|
44
|
+
request.tenant = request.member.tenant
|
45
|
+
set_member(request.member)
|
46
|
+
self.update_post_data(request)
|
47
|
+
return
|
48
|
+
if memberships.count() > 1:
|
49
|
+
set_member(None)
|
50
|
+
return
|
51
|
+
if request.user.is_staff and tenant:
|
52
|
+
set_member(None)
|
53
|
+
set_tenant(tenant)
|
54
|
+
request.tenant = tenant
|
55
|
+
return
|
56
|
+
set_member(None)
|
57
|
+
return
|
34
58
|
|
35
|
-
|
36
|
-
|
37
|
-
is_active=True,
|
38
|
-
tenant__is_active=True
|
39
|
-
)
|
40
|
-
|
41
|
-
if request.user.is_staff:
|
42
|
-
tenant_id = self.get_tenant_id_from_request(request)
|
43
|
-
if not tenant_id:
|
44
|
-
tenant_id = memberships and memberships.first().tenant.pk
|
45
|
-
request.tenant = tenant_id and Tenant.objects.filter(pk=tenant_id).first()
|
46
|
-
request.member = request.tenant.members.filter(user=request.user).first()
|
47
|
-
|
48
|
-
elif memberships and len(memberships) == 1:
|
49
|
-
request.member = memberships.first()
|
50
|
-
request.tenant = request.member.tenant
|
51
|
-
elif memberships and len(memberships) > 1:
|
52
|
-
tenant_id = self.get_tenant_id_from_request(request)
|
53
|
-
if not tenant_id:
|
54
|
-
tenant_id = memberships.first().tenant.pk
|
55
|
-
request.member = memberships.filter(tenant_id=tenant_id).first()
|
56
|
-
request.tenant = request.member.tenant
|
57
|
-
|
58
|
-
set_member(request.member)
|
59
|
-
if request.user.is_staff:
|
60
|
-
set_tenant(request.tenant)
|
61
|
-
|
59
|
+
@staticmethod
|
60
|
+
def update_post_data(request):
|
62
61
|
if request.POST and not request.POST.get('tenant') and request.tenant:
|
63
62
|
request.POST = request.POST.copy()
|
64
63
|
request.POST['tenant'] = request.tenant.pk
|
65
64
|
|
66
65
|
@staticmethod
|
67
66
|
def process_response(request, response):
|
68
|
-
|
69
|
-
|
70
|
-
response.set_cookie(
|
71
|
-
'tenant_id',
|
72
|
-
request.tenant.pk,
|
73
|
-
samesite=settings.SESSION_COOKIE_SAMESITE,
|
74
|
-
max_age=31536000
|
75
|
-
)
|
76
|
-
return response
|
77
|
-
|
78
|
-
|
79
|
-
class HtmxRedirectMiddleware(MiddlewareMixin):
|
80
|
-
|
81
|
-
@staticmethod
|
82
|
-
def process_response(request, response):
|
83
|
-
is_htmx = request.headers.get('HX-Request', 'false') == 'true'
|
84
|
-
if not is_htmx:
|
85
|
-
return response
|
86
|
-
is_redirect = isinstance(response, HttpResponseRedirect)
|
87
|
-
is_permanent_redirect = isinstance(response, HttpResponsePermanentRedirect)
|
88
|
-
if is_redirect or is_permanent_redirect:
|
89
|
-
response.status_code = 200
|
90
|
-
header = 'HX-Location' if is_redirect else 'HX-Redirect'
|
91
|
-
response[header] = response['Location']
|
67
|
+
tenant = get_tenant()
|
68
|
+
response['X-TENANT-ID'] = tenant and tenant.id or 0
|
92
69
|
return response
|
accrete/models.py
CHANGED
@@ -14,6 +14,7 @@ class TenantModel(models.Model, AnnotationModelMixin):
|
|
14
14
|
abstract = True
|
15
15
|
|
16
16
|
tenant = models.ForeignKey(
|
17
|
+
verbose_name=_('Tenant'),
|
17
18
|
to='accrete.Tenant',
|
18
19
|
on_delete=models.CASCADE
|
19
20
|
)
|
@@ -34,7 +35,8 @@ class TenantModel(models.Model, AnnotationModelMixin):
|
|
34
35
|
if self.pk and not self.tenant and not tenant:
|
35
36
|
raise ValueError(
|
36
37
|
'Tenant not provided! '
|
37
|
-
'Use accrete.tenant.set_tenant()
|
38
|
+
'Use accrete.tenant.set_tenant() '
|
39
|
+
'or set the tenant explicitly on the instance.'
|
38
40
|
)
|
39
41
|
if tenant:
|
40
42
|
self.tenant = tenant
|
@@ -45,6 +47,10 @@ class TenantModel(models.Model, AnnotationModelMixin):
|
|
45
47
|
update_fields=update_fields
|
46
48
|
)
|
47
49
|
|
50
|
+
@staticmethod
|
51
|
+
def exclude_from_filter():
|
52
|
+
return ['tenant']
|
53
|
+
|
48
54
|
|
49
55
|
class Tenant(models.Model):
|
50
56
|
|
accrete/storage.py
CHANGED
@@ -25,7 +25,10 @@ class TenantFileSystemStorage(FileSystemStorage):
|
|
25
25
|
def base_url(self):
|
26
26
|
if self._base_url is not None and not self._base_url.endswith("/"):
|
27
27
|
self._base_url += "/"
|
28
|
-
res = self._value_or_setting(
|
28
|
+
res = self._value_or_setting(
|
29
|
+
self._base_url,
|
30
|
+
f'{settings.MEDIA_URL.strip("/")}/{get_tenant().id}'
|
31
|
+
)
|
29
32
|
return res
|
30
33
|
|
31
34
|
def url(self, name):
|
accrete/tenant.py
CHANGED
@@ -2,7 +2,7 @@ import contextvars
|
|
2
2
|
import logging
|
3
3
|
import time
|
4
4
|
from django.apps import apps
|
5
|
-
from django.db.models import Q
|
5
|
+
from django.db.models import Q, QuerySet
|
6
6
|
|
7
7
|
_logger = logging.getLogger(__name__)
|
8
8
|
|
@@ -19,7 +19,12 @@ def get_tenant():
|
|
19
19
|
|
20
20
|
|
21
21
|
def set_member(member):
|
22
|
-
|
22
|
+
if member is False:
|
23
|
+
tenant = False
|
24
|
+
elif member is None:
|
25
|
+
tenant = None
|
26
|
+
else:
|
27
|
+
tenant = member.tenant
|
23
28
|
member_contextvar.set(member)
|
24
29
|
tenant_contextvar.set(tenant)
|
25
30
|
|
@@ -39,7 +44,7 @@ class Unscoped:
|
|
39
44
|
'Entering unscoped context manager with tenant already set to None!',
|
40
45
|
stack_info=True
|
41
46
|
)
|
42
|
-
set_tenant(
|
47
|
+
set_tenant(False)
|
43
48
|
|
44
49
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
45
50
|
set_tenant(self.tenant)
|
@@ -52,7 +57,7 @@ def unscoped():
|
|
52
57
|
def per_tenant(include: Q = None, exclude: Q = None):
|
53
58
|
def decorator(f):
|
54
59
|
def wrapper(*args, **kwargs):
|
55
|
-
tenants = apps.get_model('accrete', 'Tenant').objects.all()
|
60
|
+
tenants: QuerySet = apps.get_model('accrete', 'Tenant').objects.all()
|
56
61
|
if include is not None:
|
57
62
|
tenants = tenants.filter(include)
|
58
63
|
if exclude is not None:
|
accrete/utils/__init__.py
CHANGED
accrete/utils/models.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
from django.db.models import Model
|
1
|
+
from django.db.models import Model, Field
|
2
2
|
|
3
3
|
|
4
4
|
def get_related_model(model: type[Model], rel_path: str) -> tuple[Model, list[str]]:
|
@@ -16,4 +16,12 @@ def get_related_model(model: type[Model], rel_path: str) -> tuple[Model, list[st
|
|
16
16
|
related_model = next_model
|
17
17
|
except AttributeError:
|
18
18
|
break
|
19
|
+
names.append(str(related_model._meta.verbose_name))
|
19
20
|
return related_model, names
|
21
|
+
|
22
|
+
|
23
|
+
def get_related_field(model: type[Model], field_path: str) -> Field:
|
24
|
+
parts = field_path.split('__')
|
25
|
+
rel_path = '__'.join(parts[:-1])
|
26
|
+
rel_model, _ = get_related_model(model, rel_path)
|
27
|
+
return rel_model._meta.get_field(parts[-1])
|
accrete/utils/views.py
CHANGED
@@ -2,11 +2,12 @@ import logging
|
|
2
2
|
import json
|
3
3
|
import operator
|
4
4
|
from typing import Callable
|
5
|
+
|
6
|
+
from django.core import paginator
|
5
7
|
from django.db.models import Model, Q, QuerySet
|
6
8
|
from django.template.loader import render_to_string, TemplateDoesNotExist
|
7
9
|
from django.http import HttpResponseNotAllowed
|
8
10
|
from accrete.utils.models import get_related_model
|
9
|
-
from accrete.annotation import Annotation
|
10
11
|
|
11
12
|
_logger = logging.getLogger(__name__)
|
12
13
|
|
@@ -18,13 +19,40 @@ QUERYSTRING_KEY_MAP = {
|
|
18
19
|
}
|
19
20
|
|
20
21
|
|
22
|
+
def page_from_querystring(
|
23
|
+
model: type[Model], query_dict: dict, key_map: dict = None,
|
24
|
+
select_related: list = None, prefetch_related: list = None
|
25
|
+
) -> paginator.Page:
|
26
|
+
|
27
|
+
key_map = key_map or QUERYSTRING_KEY_MAP
|
28
|
+
queryset = filter_from_querystring(
|
29
|
+
model, query_dict, key_map
|
30
|
+
).select_related(
|
31
|
+
*select_related or []
|
32
|
+
).prefetch_related(
|
33
|
+
*prefetch_related or []
|
34
|
+
)
|
35
|
+
return get_page(
|
36
|
+
queryset,
|
37
|
+
cast_param(query_dict, key_map.get('paginate_by'), int, 40),
|
38
|
+
cast_param(query_dict, key_map.get('page'), int, 1)
|
39
|
+
)
|
40
|
+
|
41
|
+
|
42
|
+
def get_page(queryset: QuerySet, paginate_by: int, page_number: int) -> paginator.Page:
|
43
|
+
pages = paginator.Paginator(queryset, per_page=paginate_by)
|
44
|
+
return pages.page(
|
45
|
+
page_number <= pages.num_pages and page_number or pages.num_pages
|
46
|
+
)
|
47
|
+
|
48
|
+
|
21
49
|
def filter_from_querystring(
|
22
|
-
model: type[Model],
|
50
|
+
model: type[Model], query_dict: dict, key_map: dict = None
|
23
51
|
) -> QuerySet:
|
24
52
|
|
25
53
|
key_map = key_map or QUERYSTRING_KEY_MAP
|
26
|
-
querystring =
|
27
|
-
order =
|
54
|
+
querystring = query_dict.get(key_map['querystring'], '[]')
|
55
|
+
order = query_dict.get(key_map['order']) or model._meta.ordering
|
28
56
|
|
29
57
|
return model.objects.filter(
|
30
58
|
parse_querystring(model, querystring)
|
@@ -34,7 +62,7 @@ def filter_from_querystring(
|
|
34
62
|
def parse_querystring(model: type[Model], query_string: str) -> Q:
|
35
63
|
"""
|
36
64
|
param: query_string: JSON serializable string
|
37
|
-
[{term: value},
|
65
|
+
[{"term": value}, "&", [{t"erm": value}, "|", {"~term": value}]]
|
38
66
|
Q(term=value) & (Q(term=value) | ~Q(term=value))
|
39
67
|
"""
|
40
68
|
|
@@ -51,19 +79,7 @@ def parse_querystring(model: type[Model], query_string: str) -> Q:
|
|
51
79
|
|
52
80
|
rel_path = '__'.join(parts[:-2])
|
53
81
|
related_model, x = get_related_model(model, rel_path)
|
54
|
-
|
55
|
-
if not isinstance(attr, Annotation):
|
56
|
-
expression = Q(**{term: value})
|
57
|
-
return ~expression if invert else expression
|
58
|
-
|
59
|
-
objects = related_model.objects.filter(Q(**{
|
60
|
-
'__'.join(parts[-2:]): value
|
61
|
-
}))
|
62
|
-
expression = Q(**{
|
63
|
-
f'{rel_path}{"__" if rel_path else ""}id__in':
|
64
|
-
objects.values_list('id', flat=True)
|
65
|
-
})
|
66
|
-
|
82
|
+
expression = Q(**{term: value})
|
67
83
|
return ~expression if invert else expression
|
68
84
|
|
69
85
|
def parse_query_block(sub_item) -> Q:
|
@@ -85,7 +101,6 @@ def parse_querystring(model: type[Model], query_string: str) -> Q:
|
|
85
101
|
f'Operator must be one of &, |, ^'
|
86
102
|
)
|
87
103
|
op = ops[item]
|
88
|
-
|
89
104
|
else:
|
90
105
|
raise ValueError(
|
91
106
|
f'Unsupported item in querystring: {item}.'
|
@@ -120,6 +135,7 @@ def method_not_allowed(method: str, allowed: list[str]) -> HttpResponseNotAllowe
|
|
120
135
|
def render_templates(
|
121
136
|
templates: list[str | tuple[str, dict]],
|
122
137
|
context: dict = None,
|
138
|
+
request=None,
|
123
139
|
log_not_found: bool = False
|
124
140
|
) -> str:
|
125
141
|
context = {} if context is None else context
|
@@ -131,7 +147,7 @@ def render_templates(
|
|
131
147
|
template_name = template
|
132
148
|
template_context = context
|
133
149
|
try:
|
134
|
-
content += render_to_string(template_name, template_context)
|
150
|
+
content += render_to_string(template_name, template_context, request)
|
135
151
|
except TemplateDoesNotExist as e:
|
136
152
|
if log_not_found:
|
137
153
|
_logger.warning(repr(e))
|
accrete/views.py
CHANGED
@@ -4,9 +4,9 @@ from django.http import HttpResponse, HttpResponseNotFound
|
|
4
4
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
5
5
|
from django.contrib.auth.views import login_required
|
6
6
|
from django.core.exceptions import ImproperlyConfigured
|
7
|
-
from django.shortcuts import redirect
|
7
|
+
from django.shortcuts import redirect, get_object_or_404
|
8
8
|
from django.conf import settings
|
9
|
-
from accrete.
|
9
|
+
from accrete.models import Tenant, Member
|
10
10
|
from . import config
|
11
11
|
|
12
12
|
|
@@ -80,6 +80,8 @@ def tenant_required(
|
|
80
80
|
tenant = request.tenant
|
81
81
|
if not tenant:
|
82
82
|
return redirect(config.ACCRETE_TENANT_NOT_SET_URL)
|
83
|
+
if kwargs.get('tenant'):
|
84
|
+
kwargs.pop('tenant')
|
83
85
|
return f(request, *args, **kwargs)
|
84
86
|
return _wrapped_view
|
85
87
|
return decorator
|
@@ -87,9 +89,11 @@ def tenant_required(
|
|
87
89
|
|
88
90
|
@tenant_required()
|
89
91
|
def get_tenant_file(request, tenant_id, filepath):
|
90
|
-
tenant =
|
91
|
-
if
|
92
|
-
|
92
|
+
tenant = get_object_or_404(Tenant, pk=tenant_id)
|
93
|
+
if not request.user.is_staff:
|
94
|
+
member = Member.objects.filter(user=request.user, tenant=tenant)
|
95
|
+
if not member.exists():
|
96
|
+
return HttpResponseNotFound()
|
93
97
|
filepath = f'{settings.MEDIA_ROOT}/{tenant_id}/{filepath}'
|
94
98
|
if not os.path.exists(filepath):
|
95
99
|
return HttpResponseNotFound()
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: accrete
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.105
|
4
4
|
Summary: Django Shared Schema Multi Tenant
|
5
5
|
Author-email: Benedikt Jilek <benedikt.jilek@pm.me>
|
6
6
|
License: Copyright (c) 2023 Benedikt Jilek
|
@@ -22,6 +22,7 @@ License: Copyright (c) 2023 Benedikt Jilek
|
|
22
22
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
23
23
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
24
24
|
SOFTWARE.
|
25
|
+
License-File: LICENSE
|
25
26
|
Classifier: Environment :: Web Environment
|
26
27
|
Classifier: Framework :: Django
|
27
28
|
Classifier: Intended Audience :: Developers
|